Canvas是HTML的API,我們可以用它在網頁中實時的來生成圖像。
文章導讀
1.必備技能
2.用於畫圖的函數
例子:
-會話氣泡- -心形- -鍾表- -星球里的星星-
-調色板- -鼠標繪圖- -旋轉的小方塊-
3.圖像的處理
例子:
-圖像的灰度和翻轉效果- -拾色器-
-放大鏡- -圖像的高斯模糊-
一.必備技能
<canvas id="Canvas" width="400" height="200">
不支持canvas的瀏覽器,你就會看到這句話!
</canvas>
控制它的寬高是要寫在行內樣式中的,像上面那樣。這樣我們就有了一個canvas元素,然后我們就可以去操作它了:
var canvas = document.getElementById('Canvas'); if (canvas.getContext) { var content = canvas.getContext('2d'); }
獲取元素的同時,還要獲取canvas的2D繪圖環境。要是用於3D繪圖的話,就要用WebGL了。
二.用於畫圖的函數
然后,我們要開始在畫布上繪圖了。它的畫布是這樣的網格:
2.1矩形
canvas中只支持這一種形狀的函數,別的形狀就都要自己組合來實現了。
fillRect(x, y, width, height) //畫一個填充的矩形 strokeRect(x, y, width, height) //畫一個只有邊框的矩形 clearRect(x, y, width, height) //清除指定的矩形區域
其中的(x,y)是指矩形左上角的坐標。
2.2路徑
1.首先,創建一條路徑--beginPath() 2.然后,通過一些繪圖的方法做一些繪圖操作 3.然后關閉路徑--closePath() 4.我們已經把路徑創建好了,然后就是填充或者繪制路徑到我們的畫布上--stroke()/fill()
比如說畫一個三角形:
需要注意的是其中兩個三角形,顏色不同,一不小心可能會出問題,所以我們有時可能需要save()和restore()函數的幫助來達到局部作用的效果。比如上面示例源碼中所做的那樣:

1 2 content.save();//局部作用效果 3 content.fillStyle = 'red'; 4 content.beginPath();//beginPath()不寫的時候可能會出錯,所以確保beginPath和closePath都相對應 5 content.moveTo(100,100); 6 content.lineTo(200,200); 7 content.lineTo(300,200); 8 content.closePath(); 9 content.fill(); 10 content.restore(); 11 12 13 content.beginPath(); 14 content.moveTo(100,200); 15 content.lineTo(200,300); 16 content.lineTo(300,300); 17 content.closePath(); 18 content.fill();
canvas還提供了兩個移動畫筆的函數:
moveTo(x, y) //將畫筆移動到指定的(x,y)的位置 lineTo(x, y) //在當前畫筆的位置到指定的(x,y)位置畫一條線
嘗試一下moveTo()方法我們可以畫個笑臉:
結合使用moveTo和lineTo方法,我們可以實現鼠標像畫筆一樣在畫板上繪圖的效果:
然后我們嚴格的按照上面說的規范步驟,1,2,3,4並結合畫圓的函數,可以自己繪制一個鍾表,稍加改進就是個嘀嗒嘀嗒走的時鍾了:
2.3文字
fillText('要顯示的文本',x坐標,y坐標)此方法不支持文本斷行,要顯示多行文本只能多次調用
通過fillText()方法我們可以在canvas畫布上寫文字:
2.4圓形和扇形
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise); (x,y)--圓心坐標 radius--半徑 startAngle--扇形的起始角度(弧度) endAngle--扇形的終止角度(弧度) anticlockwise--做圖時是順時針畫(true)還是逆時針(false)畫
結合對rgb顏色的相關操作,我們可以做出類似調色板的效果:
2.5不規則圖形
我們可以通過畫曲線來得到不規則的圖形,canvas為我們提供了兩個高大上的函數:
-------------------------------
#quadraticCurveTo(cp1x, cp1y, x, y)
(cp1x,cp1y)--控制點
此函數表示從當前的畫筆位置到(x,y)畫一條二次曲線
-------------------------------
#bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
(cp1x,cp1y)--控制點1 (cp2x,cp2y)--控制點2
此函數表示從當前的畫筆位置到(x,y)畫一條bezier曲線
-------------------------------
這兩個曲線具體含義我也不太清楚,但大概是這個樣子:
試一下quadraticCurveTo函數:
試一下bezierCurveTo函數:
2.6漸變和陰影
#漸變: var myGradient = ctx.createLinearGradient(x1, y1, x2, y2); (x1,y1)--起點坐標 (x2,y2)--終點坐標 myGradient.addColorStop(0, "#BABABA"); --添加起始顏色 myGradient.addColorStop(1, "#636363"); --添加終止的顏色 ----------------------------- #陰影 ctx.shadowOffsetX = 10; // 設置水平位移 ctx.shadowOffsetY = 10; // 設置垂直位移 ctx.shadowBlur = 5; // 設置模糊度 ctx.shadowColor = "rgba(0,0,0,0.5)"; // 設置陰影顏色
一個也不太搭邊兒的例子,不管怎么說,也用到了漸變呢:
這里面用到了clip()方法來實現星星,這跟某css屬性clip-path比較神似,我以前嘗試過:
2.7變換
canvas中有我們熟悉的幾個變換函數:
translate(x, y) --平移
rotate(angle) --旋轉
scale(x, y) --縮放
transform(a, b, c, d, e, f) --變換
上面的變換函數是根據變換矩陣進行的:
a c e
[ b d f ]
0 0 1
簡單的小例子:
三.圖像處理
var myImageData = ctx.createImageData(width, height); ctx.getImageData(left, top, width, height);//讀取canvas的內容,返回一個對象且該對象有一個data屬性,可以供我們操作頁面的像素。 ctx.putImageData(myImageData, dx, dy);//將操作好的對象重新繪制在畫布中 ctx.drawImage(img, 0, 0); // 我們常用的drawImage方法,設置對應的圖像對象,以及它在畫布上的位置
通過getImageData()方法獲取到圖像對象,訪問它的data屬性就可以得到一個像素數組,我們對像素進行處理,使我們可以用canvas處理圖像。
3.1 灰度效果
灰度圖(grayscale)就是取紅、綠、藍三個像素值的算術平均值,這實際上將圖像轉成了黑白形式。假定d[i]是像素數組中一個象素的紅色值,則d[i+1]為綠色值,d[i+2]為藍色值,d[i+3]就是alpha通道值。轉成灰度的算法,就是將紅、綠、藍三個值相加后除以3,再將結果寫回數組。

1 for(var i=0;i<data.length;i+=4){ 2 var avg = (data[i]+data[i+1]+data[i+2]) / 3; 3 data[i] = avg; //r 4 data[i+1] = avg; //g 5 data[i+2] = avg; //b 6 }
3.2 復古效果
復古效果(sepia)則是將紅、綠、藍三個像素,分別取這三個值的某種加權平均值,使得圖像有一種古舊的效果。

1 for (var i = 0; i < d.length; i += 4) { 2 var r = d[i]; 3 var g = d[i + 1]; 4 var b = d[i + 2]; 5 d[i] = (r * 0.393)+(g * 0.769)+(b * 0.189); // red 6 d[i + 1] = (r * 0.349)+(g * 0.686)+(b * 0.168); // green 7 d[i + 2] = (r * 0.272)+(g * 0.534)+(b * 0.131); // blue 8 }
3.3 反轉效果
反轉效果(invert)是指圖片呈現一種色彩顛倒的效果。算法為紅、綠、藍通道都取各自的相反值(255-原值)。

1 for (var i = 0; i < d.length; i += 4) { 2 d[i] = 255 - d[i]; 3 d[i+1] = 255 - d[i + 1]; 4 d[i+2] = 255 - d[i + 2]; 5 }
我嘗試了一下灰度圖的效果和反轉圖的效果:
[查看源碼]
注意,在我們自己本地寫demo准備運行查看結果的時候,getImageData()方法可能會有問題,比如說報這個錯:Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data
我們知道這是域和本地運行等相關的問題,這時我們自己本地啟用服務器,localhost訪問就可以了。
3.4 拾色器
實現思路:通過getImageData(x,y,1,1)獲得當前鼠標所在位置的一像素的圖像對象,通過其data屬性操作拿到它的像素值,也就是我們想得到的rgb值。
[查看源碼]
3.5 放大鏡
實際上drawImage方法可以有多個參數,提供更加豐富的功能:
1 ctx.drawImage(image, dx, dy); 2 ctx.drawImage(image, dx, dy, dWidth, dHeight); 3 ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
各個參數就像下面的示意圖這樣:
然后我們就可以使用drawImage方法在原圖像變換到繪制的圖像中做一個等比放大的操作,就可以實現放大鏡的效果啦。
「查看源碼」
3.6圖像的高斯模糊
其實,css中有一個兼容性不那么好的filter屬性,可以簡單的實現圖像模糊的效果:
-webkit-filter: blur(20px); /* Chrome, Opera */ -moz-filter: blur(20px); -ms-filter: blur(20px); filter: blur(20px);
「查看源碼」
但是使用canvas可以實現真正意義上的高斯模糊(就是算法那種balabala的)。
這里有一個實現的很好的高斯模糊的js:「StackBlur」
使用它我們就可以輕松的實現圖像的高斯模糊了:「查看源碼」
文章中的源碼Demo都放在了github上,打開可能會有些慢,如果第一次打不開,刷新一下也會好了。
四.資源
canvas中繪圖環境所有的屬性和方法都可以在這里找到:「CanvasRenderingContext2D interface」