一、图像绘制
canvas绘制图像的方法是ctx.drawImage();该方法有三种使用方式
1、ctx.drawImage(img,x,y);
img是指图像对象,x和y分别是这个图像左上角在canvas画布的坐标,而图像显示的大小为图像本身的像素值,超出canvas画布的部分不显示。
2、ctx.drawImage(img,x,y,w,h);
和上面的那个函数相比,可以控制图像在canvas中显示的宽度和高度,而不再是图片自身的高宽像素
3、ctx.drawImage(img , dx , dy , dw , dh , x , y , w , h);
这个又比之前的函数多了dx、dy、dw、dh这四个参数,这四个参数可以控制图片切片显示在canvas画布中。在图片上同样以左上角为原点,向右为X轴正方向,向下为Y轴正方向,这四个参数表示,在图片中在(dx,dy)这个坐标,切dw和dh大小的切片。然后将这个切片显示在canvas画布上。
最后这个img参数,不单单可以是Image对象,还可以是video对象,也可以是另外的canvas对象。所以可以通过该方法对video视频进行截屏,也可以通过加载canvas制作图像水印、放大镜、以及淘宝双11主会场那样的特效,一个canvas加载另一个canvas,也被称为canvas的离屏技术。
二、图像的像素级处理
1、ctx.getImageData(x , y , w , h);
这个方法是获取画布(x,y)坐标,宽高分别为w和h的像素对象。而这个对象的data属性,则是其rgba的值。一个画布的像素值是从画布的原点,从左向右,从上到下,一排一排的排列下来的。常用取出canvas像素值的方法有两种,一种是采用一层循环,另一种是采用双重for循环,代码如下:
var imageData = ctx.getImageData(0 , 0 , canvasW , canvasH), pixelData = imageData.data;//方式一for(var i = 0; i < canvasH*canvasW ; i++){ var r = pixelData[4*i + 0], g = pixelData[4*i + 1], b = pixelData[4*i + 2], a = pixelData[4*i + 3];} //方式二for(var y = 0 ; i < canvasH ; y++){ for(var x = 0 ; x < canvasW ; x++){ var pixe = canvasW*y + x, r = pixelData[4*pixe + 0], g = pixelData[4*pixe + 1], b = pixelData[4*pixe + 2], a = pixelData[4*pixe + 3]; }}
该方法是设置canvas画布上的像素数据,它有七个参数,在w3c上是这么解释的
我的理解就是将imgData,放在画布上的位置是(x + dirtyX , y + dirtyY),高宽分别是dirtyHeight、dirtyWidth的矩形。
三、代码demo
1、图片缩放,以及像素变换
<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>图像学习</title> <style> #main,#copy,.contain{ margin: 15px; padding: 0; float: left; } #main{ cursor:move } .contain *{ padding: 2px; margin: 0;z } .contain a{ display: block; text-align: center; margin-top:5px; font-size:15px; line-height: 20px; } </style> </head> <body> <canvas id="main" width="400" height="300" style="display:block;border: 1px solid #AAA;"> 亲,您的浏览器不支持canvas </canvas> <div class="contain" style="width: 290px;height: 300px;"> <div> <span>缩放</span> <input type="range" id="scaleChange" min="0.5" max="4.0" step="0.03" value="1.0" style="display:inline-block;width: 240px;"/> </div> <a href="javascript:void(0)" id="greyChange">灰度转换</a> <a href="javascript:void(0)" id="blackChange">黑度转换</a> <a href="javascript:void(0)" id="reverChange">反相转换</a> <a href="javascript:void(0)" id="blurChange">模糊处理</a> <a href="javascript:void(0)" id="mosaicChange">马赛克处理</a> </div> <canvas id="copy" width="400" height="300" style="display:block;border: 1px solid #AAA;"> 亲,您的浏览器不支持canvas </canvas> <canvas id="waterMark" style="display: none;"> 亲,您的浏览器不支持canvas <!-- 水印作图的canvas,把一个canvas载入另一个canvas的做法叫做,离屏canvas --> </canvas> </body> <script> document.body.onload = function(){ var canvas = document.getElementById("main"), scaleChange = document.getElementById("scaleChange"), copyCanvas = document.getElementById("copy"), waterMarkCanvas = document.getElementById("waterMark"), ctx = canvas.getContext("2d"), copyCtx = copyCanvas.getContext("2d"), canvasW = canvas.width, canvasH = canvas.height; var img = new Image(); img.src = "img.jpg"; img.onload = function(){ //当图片加载完毕后,执行canvas画图函数 init(); } //用于初始化 function init(){ toolFunc.handleWaterMark(); toolFunc.handleScale(); toolFunc.drawImg(); toolFunc.handleEffect(); toolFunc.handleCanvasMove(); } var toolFunc = { scaleImgX : 0, scaleImgY : 0, drawImg : function(moveX , moveY){ //绘制图片 var scale = scaleChange.value; //根据比例计算缩放后图像的大小 var imgW = canvasW*scale, imgH = canvasH*scale, x = (canvasW - imgW)/2, y = (canvasH - imgH)/2, tempX = x , tempY = y; if(moveX || moveY){ x = this.scaleImgX + moveX; y = this.scaleImgY + moveY; if(x >= 0 || y >= 0 || x <= 2*tempX || y <= 2*tempY){ return; } } ctx.clearRect(0 , 0 , canvasW , canvasH); ctx.drawImage(img , x , y , imgW , imgH); ctx.drawImage(waterMarkCanvas , canvasW - waterMarkCanvas.width , canvasH - waterMarkCanvas.height , waterMarkCanvas.width , waterMarkCanvas.height); this.scaleImgX = x; this.scaleImgY = y; }, handleWaterMark : function(){ //用于处理水印 waterMarkCanvas.width = 200; waterMarkCanvas.height = 70; var waterCtx = waterMarkCanvas.getContext("2d"); waterCtx.font = "bold 20px Arial"; waterCtx.lineWidth = 1; waterCtx.fillStyle = "white"; waterCtx.textAlign = "center"; waterCtx.textBaseline = "middle"; waterCtx.fillText( "~!~ sky ~!~" , 140 , 50 , waterMarkCanvas.width); }, handleScale : function(){ //用于处理缩放 var isScale = false, _this = this; scaleChange.onmousedown = function(){ isScale = true; } scaleChange.onmousemove = function(){ if(isScale){ //画图 _this.drawImg(); } } scaleChange.onmouseup = function(){ isScale = false; } }, handleEffect : function(){ //处理效果变换 var imageData,pixelData; document.getElementById("greyChange").onclick = function(){ //灰度变换 imageData = ctx.getImageData(0 , 0 , canvasW , canvasH); pixelData = imageData.data; for(var i = 0; i < canvasH*canvasW ; i++){ var r = pixelData[4*i + 0], g = pixelData[4*i + 1], b = pixelData[4*i + 2], grey = r*0.3+g*0.59+b*0.11;//此为灰度图像的算法 pixelData[4*i + 0] = grey; pixelData[4*i + 1] = grey; pixelData[4*i + 2] = grey; } copyCtx.putImageData(imageData,0,0,0,0,canvasW,canvasH); } document.getElementById("blackChange").onclick = function(){ //黑度变换 imageData = ctx.getImageData(0 , 0 , canvasW , canvasH); pixelData = imageData.data; for(var i = 0; i < canvasH*canvasW ; i++){ var r = pixelData[4*i + 0], g = pixelData[4*i + 1], b = pixelData[4*i + 2], grey = (function(n){ //黑度图像就只有黑白两色 if(n > 127){ return 255; }else{ return 0; } })(r*0.3+g*0.59+b*0.11); pixelData[4*i + 0] = grey; pixelData[4*i + 1] = grey; pixelData[4*i + 2] = grey; } copyCtx.putImageData(imageData,0,0,0,0,canvasW,canvasH); } document.getElementById("reverChange").onclick = function(){ //反向变换 imageData = ctx.getImageData(0 , 0 , canvasW , canvasH); pixelData = imageData.data; for(var i = 0; i < canvasH*canvasW ; i++){ var r = pixelData[4*i + 0], g = pixelData[4*i + 1], b = pixelData[4*i + 2]; pixelData[4*i + 0] = 255 - r; pixelData[4*i + 1] = 255 - g; pixelData[4*i + 2] = 255 - b; } copyCtx.putImageData(imageData,0,0,0,0,canvasW,canvasH); } document.getElementById("blurChange").onclick = function(){ //模糊处理 imageData = ctx.getImageData(0 , 0 , canvasW , canvasH); pixelData = imageData.data; var pixelTmpData = pixelData; //作为一个备份数据 var r = 3 , totalNum = (2*r + 1)*(2*r + 1); //去上下左右四个方向各3个像素点的平均值 for(var i = r ; i < canvasH - r ; i++){ for(var j = r ; j < canvasW - r; j++){ var totalR,totalG,totalB; totalR = totalG = totalB = 0; for(var m = -r ; m <= r ; m++){ for(var n = -r ; n <= r ; n++){ var pixeY = i + m , piexX = j + n, pixe = canvasW*pixeY + piexX; totalR += pixelTmpData[4*pixe + 0]; totalG += pixelTmpData[4*pixe + 1]; totalB += pixelTmpData[4*pixe + 2]; } } var p = i*canvasW + j; pixelData[4*p + 0] = totalR/totalNum; pixelData[4*p + 1] = totalG/totalNum; pixelData[4*p + 2] = totalB/totalNum; } } copyCtx.putImageData(imageData,0,0,0,0,canvasW,canvasH); } document.getElementById("mosaicChange").onclick = function(){ //马赛克处理 imageData = ctx.getImageData(0 , 0 , canvasW , canvasH); pixelData = imageData.data; var pixelTmpData = pixelData; //作为一个备份数据 var size = 8,total = size*size; for(var i = 0 ; i < canvasH ; i += size){ for( var j = 0 ; j < canvasW ; j += size){ var totalR,totalG,totalB; totalR = totalG = totalB = 0; for(var y = 0 ; y < size ; y++){ for(var x = 0 ; x < size ; x++){ var py = i + y, px = j + x, pixe = px + py*canvasW; totalR += pixelTmpData[4*pixe + 0]; totalG += pixelTmpData[4*pixe + 1]; totalB += pixelTmpData[4*pixe + 2]; } } var newR = totalR/total, newG = totalG/total, newB = totalB/total; for(var dy = 0 ; dy < size ; dy++){ for(var dx = 0 ; dx < size ; dx++ ){ var py = i + dy, px = j + dx, pixe = px + py*canvasW; pixelData[4*pixe + 0] = newR; pixelData[4*pixe + 1] = newG; pixelData[4*pixe + 2] = newB; } } } } copyCtx.putImageData(imageData,0,0,0,0,canvasW,canvasH); } }, handleCanvasMove:function(){ //处理canvans移动 var isCanvasMove = false, lastX,lastY; canvas.onmousedown = function(e){ e.preventDefault(); isCanvasMove = true; lastX = e.clientX - canvas.getBoundingClientRect().left; lastY = e.clientY - canvas.getBoundingClientRect().top; } canvas.onmousemove = function(e){ e.preventDefault(); if(!isCanvasMove || scaleChange.value <= 1) return; var x = e.clientX - canvas.getBoundingClientRect().left, y = e.clientY - canvas.getBoundingClientRect().top; var moveX = (x - lastX), moveY = (y - lastY); toolFunc.drawImg(moveX,moveY); lastX = x; lastY = y; } canvas.onmouseup = function(e){ e.preventDefault(); isCanvasMove = false; } canvas.onmouseout = function(e){ e.preventDefault(); isCanvasMove = false; } } }; } </script></html>
效果图为
用的图片是
2、图片放大镜
<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>canvas放大镜</title> <style type="text/css"> *{ margin: 0; padding: 0; } </style> </head> <body> <canvas id="main" width="800" height="600" style="display: block;margin: 0 auto;border: 1px solid #AAA;"> 亲,您的浏览器不支持canvas </canvas> <canvas id="magnify" width="200" height="200" style="display: none;"></canvas> <script> var canvas = document.getElementById("main"), ctx = canvas.getContext("2d"), canvasW = canvas.width, canvasH = canvas.height, magnifyCanvas = document.getElementById("magnify"), mCtx = magnifyCanvas.getContext("2d"), magnifyR = 100; var img = new Image(); img.src = "magnify.jpg"; img.onload = function(){ ctx.drawImage(this , 0 , 0 , canvasW , canvasH); handleEvent(); } function handleEvent(){ var isShow = false; canvas.onmousedown = function(e){ e.preventDefault(); draw(e); } canvas.onmouseup = function(e){ e.preventDefault(); draw(e); } canvas.onmousemove = function(e){ e.preventDefault(); if(!isShow) return; draw(e); } canvas.onmouseout = function(e){ e.preventDefault(); draw(e); } function draw(event){ isShow = (event.type == "mousedown" || event.type == "mousemove"); ctx.clearRect(0 , 0 , canvas.width , canvas.height); ctx.drawImage(img , 0 , 0 , canvasW , canvasH); if(!isShow) return; var x = event.clientX - canvas.getBoundingClientRect().left, y = event.clientY - canvas.getBoundingClientRect().top; mCtx.save(); mCtx.lineWidth = 3; mCtx.strokeStyle = "skyblue"; mCtx.beginPath(); mCtx.arc(100,100,90,0,Math.PI*2); mCtx.stroke(); mCtx.clip(); mCtx.drawImage(img , 2*x - 90 , 2*y - 90 , 180 , 180, 0 , 0 , magnifyCanvas.width, magnifyCanvas.height); mCtx.closePath(); mCtx.restore(); ctx.drawImage(magnifyCanvas, (x-(magnifyCanvas.width/2)) , (y-(magnifyCanvas.height/2)) , magnifyCanvas.width , magnifyCanvas.height); } } </script> </body></html>
用的图片是
- 1楼林小草
- 前端交流群 163270241
- Re: 古月枫
- @林小草,已提交入群申请,哈哈