【中篇】 -- 建議學習時間4小時 課程共(上中下)三篇
此筆記是我初次接觸canvas的時候的學習筆記,這次特意整理為博客供大家入門學習,幾乎涵蓋了canvas所有的基礎知識,並且有眾多練習案例,建議大家學習10~15個小時,里面的案例請挨個敲一遍,這樣才能轉化為自己的知識。
技術要求:有html/css/js基礎。
顏色
為canvas添加顏色我們使用 fillStyle 和 strokeStyle,在上一篇中我們已經簡單的使用過
fillStyle = color
設置圖形的填充顏色。
strokeStyle = color
設置圖形輪廓的顏色。
其中的 color可以是 顏色名/顏色值/rgba等等
例子:繪制多彩方塊
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); for(var i=0; i<6; i++){ for(var j=0; j<6; j++){ ctx.fillStyle = 'rgb('+Math.floor(255-42.5*i)+','+Math.floor(255-42.5*j)+',0)'; ctx.fillRect(150+j*25,50+i*25,25,25); } }
例子:繪制多彩圓圈
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); for(var i=0; i<6; i++){ for(var j=0; j<6; j++){ ctx.strokeStyle = 'rgb(0,'+Math.floor(255-42.5*i)+','+Math.floor(255-42.5*j)+')'; ctx.beginPath(); ctx.arc(150+j*25,50+i*25,10,0,Math.PI*2,true); ctx.stroke(); } }
透明度
全局透明度:
ctx.globalAlpha = transparencyValue
這個屬性影響到 canvas 里所有圖形的透明度,有效的值范圍是 0.0 (完全透明)到 1.0(完全不透明),默認是 1.0。
下面代碼設置全局透明度為 0.3,后續繪制的所有圖像,都會是0.3的透明度。
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.globalAlpha = 0.3;//設置全局透明度 ctx.fillStyle = "red"; //設置顏色 ctx.fillRect(50,50, 100, 100); //繪制方塊
例子:
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.fillStyle = "#fd0"; ctx.fillRect(0,0,75,75); ctx.fillStyle = "#6c0"; ctx.fillRect(75,0,75,75); ctx.fillStyle = "#09f"; ctx.fillRect(0,75,75,75); ctx.fillStyle = "#f30"; ctx.fillRect(75,75,75,75); ctx.fillStyle = "#fff"; //設置透明值 ctx.globalAlpha = 0.2; for(var i=0; i<7; i++){ ctx.beginPath(); ctx.arc(75, 75, 10+10*i, 0, Math.PI*2, true); ctx.fill(); }
這種設置全局透明度的方式不好控制,我們通常通過設置 rgba 顏色值的透明度來設置透明
例子:
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.fillStyle = "#fd0"; ctx.fillRect(0,0,150,37.5); ctx.fillStyle = "#6c0"; ctx.fillRect(0,37,150,37.5); ctx.fillStyle = "#09f"; ctx.fillRect(0,75,150,37.5); ctx.fillStyle = "#f30"; ctx.fillRect(0,112,150,37.5); ctx.fillStyle = "#fff"; for(var i=0; i<10; i++){ ctx.fillStyle = "rgba(255, 255, 255,"+ (i+1)/10 +")"; for(var j=0; j<4; j++){ ctx.fillRect(5+i*14, 5+j*37.5, 14, 27.5); } }
漸變色
線性漸變 createLinearGradient(x1, y1, x2, y2)
createLinearGradient 方法接受 4 個參數,表示漸變的起點 (x1,y1) 與終點 (x2,y2)。
徑向漸變 createRadialGradient(x1, y1, r1, x2, y2, r2)
createRadialGradient 方法接受 6 個參數,前三個定義一個以 (x1,y1) 為原點,半徑為 r1 的圓,后三個參數則定義另一個以 (x2,y2) 為原點,半徑為 r2 的圓。
創建出 canvasGradient 對象后,我們就可以用 addColorStop 方法給它上色了。
gradient.addColorStop(position, color)
addColorStop 方法接受 2 個參數,position 參數必須是一個 0.0 與 1.0 之間的數值,表示漸變中顏色所在的相對位置。例如,0.5 表示顏色會出現在正中間。color 參數必須是一個有效的 CSS 顏色值(如 #FFF, rgba(0,0,0,1),等等)。
線性漸變 示例:
代碼如下:
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); //線性漸變 var lineargradient = ctx.createLinearGradient(0,0,0,140); lineargradient.addColorStop(0,'#00ABEB'); lineargradient.addColorStop(0.5, "#fff"); lineargradient.addColorStop(0.5, "green"); lineargradient.addColorStop(1,'#fff'); var lineargradient2 = ctx.createLinearGradient(0,50,0,95); lineargradient2.addColorStop(0,'#000'); lineargradient2.addColorStop(1,'rgba(0,0,0,0)'); ctx.fillStyle = lineargradient; ctx.strokeStyle = lineargradient2; ctx.fillRect(10,10,130,130); ctx.strokeRect(50,50,50,50);
徑向漸變 示例:
代碼如下:
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); /*徑向漸變*/ var radgrad = ctx.createRadialGradient(45,45,10,52,50,30); radgrad.addColorStop(0, "#fff"); radgrad.addColorStop(0.9, "green"); radgrad.addColorStop(1, "rgba(255,255,255,0)"); //最后一個顏色最好寫成透明的,這樣圓圈以外部分會以這個顏色填充 var radgrad2 = ctx.createRadialGradient(112,120,0,112,120,50); radgrad2.addColorStop(0, "#fff"); radgrad2.addColorStop(0.9, "#FF0188"); radgrad2.addColorStop(1, "rgba(255,1,136,0)"); var radgrad3 = ctx.createRadialGradient(95,15,15,102,20,40); radgrad3.addColorStop(0, '#00C9FF'); radgrad3.addColorStop(0.8, '#00B5E2'); radgrad3.addColorStop(1, 'rgba(0,201,255,0)'); ctx.fillStyle = radgrad; ctx.fillRect(0,0,150,150); ctx.fillStyle = radgrad2; ctx.fillRect(0,0,200,200); ctx.fillStyle = radgrad3; ctx.fillRect(0,0,200,200);
線寬
lineWidth = value
設置線條寬度。 直接給數值,不需要單位,默認單位為像素
例子:
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); //線寬 for(var i=0; i<10; i++){ ctx.lineWidth = i+1; ctx.beginPath(); var a = 5; ctx.moveTo(a+i*14, 5); ctx.lineTo(a+i*14, 140); ctx.stroke(); }
大家會發現,我們繪制出的線條中,基數線條居然是模糊的
那這是什么原因呢!見下圖,用網格來代表 canvas 的坐標格,每一格對應屏幕上一個像素點。在第一個圖中,填充了 (2,1) 至 (5,5) 的矩形,整個區域的邊界剛好落在像素邊緣上,這樣就可以得到的矩形有着清晰的邊緣。
如果你想要繪制一條從 (3,1) 到 (3,5),寬度是 1.0 的線條,你會得到像第二幅圖一樣的結果。實際填充區域(深藍色部分)僅僅延伸至路徑兩旁各一半像素。而這半個像素又會以近似的方式進行渲染,這意味着那些像素只是部分着色,結果就是以實際筆觸顏色一半色調的顏色來填充整個區域(淺藍和深藍的部分)。這就是上例中為何寬度為 1.0 的線並不准確的原因。
要解決這個問題,你必須對路徑施以更加精確的控制。已知粗 1.0 的線條會在路徑兩邊各延伸半像素,那么像第三幅圖那樣繪制從 (3.5,1) 到 (3.5,5) 的線條,其邊緣正好落在像素邊界,填充出來就是准確的寬為 1.0 的線條。
以此原理,我們只要將原來繪制過程中的基數線條 +0.5 像素即可
代碼修改如下(修改地方在9/10行):
1 var c = document.getElementById("myCanvas"); 2 var ctx = c.getContext("2d"); 3 4 //線寬 5 for(var i=0; i<10; i++){ 6 ctx.lineWidth = i+1; 7 ctx.beginPath(); 8 var a = 5; 9 if(i%2 == 0){ 10 a = 5.5; //基數像素避免模糊 11 } 12 ctx.moveTo(a+i*14, 5); 13 ctx.lineTo(a+i*14, 140); 14 ctx.stroke(); 15 }
這樣呈現的效果就不模糊了
線條樣式
lineCap = type 設置線條末端樣式。它可以為下面的三種的其中之一:butt,round 和 square。默認是 butt。
示例:
代碼如下:
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); //線條末尾樣式 var lineCap = ['butt','round','aquare']; ctx.strokeStyle = "#09f"; ctx.beginPath(); ctx.moveTo(10,10); ctx.lineTo(140,10); ctx.moveTo(10,140); ctx.lineTo(140,140); ctx.stroke(); ctx.strokeStyle = "black"; for(var i=0; i<lineCap.length; i++){ ctx.lineWidth = 15; ctx.lineCap = lineCap[i]; ctx.beginPath(); ctx.moveTo(25+i*50,10); ctx.lineTo(25+i*50,140); ctx.stroke(); }
lineJoin = type 設定線條與線條間接合處的樣式。lineJoin 的屬性值決定了圖形中兩線段連接處所顯示的樣子。它可以是這三種之一:round, bevel 和 miter。默認是 miter。
示例:
代碼如下:
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); //線條鏈接處樣式 var lineJoin = ['round','bevel','miter']; ctx.strokeStyle = "#09f"; ctx.lineWidth = 10; for(var i=0; i<lineJoin.length; i++){ ctx.lineJoin = lineJoin[i]; ctx.beginPath(); ctx.moveTo(-5,5+i*40); ctx.lineTo(35,45+i*40); ctx.lineTo(75,5+i*40); ctx.lineTo(115,45+i*40); ctx.lineTo(155,5+i*40); ctx.stroke(); }
虛線
ctx.setLineDash([x1, x2]); //設置設置虛線的間隔 x1表示虛線自身長度 x2表示間隔長度 默認單位:px ctx.lineDashOffset = offset; //設置偏移量 ctx.strokeRect(x,y, width, height); //繪制虛線邊框
例子:跑馬燈效果
代碼如下:
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); //虛線 ctx.strokeStyle = "#09f"; ctx.lineWidth = 4; var offset = 0; function draw(){ ctx.clearRect(0, 0, c.width, c.height); ctx.setLineDash([4,2]); ctx.lineDashOffset = - offset; ctx.strokeRect(100,100,100,100); } function march(){ offset++; if(offset > 16){ offset = 0; } draw(); setTimeout(march, 20) } march();
描繪文字
canvas 提供了兩種方法來渲染文本:
fillText(text, x, y [, maxWidth])
在指定的(x,y)位置填充指定的文本,繪制的最大寬度是可選的.
strokeText(text, x, y [, maxWidth])
在指定的(x,y)位置繪制文本邊框,繪制的最大寬度是可選的.
文本設置(可不掌握):
font = value
當前我們用來繪制文本的樣式. 這個字符串使用和 CSS font 屬性相同的語法. 默認的字體是 10px sans-serif。
textAlign = value
文本對齊選項. 可選的值包括:start, end, left, right or center. 默認值是 start。
textBaseline = value
基線對齊選項. 可選的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默認值是 alphabetic。
direction = value
文本方向。可能的值包括:ltr, rtl, inherit。默認值是 inherit。
另外可以使用 measureText 來測量文本長度 (單位px)
示例:
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); //線條鏈接處樣式 ctx.strokeStyle = "#09f"; ctx.lineWidth = 1; ctx.font = "48px Arial"; ctx.fillText("Hellow World ! " , 50, 100); ctx.strokeText("Hellow World ! ", 50, 200); /* 測量文本長度 */ var text = ctx.measureText("Hellow World ! "); ctx.fillText(text.width , 50, 300);
繪制陰影
通過 ctx.shadow來設置
例子:
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); /* 陰影 */ ctx.shadowOffsetX = 2; //陰影x方向偏移量 ctx.shadowOffsetY = 2; //陰影y方向偏移量 ctx.shadowBlur = 5; //陰影模糊度 ctx.shadowColor = "rgba(0,0,0,0.5)"; //陰影顏色 ctx.font = "20px Times New Roman"; ctx.fillStyle = "black"; ctx.fillText("Sample String", 5, 30);
效果:
繪制圖片
使用 drawImage(image, x, y)
其中 image 是 Image對象 或者 canvas 對象,x 和 y 是其在目標 canvas 里的起始坐標。
示例(圖片使用了網絡圖片,所以地址比較長):
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); var img = new Image(); //創建了一個Image對象 img.src = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1507713846845&di=51e9f2958ed57789a0fa8e656a8c57c8&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fimage%2Fc0%253Dshijue1%252C0%252C0%252C294%252C40%2Fsign%3D952c2aeb14178a82da3177e39e6a19f8%2Fb8014a90f603738dbe7ddc05b91bb051f819ece6.jpg"; img.onload = function(){ ctx.drawImage(img,50,50,300,190); //將圖片繪制到canvas中 }
這里繪制圖片也可以使用 從頁面獲取的 Img dom對象,如: ctx.drawImage(document.getElementsByTagName("img")[0],100,100,300,190);
圖片導出/下載圖片
使用c.toDataURL()對canvas圖片數據進行輸出
示例:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> *{margin: 0;padding: 0} body{ margin: 30px; } canvas{ border: 1px solid #a4e2f9; float: left; } img{ float:left; margin: 0 10px; } </style> </head> <body> <canvas height="300" width="300" id="myCanvas"></canvas> <img id="showImg" src="" alt=""/> <a id="downloadBtn" href="" download="testImg">下載</a> <!-- a標簽設置 download="圖片名稱" 來設置點擊下載 --> <script> var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.fillStyle = "#fd0"; ctx.fillRect(0,0,150,37.5); ctx.fillStyle = "#6c0"; ctx.fillRect(0,37,150,37.5); ctx.fillStyle = "#09f"; ctx.fillRect(0,75,150,37.5); ctx.fillStyle = "#f30"; ctx.fillRect(0,112,150,37.5); ctx.fillStyle = "#fff"; for(var i=0; i<10; i++){ ctx.fillStyle = "rgba(255, 255, 255,"+ (i+1)/10 +")"; for(var j=0; j<4; j++){ ctx.fillRect(5+i*14, 5+j*37.5, 14, 27.5); } } //將canvas生成的圖像 鏈接到 img標簽 和 a標簽下載鏈接中 document.getElementById("showImg").setAttribute("src",c.toDataURL('png')); //輸出為png格式 document.getElementById("downloadBtn").setAttribute("href",c.toDataURL('png')); </script> </body> </html>
獲取和操作canvas像素信息
可以用getImageData()方法,獲得一個包含畫布場景像素數據的ImageData對像
var myImageData = ctx.getImageData(left, top, width, height);
你可以用putImageData()方法去對場景進行像素數據的寫入。
ctx.putImageData(myImageData, dx, dy);
dx和dy參數表示你希望在場景內左上角繪制的像素數據所得到的設備坐標。
下面示例中,我們獲取canvas已經繪制的圖形信息,然后經過去色處理,然后再在旁邊重新繪制去色的圖形,具體解釋在代碼注釋中
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> *{margin: 0;padding: 0} body{ margin: 30px; } canvas{ border: 1px solid #a4e2f9; } </style> </head> <body> <canvas height="300" width="600" id="myCanvas"></canvas> <script> var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.fillStyle = "#fd0"; ctx.fillRect(0,0,150,37.5); ctx.fillStyle = "#6c0"; ctx.fillRect(0,37,150,37.5); ctx.fillStyle = "#09f"; ctx.fillRect(0,75,150,37.5); ctx.fillStyle = "#f30"; ctx.fillRect(0,112,150,37.5); ctx.fillStyle = "#fff"; for(var i=0; i<10; i++){ ctx.fillStyle = "rgba(255, 255, 255,"+ (i+1)/10 +")"; for(var j=0; j<4; j++){ ctx.fillRect(5+i*14, 5+j*37.5, 14, 27.5); } } //獲取canvas的圖片數據 var imageData = ctx.getImageData(0, 0, 150, 150); //獲取canvas的繪制區域的像素信息 var idata = imageData.data; //真實的像素rgba信息在 data中 console.log(idata) /* idata中的數據是一個數組,每四個分別代表一個像素點的 r g b a 值 ,如下: [159,159,159,255,159,159,159,255......] R - 紅色 (0-255) G - 綠色 (0-255) B - 藍色 (0-255) A - alpha 通道 (0-255; 0 是透明的,255 是完全可見的) */ for(var i=0; i<idata.length; i+=4){ var avg = (idata[i]+idata[i+1]+idata[i+2])/3; idata[i] = avg; // red idata[i + 1] = avg; // green idata[i + 2] = avg; // blue } ctx.putImageData(imageData, 200, 0); </script> </body> </html>
今天就講到這里,下節課我們講解:canvas變換 / 路徑保存 / 綜合案例
關注公眾號,博客更新即可收到推送