沒有前奏,直接進入主題
transform調用方法:
ctx.transform(a,b,c,d,e,f);如下
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();
結果如下(只截取了主要部分):
使用transform前 使用transform后
咋一看,三個變化:
①兩個圖形的位置變化了
②矩形的角度變化了
③圖形的大小變化
這里正好反應了三個屬性:位移、旋轉、縮放,對應canvas的另外三個API則是translate()、rotate()、scale()。這三個API使用起來直觀,但是卻不能實現某些特定的效果,比如斜切。
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點的坐標值可以用如下的等式表示:
x1 = 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軸向下才是正數,所以請換位思考一下)。
如果你會解答這道題,並且能推演一個坐標隨角度變化而變化的公式,那么你很對得起你的幾何老師,可惜我推不出來,我數學是掛科的!!!所以我就直接把答案擱這兒了:
x1 = x * cos(弧度) - y * sin(弧度);
y1 = y * cos(弧度) + x * sin(弧度);
弧度 = Math.PI * 180 / 角度
-------------------------------------------------------------
最終的公式
x1 = 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);
總結:
- 使用transform完全可以替換原有的三個API並且能夠做出更多的效果,比如斜切;
- transform如果既有平移又有旋轉,則先平移后旋轉(平移和旋轉順序不同,結果不同,我拿着紙畫了,確實不同 - -|);
- 如果transform的第二第三個參數中,一個為零一個非零,則會得到斜切;第二個參數拉伸Y軸,第三個X軸
- 旋轉效果中,如果第二個為負數,第三個為正數,則旋轉方向是逆時針的
- transform的效果可以疊加,setTransform則可以用來將畫布歸回原位后再進行相應的效果處理;