上一篇博客介紹了canvas基礎用法,本文將更進一步,介紹canvas的圖形處理和進階用法html
圖形變換是指用數學方法調整所繪形狀的物理屬性,其實質是座標變形。全部的變換都依賴於後臺的數學矩陣運算。談到圖形變換,不得不得說的三個基本變換方法就是canvas
平移變換:translate(x,y)
旋轉變換:rotate(deg)
縮放變換:scale(sx,sy)
【translate()】跨域
translate(x,y):將座標原點移動到(x,y)。執行這個變換以後,座標(0,0)會變成以前由(x,y)表示的點數組
平移變換,顧名思義,就是通常的圖形位移。好比這裏想將位於(100,100)的矩形平移至(200,200)點。那麼只要在繪製矩形以前加上context.translate(100,100)
便可瀏覽器
下面結合代碼來看看效果服務器
<canvas id="drawing" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.beginPath(); context.fillStyle = "#00AAAA"; context.fillRect(0,0,100,50); context.fillStyle = "red"; context.translate(50,50); context.fillRect(0,0,100,50); } </script>
青藍色矩形經過調用translate()方法後,將矩形向右移動了(50,50),並變成了紅色,尺寸不改變dom
想把矩形再向右移動(50,50),應該使用translate(50,50),仍是從最開始算起,使用traslate(100,100)呢?ide
<script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.beginPath(); context.fillStyle = "#00AAAA"; context.fillRect(0,0,100,50); context.fillStyle = "red"; context.translate(50,50); context.fillRect(0,0,100,50); context.fillStyle = "lightblue"; context.translate(50,50); context.fillRect(0,0,100,50); } </script>
由結果可知,使用translate(50,50)便可實現,依照translate()的定義,第一次進行translate()變換時,已經變換了座標系的原點,此次是基於新座標系又一次地變換spa
【狀態保存】rest
因爲屢次運行變換後,座標系可能就亂套了,因此最好以最初狀態爲參照物。這時就須要用到save()和resore()方法
save()方法能夠保存當前環境的狀態,並返回某組屬性與變換的組合。全部設置都會進入一個棧結構,得以妥善保管
restore()方法能夠在保存設置的棧結構中向前返回一級,恢復以前保存過的路徑狀態和屬性。連續調用save()方法能夠把更多設置保存到棧結構中,以後再連續調用restore()方法能夠一級一級返回
若是使用狀態保存,則代碼以下所示
<script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.beginPath(); context.fillStyle = "#00AAAA"; context.fillRect(0,0,100,50); context.save(); context.fillStyle = "red"; context.translate(50,50); context.fillRect(0,0,100,50); context.restore(); context.save(); context.fillStyle = "lightblue"; context.translate(100,100); context.fillRect(0,0,100,50); context.restore(); } </script>
這樣,每次圖形變換,都以(0,0)點爲基準,不會形成座標系的混亂
【rotate()】
rotate(angle):圍繞原點旋轉圖像angle弧度
同畫圓弧同樣,這裏的rotate(deg)
傳入的參數是弧度,不是角度。同時須要注意的是,這個的旋轉是以座標系的原點(0,0)爲圓心進行的順時針旋轉。因此,在使用rotate()
以前,一般須要配合使用translate()
平移座標系,肯定旋轉的圓心。即,旋轉變換一般搭配平移變換使用的。
最後一點須要注意的是,Canvas是基於狀態的繪製,因此每次旋轉都是接着上次旋轉的基礎上繼續旋轉,因此在使用圖形變換的時候必須搭配save()
與restore()
方法,一方面重置旋轉角度,另外一方面重置座標系原點
下面是一個例子
<canvas id="drawing" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.beginPath(); context.fillStyle = "#00AAAA"; context.fillRect(0,0,50,50);
context.save(); context.fillStyle = "red"; context.rotate(30*Math.PI/180); context.fillRect(0,0,50,50); context.restore();
context.save(); context.fillStyle = "green"; context.rotate(60*Math.PI/180); context.fillRect(0,0,50,50); context.restore(); } </script>
由結果可知,元素是圍繞原點進行順時針旋轉rotate的
下面以元素左上角爲圓心進行旋轉
<script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.beginPath(); context.fillStyle = "#00AAAA"; context.fillRect(30,30,50,50); context.save(); context.fillStyle = "red"; context.translate(30,30); context.rotate(30*Math.PI/180); context.fillRect(0,0,50,50); context.restore(); context.save(); context.fillStyle = "lightgreen"; context.translate(30,30); context.rotate(60*Math.PI/180); context.fillRect(0,0,50,50); context.restore(); } </script>
下面以元素中心點爲圓心進行旋轉,會出現以下所示神奇的效果
<canvas id="drawing" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.beginPath(); context.fillStyle = "rgba(255,0,0,0)"; context.fillRect(45,45,50,50); for(var i = 1; i <=10; i++){ context.save(); context.fillStyle = "rgba(255,0,0,0.25)"; context.translate(70,70); context.rotate(i*36*Math.PI/180); context.fillRect(0,0,50,50); context.restore(); } } </script>
【scale()】
scale(scaleX,scaleY):縮放圖像,在x方向上乘以scaleX,在Y方向上乘以scaleY。scaleX和scaleY的默認值是1
context.scale(2,2)
就是對圖像放大兩倍。看上去簡單,實際用起來仍是有一些問題的。先來看一段代碼
<canvas id="drawing" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.beginPath(); context.lineWidth = 3; context.strokeRect(10,10,50,50); for(var i = 1; i <= 2; i++){ context.save(); context.scale(i,i); context.strokeRect(20,20,50,50); context.restore(); } } </script>
結果以下,左上角頂點的座標變了,而是線條的粗細也變了
所以,對於縮放變換有兩點問題須要注意:
一、縮放時,圖形左上角座標的位置也會對應縮放
二、縮放時,圖像線條的粗細也會對應縮放
上面所說的座標變換的三種方式——平移translate()
,縮放scale()
,以及旋轉rotate()
均可以經過transform()
作到
在介紹矩陣變換transform()
前,先來介紹下什麼是變換矩陣
[注意]關於transform的詳細介紹,移步CSS3的transform介紹
以上是Canvas中transform()
方法所對應的變換矩陣
【transform()】
而此方法正是傳入圖中所示的六個參數,具體爲context.transform(a,b,c,d,e,f)
各參數意義對應以下表:
參數 意義
a 水平縮放(1)
b 水平傾斜(0)
c 垂直傾斜(0)
d 垂直縮放(1)
e 水平位移(0)
f 垂直位移(0)
建議使用transform()
的時候,能夠在以下幾個狀況下使用:
一、使用context.transform (1,0,0,1,dx,dy)
代替context.translate(dx,dy)
二、使用context.transform(sx,0,0,sy,0,0)
代替context.scale(sx, sy)
三、使用context.transform(1,tany,tanx,1,0,0)
來實現傾斜效果
<canvas id="drawing" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.beginPath(); context.lineWidth = 3; context.strokeRect(10,10,50,50); context.save(); context.transform (1,0,0,1,30,30); context.strokeStyle = 'pink'; context.strokeRect(0,0,50,50); context.restore(); context.save(); context.transform(2,0,0,2,0,0) context.strokeStyle = 'lightblue'; context.strokeRect(50,10,50,50); context.restore(); context.save(); context.transform(1,0,Math.tan(30*Math.PI/180),1,0,0); context.strokeStyle = 'lightgreen'; context.strokeRect(200,10,50,50); context.restore(); } </script>
【setTransform()】
setTransform():將變換矩陣重置爲默認狀態,而後再調用transform()
transform()
方法的行爲相對於由 rotate()
,scale()
, translate()
, or transform()
完成的其餘變換。例如:若是已經將繪圖設置爲放到兩倍,則 transform()
方法會把繪圖放大兩倍,那麼繪圖最終將放大四倍。這一點和以前的變換是同樣的。
可是setTransform()
不會相對於其餘變換來發生行爲。它的參數也是六個,context.setTransform(a,b,c,d,e,f)
,與transform()
同樣
當前面的代碼已經使用了多個transform()、translate()、rotate()、scale()等變換方法,沒法輕易地從當前的矩陣變化到想要的矩陣時,就可使用setTransform()方法將矩陣重置爲默認狀態,而後再調用transform()
cxt.transform(1,0,0,1,50,100);
cxt.transform(2,0,0,1.5,0,0);
cxt.transform(1.-0.2,-0.2,1,0,0);
cxt.setTransform(1,0,0,1,100,100);
2D上下文會根據如下4個屬性的值自動爲形狀或路徑繪製出陰影
[注意]關於CSS陰影box-shadow的詳細狀況移步至此
shadowColor: 用CSS顏色格式表示的陰影顏色(默認爲黑色)
shadowOffsetX: 形狀或路徑x軸方向的陰影偏移量(默認爲0)
shadowOffsetY: 形狀或路徑y軸方向的陰影偏移量(默認爲0)
shadowBlur: 模糊的像素數(默認爲0,即不模糊)
這四個屬性只要設置了第一個和剩下三個中的任意一個就有陰影效果。不過一般狀況下,四個屬性都要設置
[注意]要先設置陰影,再繪製圖形
下面代碼建立一個向右下方位移各5px的紅色陰影,模糊2px
<canvas id="drawing" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.shadowColor = "red"; context.shadowOffsetX = 5; context.shadowOffsetY = 5; context.shadowBlur= 2; context.fillStyle = 'lightblue'; context.fillRect(10,10,100,100); } </script>
下面是一個文字陰影的效果
[注意]關於CSS3屬性文本陰影text-shadow的詳細狀況移步至此
<script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.shadowColor = "rgba(0,0,0,0.5)"; context.shadowOffsetX = 5; context.shadowOffsetY = 5; context.shadowBlur= 2; context.font= '30px 微軟雅黑'; context.fillText("小火柴的藍色理想",40,60); } </script>
全局透明globalAlpha是一個介於0和1之間的屬性值(包括0和1),用於指定全部繪製的透明度(默認值爲1)。若是後續全部操做都基於相同透明度,能夠先把globalAlpha設置爲適當值,而後繪製,最後再設置回默認值1
[注意]全局透明globalAlpha也是一個基於狀態的屬性,因此須要先設置該屬性,再繪製圖形
<script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.fillStyle = 'lightblue'; context.fillRect(10,10,100,100); context.globalAlpha = 0.5; context.fillStyle = 'lightblue'; context.fillRect(120,10,100,100); context.globalAlpha = 1; } </script>
兩個圖形重合的時候,就涉及到了對這兩個圖形的合成處理
globalCompositeOperation表示後繪製的圖形怎樣與先繪製的圖形結合,屬性值是字符串,可能值以下:
source-over(默認):後繪製的圖形位於先繪製的圖形上方
source-in:後繪製的圖形與先繪製的圖形重疊的部分可見,二者其餘部分徹底透明
source-out:後繪製的圖形與先繪製的圖形不重疊的部分可見,先繪製的圖形徹底透明
source-atop:後繪製的圖形與先繪製的圖形重疊的部分可見,先繪製的圖形不受影響
destination-over:後繪製的圖形位於先繪製的圖形下方,只有以前透明像素下的部分纔可見
destination-in:後繪製的圖形位於先繪製的圖形下方,二者不重疊的部分徹底透明
destination-out:後繪製的圖形擦除與先繪製的圖形重疊的部分
destination-atop:後繪製的圖形位於先繪製的圖形下方,在二者不重疊的地方,先繪製的圖形會變透明
lighter:後繪製的圖形與先繪製的圖形重疊部分的值相加,使該部分變亮
copy:後繪製的圖形徹底替代與之重疊的先繪製圖形
xor:後繪製的圖形與先繪製的圖形重疊的部分執行"異或"操做
[注意]合成操做只能寫在兩個圖形之間
使用Canvas繪製圖像的時候,常常會想要只保留圖像的一部分,這時可使用canvas API再帶的圖像裁剪功能clip()來實現這一想法
Canvas API的圖像裁剪功能是指,在畫布內使用路徑,只繪製該路徑內所包含區域的圖像,不會只路徑外的圖像。這有點像Flash中的圖層遮罩
使用圖形上下文的不帶參數的clip()
方法來實現Canvas的圖像裁剪功能。該方法使用路徑來對Canvas設置一個裁剪區域。所以,必須先建立好路徑。建立完整後,調用clip()
方法來設置裁剪區域
[注意]裁剪是對畫布進行的,裁切後的畫布不能恢復到原來的大小,也就是說畫布是越切越小的,要想保證最後仍然能在canvas最初定義的大小下繪圖須要注意save()
和restore()
<button id="btn">裁剪</button> <canvas id="drawing" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); drawing.style.background = '#333'; var oBtn =document.getElementById('btn'); oBtn.onclick = function(){ context.arc(50,50,40,0,2*Math.PI,false); context.fillStyle = '#fff'; context.fill(); context.clip(); } } </script>
可使用clip()實現相似於探照燈的效果
把一幅圖像繪製到畫布上可使用drawImage()方法,根據指望的最終結果的不一樣,調用這個方法時,可使用三種不一樣的參數組合
一、最簡單的調用方式是傳入一個<img>元素,以及繪製該圖像的起點的x和y座標
context.drawImage(img,10,10);
二、若想要改變繪製的圖像的大小,能夠多傳入兩個參數,分別表示目標寬度和目標高度
context.drawImage(img,50,10,20,30);
三、還能夠選擇把圖像中的某個區域繪製到上下文中。drawImage()方法的這種調用方式總共須要傳入9個參數:要繪製的圖像、源圖像的x座標、源圖像的y座標、源圖像的寬度、源圖像的高度、目標圖像的x座標、目標圖像的y座標、目標圖像的寬度、目標圖像的高度
context.drawImage(img,0,10,50,50,0,100,40,60);
[注意]在引入圖像時,要先使用onload()事件,等圖像加載完成後,再進行操做
var image = new Image();
image.src="xx/xxx.jpg"; image.onload = function(){ cxt.drawImage(image,0,0); }
除了給drawImage()方法傳入<img>元素外,還能夠傳入另外一個<canvas>元素做爲其第一個參數
使用toDataURL()方法能夠導出在canvas元素上繪製的圖像。這個方法接受一個參數,即圖像的MIME類型格式,並且適合用於建立圖像的任何上下文
[注意]toDataURL()方法只能夠在服務器端使用,在本地無效,且不可跨域
var imgURI = drawing.toDataURL('image/png'); var image = document.createElement('img'); image.src=imgURI;
下面是一個實例,把一幅圖像繪製到畫布上
<script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); var image = new Image(); image.src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/chunfen.jpg"; image.onload = function(){ context.drawImage(image,0,0); } } </script>
接下來,繪製路徑, 並使用clip()裁剪,只在裁剪區域內顯示圖片
<canvas id="drawing" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); context.arc(80,60,50,0,2*Math.PI,false); context.fillStyle = '#fff'; context.fill(); context.clip(); var image = new Image(); image.src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/chunfen.jpg"; image.onload = function(){ context.drawImage(image,0,0); } } </script>
【獲取】
2D上下文能夠經過getImageData()取得原始圖像數據。這個方法接收4個參數:畫面區域的x和y座標以及該區域的像素寬度和高度
例如,要取得左上角座標爲(10,5)、大小爲50*50像素的區域的圖像數據,可使用如下代碼:
var imageData = context.getImageData(10,5,50,50);
返回的對象是ImageData的實例,每一個ImageData對象有3個屬性:width\height\data
一、width:表示imageData對角的寬度
二、height:表示imageData對象的高度
三、data是一個數組,保存着圖像中每個像素的數據。在data數組中,每個像素用4個元素來保存,分別表示red、green、blue、透明度
[注意]圖像中有多少像素,data的長度就等於像素個數乘以4
//第一個像素以下
var data = imageData.data;
var red = data[0];
var green = data[1];
var blue = data[2];
var alpha = data[3];
數組中每一個元素的值是在0-255之間,可以直接訪問到原始圖像數據,就可以以各類方式來操做這些數據
下面來獲取canvas中某一點的原始圖像數據
[注意]若是要使用getImageData()獲取的canvas中包含drawImage()方法,則該方法中的URL不能跨域
<script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var context = drawing.getContext('2d'); var image = new Image(); image.src="chunfen.jpg"; image.onload = function(){ context.drawImage(image,0,0); var imageData = context.getImageData(10,5,50,50); var data = imageData.data //42 189 209 255 console.log(data[0],data[1],data[2],data[3]); } } </script>
從代碼中可知,(10,5)點座標的紅色值爲42,綠色值爲189,藍色值爲209,透明度爲255(即不透明)
【建立】
createImageData(width,height)方法建立新的空白ImageData對象。新對象的默認像素值 transparent black,至關於rgba(0,0,0,0)
var imgData = context.createImageData(100,100);
【修改】
putImageData()方法將圖像數據從指定的ImageData對象放回畫布上,該方法共有如下參數
imgData:要放回畫布的ImageData對象(必須)
x:imageData對象的左上角的x座標(必須)
y:imageData對象的左上角的y座標(必須)
dirtyX:在畫布上放置圖像的水平位置(可選)
dirtyY:在畫布上放置圖像的垂直位置(可選)
dirtyWidth:在畫布上繪製圖像所使用的寬度(可選)
dirtyHeight:在畫布上繪製圖像所使用的高度(可選)
[注意]參數3到7要麼都沒有,要麼都存在
context.putImageData(imgData,0,0); context.putImageData(imgData,0,0,50,50,200,200);
下面利用上面的方法來實現一個灰階過濾效果,經過getImageData()取得每一個像素的紅、綠、藍三種顏色的色值後,計算出它們的平均值,再把這個平均值設置爲每一個顏色的值,再調用putImageData()把圖像繪製到畫布上
<canvas id="drawing1" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <canvas id="drawing2" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var drawing1 = document.getElementById('drawing1'); if(drawing1.getContext){ var cxt1 = drawing1.getContext('2d'); var img = new Image(); img.src="chunfen.jpg"; img.onload = function(){ cxt1.drawImage(img,0,0); var imageData = cxt1.getImageData(0,0,img.width,img.height); setImage(imageData); } } var drawing2 = document.getElementById('drawing2'); if(drawing2.getContext){ var cxt2 = drawing2.getContext('2d'); function setImage(imageData){ var data = imageData.data; for(var i = 0, len = data.length; i < len; i+=4){ var red = data[i]; var green = data[i+1]; var blue = data[i+2]; var alpha = data[i+3]; var average = Math.floor((red+green+blue)/3); data[i] = data[i+1] = data[i+2] = average; } imageData.data = data; cxt2.putImageData(imageData,0,0); } } </script>
實現顏色反轉效果也是相似的原理,將每一個色值從新賦值爲255-當前值便可
<canvas id="drawing1" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <canvas id="drawing2" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var drawing1 = document.getElementById('drawing1'); if(drawing1.getContext){ var cxt1 = drawing1.getContext('2d'); var img = new Image(); img.src="data:image/gif;base64,R0lGODlhIAAgAPcAAP91AafK42iOqf+9RP/Kkv/x5P+TPP/Ah5C20P+NIv/ss/+6e+Du9/+ZAP/qyv/iv32ivP+bSP+8X//eqP/8+bW4uf+LAP+lIv/x2LrV6P/chP+0Of+bEP+nWqfE2f++Wf/Udf99EIOrxv/gtP+LEP/Xhf+oP//ll//sz/+sIf+eJ/+2S/+/Uv/MZmuUsf+CAP/17Z3A2e34///Pc7PG1P/dlf+lEP+UAP+iAP+xUf+sJ//x3v/di//LhP/npXOcuP/Ga//VeYGow/+tSMfa54uwyv+hF//WiqrN5f/tvf+uO//Me/+YQv+3Qv/Jcf/Nav/pv/+kB7jL2P+TIf96AP/00//GXP/js/+ZM//env+sVP///3GXsf+UB//orv/GVP/69v+zJ/+DDP/45v/GY/+OCf+EDpy93v/bff/14P+fCYquyP/erf/moLO8wf+aFf+JGf/Mjv+7SP+nKv+1If/xxf/pwv+2XMTW4/+4M//WlP/Ubv+wXP+BGP/Jff+dNv+sG/+PJ7nN2wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAHAP8ALAAAAAAgACAAAAj/ALcIHEiwoMGDCBMqXJjQwYwNgHAA2jDDAcOEGFhE2Yijo0cWGC4WvJJiQxM5cvKECZPCRkcbV0QKnKCDxYwjR4I8+TJgQJ6WDRpMEIkiBYsjV+zY8VIjSAsrPMPYaKAGxcUmSpxcwTAmjQOmaPa0iDpVCcMHHJocsTNmC4wqSdrwQIPmiZUmOrp0GbFQAocVR6CkgTEGro8TGjQ4ZbHByI0PC990adKDjQMMGOooaHOCx9w9dy/ceKNwjIUuSoDU8JIkiYLDnnloAEFGzpwuFmBgtFBmzoclNXwI7xx7NpkVc8pYCImwAO85K1oEiU1dNm3kynUjpPDihYohVmaU/qjuucQMK0NUWHixEMuLKSY+AAmigXwQIB9MTHmBZeGBFyT8sQIZ4o0Xm3nH/UHCCwssVIAYVMAn3xIlGFjCEvjpR8ULBTB0ABVmYDGEBE6UkMWJeiwhwRBYmEFFgwxR0AEAcPyRgx9ZXKFjFj3c8QccAERAgUgUMAFAAlrEMYIDTI4QBx+BBAmGTFuAYUAIHRDwwA4w7PAAAR30wcSUVLrlwg9CIBAAEjGsAQEXApBZ5hYVoKkmm25y4cacA7kQQwYMyMAAER4UIYIAfAokhZ1rtvkmDYlu4SegghJqKKKJLppmo3lCGukPlA5aKBeRCiTIpnhCIEWpAxHRKB6sA7IaEAA7"; img.onload = function(){ cxt1.drawImage(img,0,0); var imageData = cxt1.getImageData(0,0,img.width,img.height); setImage(imageData); } } var drawing2 = document.getElementById('drawing2'); if(drawing2.getContext){ var cxt2 = drawing2.getContext('2d'); function setImage(imageData){ var data = imageData.data; var len = data.length; for(var i = 0; i < len; i++){ if((i+1)%4==0){ data[i] = 255; }else{ data[i] = 255- data[i]; } } imageData.data = data; cxt2.putImageData(imageData,0,0); } } </script>
模式其實就是重複的圖像,能夠用來填充或描邊圖形
【createPattern()】
調用createPattern()方法建立模式,該方法接收兩個參數:一個<img>或<canvas>或<video>元素和一個表示如何重複的字符串。其中,第二個參數的值與CSS的background-repeat的屬性值相同,包括repeat\repeat-x\repeat-y\no-repeat
[注意]模式與漸變同樣,都是從畫布的原點(0,0)開始的,將填充樣式fillStyle設置爲模式對象,只表示在某個特定的區域內顯示重複的圖像,而不是要從某個位置開始繪製重複的圖像
var img = document.images[0]; img.onload = function(){ if(drawing.getContext){ var context = drawing.getContext('2d'); var pattern = context.createPattern(img,'repeat'); context.fillStyle = pattern; context.fillRect(0,0,500,500); } }
在一個區域裏取一個點,並向外引一條射線。這條射線會與組成這個區域的線條相交。而區域的線條都是有方向的,假設射線與一個方向的線條相交爲+1,與另外一個方向相交爲-1。若是射線與全部方向的相交的結果的和爲非0,則射線的起始點處於多邊形的裏面;若是爲0,則處於多邊形的外面
通常地,不多會繪製上圖中無規律的圖形。可是,在繪製空心圖形,如圓環等,非零環繞原則也是適用的
<canvas id="canvas" height="80" width="80">當前瀏覽器不支持canvas,請更換瀏覽器後再試</canvas> <script> var canvas = document.getElementById('canvas'); var H = 80,W=80; if(canvas.getContext){ var cxt = canvas.getContext('2d'); cxt.beginPath(); cxt.arc(40,40,30,0,Math.PI*2,false); cxt.arc(40,40,15,0,Math.PI*2,true); cxt.fillStyle = '#058'; cxt.shadowColor = 'gray'; cxt.shadowOffsetX = 3; cxt.shadowOffsetY = 3; cxt.shadowBlur = 3; cxt.fill(); } </script>
接下來,利用非零環繞原則和陰影來繪製一個鏤空的剪紙效果
<canvas id="drawing" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var canvas = document.getElementById('drawing'); if(drawing.getContext){ var cxt = drawing.getContext('2d'); cxt.fillStyle = "#FFF"; cxt.fillRect(0,0,300,150); cxt.beginPath(); cxt.rect(0,0,300,150); drawPathRect(cxt, 10, 10, 200, 80); drawPathTriangle(cxt, 10, 140,30, 100, 50, 140); cxt.arc(150, 120, 20, 0, Math.PI * 2, true); cxt.closePath(); cxt.fillStyle = "#058"; cxt.shadowColor = "gray"; cxt.shadowOffsetX = 10; cxt.shadowOffsetY = 10; cxt.shadowBlur = 10; cxt.fill(); //逆時針繪製矩形 function drawPathRect(cxt, x, y, w, h){ cxt.moveTo(x, y); cxt.lineTo(x, y + h); cxt.lineTo(x + w, y + h); cxt.lineTo(x + w, y); cxt.lineTo(x, y); } //逆時針繪製三角形 function drawPathTriangle(cxt, x1, y1, x2, y2, x3, y3){ cxt.moveTo(x1,y1); cxt.lineTo(x3,y3); cxt.lineTo(x2,y2); cxt.lineTo(x1,y1); } } </script>
canvas有一個交互性很強的API——isPointInPath(),
isPointInPath(x,y)方法用來檢測指定點是在路徑內仍是在路徑外。若是在當前路徑中,則返回true,不然返回false
下面是一個canvas交互的實例,點擊藍色的小圓,可變成紅色
<canvas id="drawing" style="border:1px solid black"> <p>The canvas element is not supported!</p> </canvas> <script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var cxt = drawing.getContext('2d'); var H=150,W=300; var balls = []; var NUM = 5; for(var i = 0; i < NUM; i++){ var tempBall = { x:Math.floor(Math.random()*W), y:Math.floor(Math.random()*H), r:Math.floor(Math.random()*40+20) } balls[i] = tempBall; draw(); drawing.addEventListener('click',fnDetect); } function draw(){ for(var i = 0; i < balls.length; i++){ cxt.beginPath(); cxt.arc(balls[i].x,balls[i].y,balls[i].r,0,Math.PI*2); cxt.fillStyle = '#058'; cxt.fill(); } } function fnDetect(e){ e = e || event; var x = e.clientX - drawing.getBoundingClientRect().left; var y = e.clientY - drawing.getBoundingClientRect().top; for(var i = 0; i < balls.length; i++){ cxt.beginPath(); cxt.arc(balls[i].x,balls[i].y,balls[i].r,0,Math.PI*2); if(cxt.isPointInPath(x,y)){ cxt.fillStyle = 'red'; cxt.fill(); } } } } </script>