DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> HTML基礎知識 >> HTML5教程 >> canvas API ,通俗的canvas基礎知識(四)
canvas API ,通俗的canvas基礎知識(四)
編輯:HTML5教程     

今天要講的內容是canvas的轉換功能,前面的內容沒用看的同學可以出門右轉,先看看前面的基礎知識,廢話不多說,開始進入正題吧!

何為轉換功能?熟悉css3的同學都知道,css3裡面有transform,translate,scale,rotate,animation等等,這就是css3的轉換功能,同樣,canvas也支持,但是只是支持部分,那我們來看看,都支持哪些,和css3相比,有什麼區別?

1、scale

scale(scaleWidth,scaleHeight)  縮放當前繪圖

參數:scaleWidth 表示縮放當前繪圖的寬度,取值如0.5 = 50% ,1 = 100% , 2 = 200%以此類推 ; scaleHeight 表示縮放當前繪圖的高度,取值如0.5 = 50% ,1 = 100% , 2 = 200%以此類推 

我們可以先看看css3是什麼表現:

css3 scale(sx,sy)  sx,sy分別表示向橫坐標和縱坐標的縮放向量,取值如0.5 = 50% ,1 = 100% , 2 = 200%以此類推 基本上跟canvas是一樣,只是說法不一樣而已,那既然用法是一樣的,我們就來試一下:

ctx.strokeStyle = 'red';
ctx.strokeRect(5,5,50,50);
ctx.scale(2,2);
ctx.strokeRect(5,5,50,50);
ctx.scale(2,2);
ctx.strokeRect(5,5,50,50);

咦,你會看到一個很奇怪的現象,它不是從定位的原點開始縮放的,而是偏移了原點,實際上是不僅僅是圖形縮放了,就連圖形的邊距也縮放了,且縮放的倍數與圖形倍數一致,我們看一下css3的scale會有什麼樣的表現:

.box{
    width:50px;
    height:50px;
    border:1px solid #000;
    margin:20px;
}
.box:hover{
    -webkit-transform:scale(2,2);
}

    

css3的表現是以圖形的中心點為原點,然後向四周縮放,由此我們得到canvas 的scale與css3的scale的不同點之一;

不同點之二就是,css3的scale,如果x,y軸的縮放倍數一樣的話,是可以縮寫成一個參數的,如:

.box:hover{
    -webkit-transform:scale(2);
}

 

效果是一樣一樣的,但是canvas的2個參數即使縮放倍數一樣,也是不能進行簡寫的,必須2個參數都寫才能運行;

說到這裡,我想起來在canvasAPI開篇的時候留了一個懸念,就是canvas畫布的寬高設置必須在canvas標簽屬性上設置,而不能在css裡設置,這是為什麼呢?下面大家來看一個例子:

第一組,我們用css來定義canvas的寬高,標簽屬性上不設樣式:

canvas{ background:#fff; width:300px; height:300px; }

 

ctx.strokeStyle = 'red';
ctx.strokeRect(5,5,50,50);

 

咦,這是什麼鬼?

第二組,我們用canvas標簽屬性來設寬高,不用css設置:

<canvas width="300" height="300" id="canvas">
        <span>親,您的浏覽器不支持canvas,換個浏覽器試試吧!</span>
</canvas>

明顯是在標簽屬性上設置的寬高是正常的,為什麼css設置寬高會出現這種詭異的狀況,原因是canvas本身是有默認寬高的(寬300,高150),如果在css中設置寬高,會讓canvas認為,現在canvas的寬度倍自動縮放了,縮放比例為css設置的寬度/300,高的也一樣,那麼就可以理解了,現在css設置的寬度是300,高的是300,那麼就會縮放寬=300/300,高縮放=300/150,高的自然就被拉高了一倍,所以這才是必須在canvas的屬性上設置寬高的原因

我們回到scale,我們來給給一個動態圖,來看看scale的變化過程:

var timer = null;
ctx.strokeStyle = 'red';
timer = setInterval(function(){
    ctx.beginPath();
    ctx.scale(1.5,1.5);
    ctx.strokeRect(5,5,50,50);
    ctx.closePath();
},500)

可以從這個gif圖中可以看出,scale的變化是在前面的一次繪圖的基礎上再次縮放,然後再縮放,有人說,你這個定時器是本來就是原來的基礎上再縮放一次,理所應該就是這樣,但是這個效果不好看,能不能我設一個參數,然後讓它累加,慢慢的縮放,且只有一個圖形呢?

嗯,這裡就需要解釋一個方法叫clearRect(),表示在指定的范圍內清除樣式,這裡如果需要只有一個圖形,那麼就必須在下一次繪制圖形之前清除掉前面的一次繪圖,因為中間的時間極短,就感覺是連續的,我們先介紹一下這個clearRect()方法吧:

clearRect(x,y,w,h) 參數:w,y表示需要清除的矩形的左上角坐標,w,h表示需要清除的矩形的寬高

從參數可以看出,它是可以清除局部區域的像素的,如果區域設為畫布,則是清除整個畫布了,好了,讓我們一起來寫一下你想要的那種效果:

var timer = null;
        var num = 1;
        ctx.strokeStyle = 'red';
        timer = setInterval(function(){
            
            if(parseInt(num) >=5){
                clearInterval(timer);
                num =5;    
            }else{
                num +=0.1;
            }
            ctx.clearRect(0,0,canvas.clientWidth,canvas.clientHeight);
            ctx.save();
            ctx.beginPath();
            ctx.scale(num,num);
            ctx.strokeRect(5,5,50,50);
            ctx.closePath();
            ctx.restore();
        },500)

看上圖,現在就可以安安靜靜看它是怎麼縮放的了,邊距和圖形一起縮放,比例也是一樣的,這裡的效果之所以沒有和上面的gif圖一樣,在上一次縮放的基礎上縮放,是因為這一對活寶:save()和restore(),這對活寶上一篇已經講過了,如果還是不熟悉的同學出門右轉,找到API的第3篇,這裡的這一對主要功能是保存當前的路徑,不被其他的路徑污染,這對活寶和clearRect()在做運動的時候是非常有用的,這裡終點提示一下!

2、rotate

rotate(angle)  旋轉當前繪圖  參數:angle表示旋轉角度,這裡需要填寫弧度(弧度和角度的關系,在前面就已經講過了,不熟悉的同學可以找到API的第2篇)

同樣我們看一下css3 rotate的表現:

.box{
    width:50px;
    height:50px;
    border:1px solid #000;
    margin:20px;
}
.box:hover{
    -webkit-transform:rotate(30deg);
}

可以看到css3的旋轉是以中心為原點進行旋轉,切接受的參數直接就是角度,而不是弧度,那canvas的rotate的表現是什麼呢?

ctx.fillStyle = 'red';
ctx.fillRect(0,0,150,50);
ctx.beginPath();
ctx.rotate(30*Math.PI/180);
ctx.strokeRect(0,0,150,50);
ctx.closePath();

   紅色為初始圖形,黑色為旋轉圖形,這是將圖形坐標設置畫布左上角的地方的

ctx.fillStyle = 'red';
ctx.fillRect(50,50,150,50);
ctx.beginPath();
ctx.rotate(30*Math.PI/180);
ctx.strokeRect(50,50,150,50);
ctx.closePath();

   圖形坐標設置50,50處

ctx.fillStyle = 'red';
ctx.fillRect(100,100,150,50);
ctx.beginPath();
ctx.rotate(30*Math.PI/180);
ctx.strokeRect(100,100,150,50);
ctx.closePath();

圖形坐標設在100,100處

從這個3組效果中,我們可以得出這樣的結論:

1、canvas的旋轉原點並不是以自身的中心為原點,而是以畫布的左上角為原點,3張圖的比較可以看出來

2、圖形的旋轉原點也不是其自身的中心,而是其左上角為原點

這裡說了2個原點,可能不好理解哈,幾個例子,比如地球,它即繞太陽轉,自己本身也轉,那麼它讓太陽轉就是我們說的第一點,圖形繞畫布旋轉,准確的來說,也是圖形的左上角繞畫布左上角旋轉,太陽的自轉就是我們說的第2點,它自己本身的旋轉,只不過canvas圖形中的自轉不是以中心為原點的旋轉,其中心在左上角,這應該就明白看吧!

3、translate

translate(x,y)  重新映射畫布上的 (0,0) 位置,這怎麼理解?通俗的將,就是重新定義坐標原點,默認原點是(0,0),用此方法會將原點改成(x,y)

參數:x 添加到水平坐標(x)上的值  y添加到垂直坐標(y)上的值

定義不好理解,那我們就用例子來理解:

ctx.fillRect(10,10,100,100);
//設置新原點
ctx.translate(110,110);
ctx.fillRect(10,10,100,100);

 

首先我們畫了一個100*100的矩形,圖形坐標(10,10),因為默認原點是畫布左上角,所以此圖形在距離左上角(10,10)的位置,理論上說,我們再畫一個一模一樣的矩形,坐標也一樣,2圖形是會覆蓋的,但是我們現在重新設置原點(110,110),剛好在第一個圖形的右下角,這樣方便觀察,然後再畫一個坐標和大小一模一樣的矩形,我們來看看效果:

第二個矩形就剛好是以(110,110)為新的原點,然後距離新原點(10,10)的距離畫了一個矩形,恩,這就是translate的作用

css3也是有translate的,我們不妨也來對比一下,下面我寫一個css3的translate的例子:

.box{
    width:150px;
    height:150px;
    border:1px solid #000;
    margin:20px;
}
.box:hover{
    -webkit-transform: translate(100px,0);
}

從gif圖可以看出,css3的translate是以自身中心為原點進行平移,但是不會改變原點坐標,所以,canvas的translate跟css3的translate又不一樣

 

4、transform

transform(a,b,c,d,e,f)   替換當前的變換矩陣

參數:

a:水平縮放繪圖

b:水平傾斜繪圖

c:垂直傾斜繪圖

d:垂直縮放繪圖

e:水平移動繪圖

f:垂直移動繪圖

參數很多,但是看這參數的解釋,還是很好理解,我們都知道css3的transform是一個集合,其中包含:scale,rotate,translate,skew和matrix,並且其中的matrix(矩陣)是可以轉換成前面的任何效果的,換句話說,就是matrix(矩陣)可以包含前面的任何效果,包括自身,而canvas中的transform就是扮演css3的matrix的角色,只是跟css3的效果不一樣而已,前面已經對比過了,具體的原理我們在這裡就不說了,如果不清楚的,可以看一下css3的matrix是什麼個原理,canvas的transform跟他的原理差不多!css3 matrix看這裡

 

scale轉成transform公式可得:

context.scale(sx, sy)

縮放我直接用公式來解釋:

x’=sx*x

y’=sy*y

(其中,sx 和sy分別表示在x軸和y軸上的縮放倍數,x和y默認為1)

matrix(sx*x,0,0,sy*y,0,0) --> context.transform(sx*x,0,0,sy*y,0,0) -->context.transform(sx,0,0,sy,0,0)

ctx.fillRect(10,10,100,100);
//縮放
ctx.transform(2,0,0,2,0,0);
ctx.fillStyle="red";
ctx.fillRect(10,10,100,100);

 

rotate轉化成transform

rotate(a*Math.PI/180)

公式推導就不推了,直接拿過來了

 context.transform(cos(a),sin(a),-sin(a),cos(a),0,0)  (a為角度)

--> context.transform(Math.cos(a*Math.PI/180),Math.sin(a*Math.PI/180),-Math.sin(a*Math.PI/180),Math.cos(a*Math.PI/180),0,0)

ctx.fillRect(10,10,100,100);
//旋轉
ctx.transform(Math.cos(30*Math.PI/180),Math.sin(30*Math.PI/180),-Math.sin(30*Math.PI/180),Math.cos(30*Math.PI/180),0,0);
ctx.fillStyle="red";
ctx.fillRect(10,10,100,100);

 

translate轉化成transform

translate(tx,ty)

context.transform(1,0,0,1,tx,ty)

ctx.fillRect(10,10,100,100);
//平移
ctx.transform(1,0,0,1,110,110);
ctx.fillStyle="red";
ctx.fillRect(10,10,100,100);

 

skew轉化成transform

雖然canvas沒有skew方法,但是transform依然可以做出來

context.transform(1,tan(ay),tan(ax),1,0,0) (ax,ay表示x方向,y方向的傾斜角度)

-->context.transform(1,Math.tan(ay*Math.PI/180),Math.tan(ax*Math.PI/180),1,0,0)

ctx.fillRect(10,10,100,100);
//傾斜
ctx.transform(1,Math.tan(30*Math.PI/180),Math.tan(30*Math.PI/180),1,0,0)
ctx.fillStyle="red";
ctx.fillRect(10,10,100,100);

那麼,如果我想實現平移,旋轉,傾斜加放大呢,怎麼做?那就分開寫呗:

ctx.fillRect(10,10,100,100);
//綜合
ctx.transform(1,0,0,1,110,110);//平移
ctx.transform(Math.cos(10*Math.PI/180),Math.sin(10*Math.PI/180),-Math.sin(10*Math.PI/180),Math.cos(30*Math.PI/180),0,0);//旋轉
ctx.transform(0.5,0,0,0.5,0,0);//縮放
ctx.transform(1,Math.tan(30*Math.PI/180),Math.tan(30*Math.PI/180),1,0,0);//傾斜
ctx.fillStyle="red";
ctx.fillRect(10,10,100,100);

 

 

5、setTransform

setTransform(a,b,c,d,e,f)  當前的變換矩陣重置為單位矩陣,用法與transform相同

參數:

a:水平縮放繪圖

b:水平傾斜繪圖

c:垂直傾斜繪圖

d:垂直縮放繪圖

e:水平移動繪圖

f:垂直移動繪圖

怎麼理解這個方法呢?

當我們用transform時,前面的變換方法會影響到後面的變換方法,我們俗稱污染,比如:

//縮放
ctx.transform(2,0,0,2,0,0);
ctx.fillStyle="red";
ctx.fillRect(10,10,100,100);
ctx.beginPath();
//旋轉
ctx.transform(Math.cos(30*Math.PI/180),Math.sin(30*Math.PI/180),-Math.sin(30*Math.PI/180),Math.cos(30*Math.PI/180),0,0);
ctx.fillStyle="green";
ctx.fillRect(10,10,100,100);

 

前面的一個圖形我想讓它放大2倍,後面的我不想讓它放大,而是想讓它旋轉30度,結果:

後面的圖形也放大了2倍,這不是我們想要的結果,有人會說,我用save()和restore()不就可以了嗎?

//縮放
ctx.save();
ctx.transform(2,0,0,2,0,0);
ctx.fillStyle="red";
ctx.fillRect(10,10,100,100);
ctx.restore();
ctx.beginPath();
//旋轉
ctx.transform(Math.cos(30*Math.PI/180),Math.sin(30*Math.PI/180),-Math.sin(30*Math.PI/180),Math.cos(30*Math.PI/180),0,0);
ctx.fillStyle="green";
ctx.fillRect(10,10,100,100);

 

如果你用這2個方法,我就不得不給你贊一個,說明前面的你看進去了

但是我想說的是,我們有更好的方法,就是我們現在要講的這個--setTransform

//縮放
ctx.setTransform(2,0,0,2,0,0);
ctx.fillStyle="red";
ctx.fillRect(10,10,100,100);
ctx.beginPath();
//旋轉
ctx.setTransform(Math.cos(30*Math.PI/180),Math.sin(30*Math.PI/180),-Math.sin(30*Math.PI/180),Math.cos(30*Math.PI/180),0,0);
ctx.fillStyle="green";
ctx.fillRect(10,10,100,100);

效果跟上面的一樣,官方解釋是該變換只會影響 setTransform() 方法調用之後的繪圖,當然,如果你把transform和setTransform一起混用,那也是會污染的:

//縮放
ctx.setTransform(2,0,0,2,0,0);
ctx.fillStyle="red";
ctx.fillRect(10,10,100,100);
ctx.beginPath();
//旋轉
ctx.transform(Math.cos(30*Math.PI/180),Math.sin(30*Math.PI/180),-Math.sin(30*Math.PI/180),Math.cos(30*Math.PI/180),0,0);
ctx.fillStyle="green";
ctx.fillRect(10,10,100,100);

 

要是把這2方法調個個看看:

//縮放
ctx.transform(2,0,0,2,0,0);
ctx.fillStyle="red";
ctx.fillRect(10,10,100,100);
ctx.beginPath();
//旋轉        
ctx.setTransform(Math.cos(30*Math.PI/180),Math.sin(30*Math.PI/180),-Math.sin(30*Math.PI/180),Math.cos(30*Math.PI/180),0,0); ctx.fillStyle="green"; ctx.fillRect(10,10,100,100);

 

看看,效果就又不一樣了,所以,在用這些變換方法的時候,必須要弄清楚他們的作用范圍和順序,才能做出我們想要的效果,也不會污染其他的效果,這點,需謹記了!

好了,變換部分就講完了,感謝大家的關注,如有將的不對的地方,希望能踴躍指正,不甚感謝!

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved