1. 程式人生 > >canvas圖形處理和進階用法

canvas圖形處理和進階用法

前面的話

  上一篇部落格介紹了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()前,先來介紹下什麼是變換矩陣

  以上是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 = 'lightblue';
    context.fillRect(120,10,100,100);
    context.globalAlpha = 1;
}
</script>

圖形合成

  兩個圖形重合的時候,就涉及到了對這兩個圖形的合成處理

  globalCompositeOperation表示後繪製的圖形怎樣與先繪製的圖形結合,屬性值是字串,可能值如下:

source-over(預設):後繪製的圖形位於先繪製的圖形上方
source-in:後繪製的圖形與先繪製的圖形重疊的部分可見,兩者其他部分完全透明
source-out:後繪製的