canvas學習之路--記錄(二)
接著上面...
一、圖形組合
當我們在canvas中繪制的圖形重合在一起的時候,能看到哪個圖形則完全取決於圖形的繪制順序(後繪制的圖形會覆蓋掉之前繪制的圖形,當然,如果後面繪制的圖形顏色透明度不為1,則會受之前繪制圖形的顏色影響).
圖形上下文對象的globalCompositeOperation屬性能讓我們自己決定圖形的組合方式,如context.globalCompositeOperation = type;
其中type的值必須是下面幾種字符串之一:
type值 | type所代表的的含義 |
source-over(默認值) | 為默認值,表示新圖形覆蓋在原有圖形之上 |
destination-over | 在原有圖形之下繪制新圖形 |
source-in | 只顯示新圖形中與原有圖形相重疊的部分,其他部分則變成透明 |
destination-in | 只顯示原有圖形中與新圖形相重疊的部分,新圖形與原有圖形的其他部分都透明 |
source-out | 只顯示新圖形中與原有圖形不重疊的部分,新圖形與原有圖形的其他部分變成透明 |
destination-out | 只顯示原有圖形中與新圖形不重疊的部分,新圖形與原有圖形的其他部分變成透明 |
source-atop | 只繪制新圖形中與原有圖形重疊的部分與未被重疊覆蓋的原有圖形,新圖形的其他部分變成透明 |
destination-atop | 只繪制原有圖形中被新圖形覆蓋的部分與新圖形的其他部分,原有圖形的其他部分變成透明 |
lighter | 原有圖形與新圖形均繪制,重疊部分做加色處理 |
xor | 只繪制新圖形中與原有圖形不重疊的部分,重疊部分變成透明 |
copy | 只繪制新圖形,原有圖形中未與新圖形重疊的部分變成透明 |
可在繪制路徑之前,提前設置context.globalCompositeOperation = [[type]]-->11中類型,如果,未填寫正確類型,則會是默認類型
二、給圖形繪制陰影
使用圖形上下文對象的幾個關於陰影繪制的屬性即可添加陰影效果
shadowOffsetX-->陰影的橫向偏移量
shadowOffsetY-->陰影的縱向偏移量
shadowColor-->陰影的顏色
shadowBlur-->陰影的模糊範圍,屬性可選,它表示陰影邊緣的模糊範圍,如果不希望陰影的邊緣太清晰,需要將陰影的邊緣模糊化時可以使用該屬性,設定的屬性值為比0大的數字,否則會被忽略,一般設定0到10之間
註:使用圖形上下文對象的繪制陰影屬性,這幾個屬性與路徑無關,只要設定一次,全部的圖形都會具有陰影效果,如果想讓某一個圖形沒有陰影效果,只需要重新將shadowColor設定為rgba(0,0,0,0)就可以了.
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext(‘2d‘); $ctx.fillStyle = ‘#eef‘; $ctx.fillRect(0,0,400,300); $ctx.shadowOffsetX = 10; $ctx.shadowOffsetY = 10; $ctx.shadowColor = ‘rgba(100,100,100,0.5)‘; $ctx.shadowBlur = 7.5; $ctx.translate(200,50); for (var i = 0; i < 50; i++) { $ctx.translate(25,25); $ctx.scale(0.95,0.95); $ctx.rotate(Math.PI/10); create5Star($ctx); $ctx.fill(); } } function create5Star (context) { var n = 0, dx = 100, dy = 0, s = 50; context.beginPath(); context.fillStyle = ‘rgba(255,0,0,0.5)‘; var x = Math.sin(0), y = Math.cos(0), dig = Math.PI/5*4; for (var i = 0; i < 5; i++) { var x = Math.sin(i*dig), y = Math.cos(i*dig); context.lineTo(dx+x*s,dy+y*s); } context.closePath(); }
三、使用圖像
接下來是我個人認為比較重要的幾個用途.
1.繪制圖像
在canvas中可以讀取圖像文件,然後將之繪制在畫布當中,繪制圖像的方法:drawImage
context.drawImage(image,x,y); ===> x,y-->繪制時該圖像在畫布中的起始坐標
context.drawImage(image,x,y,w,h); ===> 前三個參數與上面一樣,w、h則是指繪制時的圖像(畫布區域出現的圖像的寬高)的寬度與高度,第一種方法中省略了這兩個參數,所以繪制出來的圖像與原圖大小相同,
context.drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh); ===> 第三種方法則是將畫布中已經繪制好的圖像的全部或則局部區域復制到畫布中的另一個位置上,除了image仍然代表被復制的圖像文件,前面四個參數是原圖像的被復制區域,後面四個參數是畫布中繪制出圖像的區域.
上面三種形式中,image都代表一個圖片對象,用該對象來裝載圖片文件.
繪制圖像時需要使用不帶參數的new方法創建Image對象,然後設定該Image對象的src屬性為需要繪制的圖像文件的路徑,如下
var image = new Image(); image.src = ‘image1.jpg‘; //設置圖像路徑
然後就可以使用drawImage方法繪制該圖像文件了
事實上,即時設定好Image對象的src屬性後,也不一定立刻就能把圖形繪制完畢,譬如,有時該圖像文件是一個來源於網絡的比較大的圖像文件,這時候用戶就得耐心等待圖像全部加載完畢才能看見該圖像了.
這種情況下,可以使用
image.onload = function(){} //繪制圖像的函數
在Image對象的onload事件中同步執行繪制圖像的函數,就可以一邊裝載一邊繪制了.
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext(‘2d‘); var img = new Image(); img.src = ‘http://img17.3lian.com/d/file/201702/22/1005a2e0825ffe290b3f697404ee8038.jpg‘; img.onload = function () { $ctx.drawImage(img,0,0,400,300); } }
2.圖像平鋪
使用圖像上下文對象的createPattern方法就可以實現圖像平鋪效果: context.createPattern(image,type);
其中image為要平鋪的對象,type的值則有下面幾種類型(註意,type值為字符串類型)
no-repeat:不平鋪
repeat-x:橫方向平鋪
repeat-y:縱方向平鋪
repeat:全方向平鋪
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext(‘2d‘); var img = new Image(); img.src = ‘http://img17.3lian.com/d/file/201702/22/1005a2e0825ffe290b3f697404ee8038.jpg‘; img.onload = function () { var ptrn = $ctx.createPattern(img,‘repeat‘); $ctx.fillStyle = ptrn; $ctx.fillRect(0,0,400,300); } }
3.圖片裁剪
使用canvas繪制圖像的時候,我們經常會想要保留圖像的一部分,可以使用canvas API自帶的圖像裁剪功能來實現.
使用圖形上下文對象的不帶參數的clip方法來實現canvas元素的圖像裁剪功能.該方法使用路徑來對canvas畫布設置一個裁剪區域,因此必須先創建好路徑,等路徑創建完成後,再調用clip方法設置裁剪區域.
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext(‘2d‘); var gr = $ctx.createLinearGradient(0,400,300,0); gr.addColorStop(0,‘rgb(255,255,0)‘); gr.addColorStop(1,‘rgb(0,255,255)‘); $ctx.fillStyle = gr; $ctx.fillRect(0,0,400,300); var img = new Image(); img.onload = function () { drawImg($ctx,img); } img.src = ‘img/aaa.jpg‘; } function drawImg (context,image) { create5StarClip(context); context.drawImage(image,-50,-150,300,300); } function create5StarClip (context) { var n = 0, dx = 100, dy = 0, s = 150; context.beginPath(); context.translate(100,150); var x = Math.sin(0), y = Math.cos(0), dig = Math.PI/5*4; for (var i = 0; i < 5; i++) { var x = Math.sin(i*dig), y = Math.cos(i*dig); context.lineTo(dx+x*s,dy+y*s); } context.clip(); }
4.像素處理(本人覺得這個東西實在是太牛逼了)
使用canvas API能夠獲取圖像中的每一個像素,然後得到該像素顏色的rgb值或rgba值.使用圖形上下文對象的getImageData方法來獲取圖像中的像素,如: var imagedata = context.getImageData(sx,sy,sw,sh);
其中:sx,sy--->分別表示所獲取區域的起點橫坐標和縱坐標,sw,sh--->表示所獲區域的寬度和高度.
imagedata變量是一個CanvasPixelArray對象(畫布像素數組對象),具有height,width,data等屬性.其中data屬性是一個保存像素數據的數組,內容類似"[r1,g1,b1,a1,r2,g2,b2,a2,......]",其中r1,g1,b1,a1為第一個像素的紅色值,綠色值,藍色值,透明度值;r2,g2,b2,a2分別為第二個像素的紅色值,綠色值,藍色值,透明度值,以此類推,data.length/4為所取得像素的數量
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext(‘2d‘); $ctx.fillStyle = ‘#eef‘; $ctx.fillRect(0,0,400,300); var img = new Image(); img.src = ‘img/aaa.jpg‘; img.onload = function () { $ctx.drawImage(img,0,0,400,300); } var imagedata = $ctx.getImageData(0,0,400,300); console.log(imagedata); console.log(imagedata.data.length); }
取得這些像素之後,就可以對這些像素進行處理了,其中可以用Canvas API將圖像進行反顯操作.
首先將數組中每個像素的顏色進行反顯操作,然後保存回像素組,最後使用圖形上下文對象的putImageData方法將反顯操作後的圖像重新繪制在畫布上.
如:context.putImageData(imagedata,dx,dy[,dirtyX,dirtyY,dirtyWidth,dirtyHeight]);
該方法的函數,其中imagedata為前面所述的像素數組,dx,dy分別表示重繪圖像的起點橫坐標,起點縱坐標,後面的dirtyX,dirtyY,dirtyWidth,dirtyHeight則是可選參數,是一個矩形的起點橫坐標,起點縱坐標,寬度與高度,加上這四個參數,則只繪制像素數組中這個矩形範圍內的圖像!
註:Canvas API的像素操作頁只有部分瀏覽器支持.
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext(‘2d‘); var img = new Image(); img.src = ‘img/aaa.jpg‘; img.onload = function () { $ctx.drawImage(img,0,0,400,300); var imagedata = $ctx.getImageData(0,0,400,300); for (var i = 0; i < imagedata.data.length; i+=4) { imagedata.data[i+0] = 255 - imagedata.data[i+0]; imagedata.data[i+1] = 255 - imagedata.data[i+1]; imagedata.data[i+2] = 255 - imagedata.data[i+2]; } $ctx.putImageData(imagedata,0,0); } }
四、繪制文字
在HTML5中,可以在canvas畫布中進行文字的繪制,同時也可以指定繪制文字的字體,大小,對齊方式等,還可以進行文字的紋理填充等.
繪制文字的方法:fillText或strokeText
fillText(text,x,y[,maxWidth]) 該方法用填充方式繪制字符串,text-->所要繪制的文字 x,y-->繪制文字的起點橫縱坐標,第四個參數maxWidth為可選參數,表示顯示文字時的最大寬度,可以防止文字溢出
strokeText(text,x,y[,maxWidth]) 同上
在對文字進行繪制之前,可以對該對象的有關文字繪制的屬性進行設置:
font : 設置文字字體
textAlign : 設置文字水平對齊方式 屬性值:start(默認值),end,left,right,center
textBaseline : 設置文字垂直對齊方式 屬性值:top,hanging,middle,alpabetic(默認值),ideographic,bottom
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext(‘2d‘); $ctx.fillStyle = ‘#00f‘; $ctx.font = ‘italic 30px sans-serif‘; $ctx.textBaseline = ‘top‘; $ctx.fillText(‘上山打松鼠‘,0,0); $ctx.font = ‘bold 30px sans-serif‘; $ctx.strokeText(‘上山打松鼠‘,0,50); }
補充 : context.measureText(text)能夠得到文字的寬度
五、最後的補充
1.保存與恢復狀態
在之前介紹的圖像裁剪當中,還有一個遺留問題,當我們裁剪過圖像之後,想要取消裁剪範圍繼續繪制其他圖形,這就需要使用Canvas API中的save和restore兩個方法,這兩個方法都不帶參數,分別用來保存與恢復圖形上下文的當前回話狀態.這裏的繪畫狀態指前面所講的坐標原點,變形時的變換矩陣,以及圖形上下文對象的當前屬性值等很多內容.
在需要保存與恢復當前狀態時,首先調用save方法將當前狀態保存在棧中,做完想做的工作後,再調用restore從棧中取出之前保存的圖形上下文的狀態進行恢復.
context.save(); //保存繪畫狀態,之後可以進行想要的操作 // 新的操作 context.restore(); //恢復狀態
當出現下面幾種情況下,可以應用:
圖像或圖形變形
圖像裁剪
改變圖形上下文的以下屬性時:fillStyle, font, globalAlpha, globalCompositeOperation, lineCap, lineJoin, lineWidth, miterLimit, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, strokeStyle, textAlign, textBaseline.
2.保存文件
Canvas API保存文件的原理實際上是把當前繪畫狀態輸出到一個data URL地址所指向的數據中的過程(其實就是圖片轉成的base64編碼,之前的博客中有介紹);
其方法為canvas.toDataURL(type) (註:此方法為canvas畫布對象的方法)
$myCanvas.toDataURL(‘image/jpeg‘);
3.動畫在canvas中實際上就是不斷的擦除(context.clearRect),重繪的過程,當然比較復雜的情況下,也會插入當前繪制狀態的保存與恢復...
好了,寫到這兒,canvas中基礎的知識點就大致過了一遍,本文參考《HTML5與CSS3權威指南》一書中的canvas篇,以後會帶來更多canvas實際當中的應用
canvas學習之路--記錄(二)