[1]圖形變換 [2]矩陣變換 [3]全局陰影 [4]全局透明 [5]圖形合成 [6]裁剪區域 [7]圖像繪製 [8]使用圖像數據 [9]模式 [10]非零環繞 [11]交互 ...
前面的話
上一篇博客介紹了canvas基礎用法,本文將更進一步,介紹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),並變成了紅色,尺寸不改變
想把矩形再向右移動(50,50),應該使用translate(50,50),還是從最開始算起,使用traslate(100,100)呢?
<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()變換時,已經變換了坐標系的原點,這次是基於新坐標系又一次地變換
【狀態保存】
由於多次運行變換後,坐標系可能就亂套了,所以最好以最初狀態為參照物。這時就需要用到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>
結果如下,左上角頂點的坐標變了,而是線條的粗細也變了
因此,對於縮放變換有兩點問題需要註意:
1、縮放時,圖形左上角坐標的位置也會對應縮放
2、縮放時,圖像線條的粗細也會對應縮放
矩陣變換
上面所說的坐標變換的三種方式——平移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()
的時候,可以在如下幾個情況下使用:
1、使用context.transform (1,0,0,1,dx,dy)
代替context.translate(dx,dy)
2、使用context.transform(sx,0,0,sy,0,0)
代替context.scale(sx, sy)
3、使用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 = 'lightb