Canvas transform淺析

沒有前奏,直接進入主題canvas


transform調用方法:spa

ctx.transform(a,b,c,d,e,f);以下3d

var ctx = document.getElementById("myCanvas").getContext("2d");

//調用
ctx.transform(1,1,-1,1,1,1);

//畫個圓形的路徑
ctx.arc(200,100,25,0,2*Math.PI);

//畫個已填充的矩形
ctx.fillRect(200,150,50,50);

//對路徑描邊
ctx.stroke();

結果以下(只截取了主要部分):code

 使用transform前                       使用transform後orm

咋一看,三個變化:blog

  ①兩個圖形的位置變化了  get

  ②矩形的角度變化了  數學

  ③圖形的大小變化form

 

這裏正好反應了三個屬性:位移旋轉縮放,對應canvas的另外三個API則是translate()、rotate()、scale()。這三個API使用起來直觀,可是卻不能實現某些特定的效果,好比斜切class

var canvas = document.getElementById("myCanvas");

var ctx = canvas.getContext("2d");

//Y軸逐漸拉伸,獲得上圖右邊狀態
ctx.transform(1,Math.PI/18,0,1,0,0);

ctx.fillRect(200,150,50,50);

僅經過原有的三個比較直觀的API並不能實現這個效果,然而或許你在別的地方看過,transform除了能夠實現斜切,也能夠直接替代掉原有的三個API實現它們對應的效果。

(一)位移

用transform來替代就是修改最後兩個參數: ctx.transform(a,b,c,d,e,f) 其中的e和f兩個參數。

(二)旋轉

旋轉呢,就須要配合四個參數來實現了,ctx.transform(a,b,c,d,e,f)其中的abcd四個參數(爲何要四個?先別急,後面再說,總之你記住就行了)。

(三)縮放

縮放僅須要修改前兩個參數便可,ctx.transform(a,b,c,d,e,f);

--------------------------------------

請記住三種狀況對應的相關參數

--------------------------------------

接下來我們說說爲何?

先說簡單的:位移和縮放

對於座標軸上的任意一點 A(x,y)移動到B(x1,y1),則B點的座標值能夠用以下的等式表示:

x1 = x + e

y1 = y + f

同理,對於座標軸上的任意一點 A(x,y)縮放到B(x1,y1),則B點的座標值能夠用以下的等式表示:

x= x * a

y1 = y * d

其中a、d對應transform中的第一和第四個參數,位移的時候e和f表明具體的數值,而縮放的時候a和d則表明了對應x或者h軸上的縮放倍數,因此默認的倍數固然是1(也就是原來的大小),默認的位移固然是0,因此咱們如今知道transform(a,b,c,d,e,f)的參數中a,d爲1,e,f爲0。

接下來看比較複雜的旋轉,如圖:

以點(0,0)爲中心,將點A(100,-100)旋轉45°後求點B的座標軸是多少?(由於在咱們的Canvas中y軸向下纔是正數,因此請換位思考一下)。

若是你會解答這道題,而且能推演一個座標隨角度變化而變化的公式,那麼你很對得起你的幾何老師,惋惜我推不出來,我數學是掛科的!!!因此我就直接把答案擱這兒了:

x = x * cos(弧度)  -  y * sin(弧度);

y = y * cos(弧度)  +  x * sin(弧度); 

弧度 = Math.PI * 180 / 角度

-------------------------------------------------------------

最終的公式

x= ax + cy + e

y1 = bx + dy + f

調用:

ctx.transform(cos(弧度),sin(弧度),-sin(弧度),cos(弧度),0,0);

var canvas = document.getElementById("myCanvas");

var ctx = canvas.getContext("2d");

var deg = Math.PI/180;

//旋轉45°
ctx.transform(Math.cos(deg*45),Math.sin(deg*45),-Math.sin(deg*45),Math.cos(deg*45),0,0);

ctx.fillRect(200,0,50,50);

若是我要旋轉45°而且放大兩倍,位移到(100,100)呢?我並無在別的地方找到答案。我覺得a和d既然是控制縮放的,那麼乘以2如何?

var canvas = document.getElementById("myCanvas");

var ctx = canvas.getContext("2d");

var deg = Math.PI/180;

ctx.fillRect(200,0,50,50);  //第一個正方形(未變形)


ctx.setTransform(1,0,0,1,0,0);
ctx.beginPath();
ctx.transform(2*Math.cos(deg*45),Math.sin(deg*45),-Math.sin(deg*45),2*Math.cos(deg*45),0,0);

ctx.fillRect(200,0,50,50);   //第二個正方形(僅a和d乘以2)

ctx.setTransform(1,0,0,1,0,0);
ctx.beginPath();
ctx.transform(2*Math.cos(deg*45),2*Math.sin(deg*45),-2*Math.sin(deg*45),2*Math.cos(deg*45),0,0);

ctx.fillRect(200,0,50,50);  //第三個正方形(正確的解答)

 

你沒看錯,abcd四個參數所有乘以2纔是正確的(對於數學不太好的我,花了四五個小時也想不通,最後斗膽一試所有乘以2竟然神奇的正確了O(∩_∩)O~~)

與下面兩種方法獲得相同的結果:

//第一:直接使用縮放和旋轉的API
ctx.scale(2,2);
ctx.rotate(Math.PI/180*45);


//第二:使用transform分別替換上面兩種API
var deg = Math.PI/180;
ctx.transform(2,0,0,2,0,0);
ctx.transform(Math.cos(45*deg),Math.sin(45*deg),-Math.sin(45*deg),Math.cos(45*deg),100,100);

總結:

  1. 使用transform徹底能夠替換原有的三個API而且可以作出更多的效果,好比斜切;
  2. transform若是既有平移又有旋轉,則先平移後旋轉(平移和旋轉順序不一樣,結果不一樣,我拿着紙畫了,確實不一樣 - -|);
  3. 若是transform的第二第三個參數中,一個爲零一個非零,則會獲得斜切;第二個參數拉伸Y軸,第三個X軸
  4. 旋轉效果中,若是第二個爲負數,第三個爲正數,則旋轉方向是逆時針的
  5. transform的效果能夠疊加,setTransform則能夠用來將畫布歸回原位後再進行相應的效果處理;
相關文章
相關標籤/搜索