1. 程式人生 > >第156天:canvas(三)

第156天:canvas(三)

win 位圖 中心 style 展示 ear cloud 方法 縮小

一、變形

1.1 translate

translate(x, y)

? 用來移動 canvas 原點到指定的位置

? translate方法接受兩個參數。x 是左右偏移量,y 是上下偏移量,如右圖所示。

在做變形之前先保存狀態是一個良好的習慣。大多數情況下,調用 restore 方法比手動恢復原先的狀態要簡單得多。又如果你是在一個循環中做位移但沒有保存和恢復canvas 的狀態,很可能到最後會發現怎麽有些東西不見了,那是因為它很可能已經超出 canvas 範圍以外了。

? 註意:translate移動的是canvas的坐標原點。(坐標變換)

技術分享圖片

 1 var ctx;
 2 function
draw(){ 3 var canvas = document.getElementById(‘tutorial1‘); 4 if (!canvas.getContext) return; 5 var ctx = canvas.getContext("2d"); 6 ctx.save(); //保存坐原點平移之前的狀態 7 ctx.translate(100, 100); 8 ctx.strokeRect(0, 0, 100, 100) 9 ctx.restore(); //恢復到最初狀態 10 ctx.translate(220, 220); 11
ctx.fillRect(0, 0, 100, 100) 12 } 13 draw();

技術分享圖片

1.2 rotate

rotate(angle)

? 旋轉坐標軸。

? 這個方法只接受一個參數:旋轉的角度(angle),它是順時針方向的,以弧度為單位的值。

? 旋轉的中心是坐標原點。

技術分享圖片

 1 var ctx;
 2 function draw(){
 3   var canvas = document.getElementById(‘tutorial1‘);
 4   if (!canvas.getContext) return;
 5   var ctx = canvas.getContext("2d");
6 7 ctx.fillStyle = "red"; 8 ctx.save(); 9 10 ctx.translate(100, 100); 11 ctx.rotate(Math.PI / 180 * 45); 12 ctx.fillStyle = "blue"; 13 ctx.fillRect(0, 0, 100, 100); 14 ctx.restore(); 15 16 ctx.save(); 17 ctx.translate(0, 0); 18 ctx.fillRect(0, 0, 50, 50) 19 ctx.restore(); 20 } 21 draw();

技術分享圖片

1.3 scale

scale(x, y)

? 我們用它來增減圖形在 canvas 中的像素數目,對形狀,位圖進行縮小或者放大。

? scale方法接受兩個參數。x,y分別是橫軸和縱軸的縮放因子,它們都必須是正值。值比 1.0 小表示縮 小,比 1.0 大則表示放大,值為 1.0 時什麽效果都沒有。

? 默認情況下,canvas 的 1 單位就是 1 個像素。舉例說,如果我們設置縮放因子是 0.5,1 個單位就變成對應 0.5 個像素,這樣繪制出來的形狀就會是原先的一半。同理,設置為 2.0 時,1 個單位就對應變成了 2 像素,繪制的結果就是圖形放大了 2 倍。

1.4 transform(變形矩陣)

transform(a, b, c, d, e, f)

技術分享圖片

a (m11)

? Horizontal scaling.

b (m12)

? Horizontal skewing.

c (m21)

? Vertical skewing.

d (m22)

? Vertical scaling.

e (dx)

? Horizontal moving.

f (dy)

? Vertical moving.

1 var ctx;
2 function draw(){
3     var canvas = document.getElementById(‘tutorial1‘);
4     if (!canvas.getContext) return;
5     var ctx = canvas.getContext("2d");
6     ctx.transform(1, 1, 0, 1, 0, 0);
7     ctx.fillRect(0, 0, 100, 100);
8 }
9 draw();

  技術分享圖片

二、合成

? 在前面的所有例子中、,我們總是將一個圖形畫在另一個之上,對於其他更多的情況,僅僅這樣是遠遠不夠的。比如,對合成的圖形來說,繪制順序會有限制。不過,我們可以利用 globalCompositeOperation 屬性來改變這種狀況。

globalCompositeOperation = type

 1   var ctx;
 2     function draw(){
 3         var canvas = document.getElementById(‘tutorial1‘);
 4         if (!canvas.getContext) return;
 5         var ctx = canvas.getContext("2d");
 6 
 7         ctx.fillStyle = "blue";
 8         ctx.fillRect(0, 0, 200, 200);
 9 
10         ctx.globalCompositeOperation = "source-over"; //全局合成操作
11         ctx.fillStyle = "red";
12         ctx.fillRect(100, 100, 200, 200);
13     }
14     draw();
15 
16 </script>

註:下面的展示中,藍色是原有的,紅色是新的。

type `是下面 13 種字符串值之一:

1. source-over(default)

這是默認設置,新圖像會覆蓋在原有圖像。

技術分享圖片

2. source-in

僅僅會出現新圖像與原來圖像重疊的部分,其他區域都變成透明的。(包括其他的老圖像區域也會透明)

技術分享圖片

3. source-out

僅僅顯示新圖像與老圖像沒有重疊的部分,其余部分全部透明。(老圖像也不顯示)

技術分享圖片

4. source-atop

新圖像僅僅顯示與老圖像重疊區域。老圖像仍然可以顯示。

技術分享圖片

5. destination-over

新圖像會在老圖像的下面

技術分享圖片

6. destination-in

僅僅新老圖像重疊部分的老圖像被顯示,其他區域全部透明。

技術分享圖片

7. destination-out

僅僅老圖像與新圖像沒有重疊的部分。 註意顯示的是老圖像的部分區域。

技術分享圖片

8. destination-atop

老圖像僅僅僅僅顯示重疊部分,新圖像會顯示在老圖像的下面。

技術分享圖片

9. lighter

新老圖像都顯示,但是重疊區域的顏色做加處理

技術分享圖片

10. darken

保留重疊部分最黑的像素。(每個顏色位進行比較,得到最小的)

blue: #0000ff

red: #ff0000

所以重疊部分的顏色:#000000

技術分享圖片

11. lighten

保證重疊部分最量的像素。(每個顏色位進行比較,得到最大的)

blue: #0000ff

red: #ff0000

所以重疊部分的顏色:#ff00ff

技術分享圖片

12. xor

重疊部分會變成透明

技術分享圖片

13. copy

只有新圖像會被保留,其余的全部被清除(邊透明)

技術分享圖片

三、裁剪路徑

clip()

? 把已經創建的路徑轉換成裁剪路徑。

? 裁剪路徑的作用是遮罩。只顯示裁剪路徑內的區域,裁剪路徑外的區域會被隱藏。

? 註意:clip()只能遮罩在這個方法調用之後繪制的圖像,如果是clip()方法調用之前繪制的圖像,則無法實現遮罩。

技術分享圖片

 1 var ctx;
 2 function draw(){
 3     var canvas = document.getElementById(‘tutorial1‘);
 4     if (!canvas.getContext) return;
 5     var ctx = canvas.getContext("2d");
 6 
 7     ctx.beginPath();
 8     ctx.arc(20,20, 100, 0, Math.PI * 2);
 9     ctx.clip();
10 
11     ctx.fillStyle = "pink";
12     ctx.fillRect(20, 20, 100,100);
13 }
14 draw();

四、動畫

動畫的基本步驟

  1. 清空canvas

    再繪制每一幀動畫之前,需要清空所有。清空所有最簡單的做法就是clearRect()方法

  2. 保存canvas狀態

    如果在繪制的過程中會更改canvas的狀態(顏色、移動了坐標原點等),又在繪制每一幀時都是原始狀態的話,則最好保存下canvas的狀態

  3. 繪制動畫圖形

    這一步才是真正的繪制動畫幀

  4. 恢復canvas狀態

    如果你前面保存了canvas狀態,則應該在繪制完成一幀之後恢復canvas狀態。

控制動畫

我們可用通過canvas的方法或者自定義的方法把圖像會知道到canvas上。正常情況,我們能看到繪制的結果是在腳本執行結束之後。例如,我們不可能在一個 for 循環內部完成動畫。

也就是,為了執行動畫,我們需要一些可以定時執行重繪的方法。

一般用到下面三個方法:

  1. setInterval()
  2. setTimeout()
  3. requestAnimationFrame()

案例1:太陽系

 1 let sun;
 2 let earth;
 3 let moon;
 4 let ctx;
 5 function init(){
 6     sun = new Image();
 7     earth = new Image();
 8     moon = new Image();
 9     sun.src = "sun.png";
10     earth.src = "earth.png";
11     moon.src = "moon.png";
12 
13     let canvas = document.querySelector("#solar");
14     ctx = canvas.getContext("2d");
15 
16     sun.onload = function (){
17         draw()
18     }
19 
20 }
21 init();
22 function draw(){
23     ctx.clearRect(0, 0, 300, 300); //清空所有的內容
24     /*繪制 太陽*/
25     ctx.drawImage(sun, 0, 0, 300, 300);
26 
27     ctx.save();
28     ctx.translate(150, 150);
29 
30     //繪制earth軌道
31     ctx.beginPath();
32     ctx.strokeStyle = "rgba(255,255,0,0.5)";
33     ctx.arc(0, 0, 100, 0, 2 * Math.PI)
34     ctx.stroke()
35 
36     let time = new Date();
37     //繪制地球
38     ctx.rotate(2 * Math.PI / 60 * time.getSeconds() + 2 * Math.PI / 60000 * time.getMilliseconds())
39     ctx.translate(100, 0);
40     ctx.drawImage(earth, -12, -12)
41 
42     //繪制月球軌道
43     ctx.beginPath();
44     ctx.strokeStyle = "rgba(255,255,255,.3)";
45     ctx.arc(0, 0, 40, 0, 2 * Math.PI);
46     ctx.stroke();
47 
48     //繪制月球
49     ctx.rotate(2 * Math.PI / 6 * time.getSeconds() + 2 * Math.PI / 6000 * time.getMilliseconds());
50     ctx.translate(40, 0);
51     ctx.drawImage(moon, -3.5, -3.5);
52     ctx.restore();
53 
54     requestAnimationFrame(draw);
55 }

技術分享圖片

案例2:模擬時鐘

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <title>Title</title>
  6     <style>
  7         body {
  8             padding: 0;
  9             margin: 0;
 10             background-color: rgba(0, 0, 0, 0.1)
 11         }
 12 
 13         canvas {
 14             display: block;
 15             margin: 200px auto;
 16         }
 17     </style>
 18 </head>
 19 <body>
 20 <canvas id="solar" width="300" height="300"></canvas>
 21 <script>
 22     init();
 23 
 24     function init(){
 25         let canvas = document.querySelector("#solar");
 26         let ctx = canvas.getContext("2d");
 27         draw(ctx);
 28     }
 29 
 30     function draw(ctx){
 31         requestAnimationFrame(function step(){
 32             drawDial(ctx); //繪制表盤
 33             drawAllHands(ctx); //繪制時分秒針
 34             requestAnimationFrame(step);
 35         });
 36     }
 37     /*繪制時分秒針*/
 38     function drawAllHands(ctx){
 39         let time = new Date();
 40 
 41         let s = time.getSeconds();
 42         let m = time.getMinutes();
 43         let h = time.getHours();
 44 
 45         let pi = Math.PI;
 46         let secondAngle = pi / 180 * 6 * s;  //計算出來s針的弧度
 47         let minuteAngle = pi / 180 * 6 * m + secondAngle / 60;  //計算出來分針的弧度
 48         let hourAngle = pi / 180 * 30 * h + minuteAngle / 12;  //計算出來時針的弧度
 49 
 50         drawHand(hourAngle, 60, 6, "red", ctx);  //繪制時針
 51         drawHand(minuteAngle, 106, 4, "green", ctx);  //繪制分針
 52         drawHand(secondAngle, 129, 2, "blue", ctx);  //繪制秒針
 53     }
 54     /*繪制時針、或分針、或秒針
 55      * 參數1:要繪制的針的角度
 56      * 參數2:要繪制的針的長度
 57      * 參數3:要繪制的針的寬度
 58      * 參數4:要繪制的針的顏色
 59      * 參數4:ctx
 60      * */
 61     function drawHand(angle, len, width, color, ctx){
 62         ctx.save();
 63         ctx.translate(150, 150); //把坐標軸的遠點平移到原來的中心
 64         ctx.rotate(-Math.PI / 2 + angle);  //旋轉坐標軸。 x軸就是針的角度
 65         ctx.beginPath();
 66         ctx.moveTo(-4, 0);
 67         ctx.lineTo(len, 0);  // 沿著x軸繪制針
 68         ctx.lineWidth = width;
 69         ctx.strokeStyle = color;
 70         ctx.lineCap = "round";
 71         ctx.stroke();
 72         ctx.closePath();
 73         ctx.restore();
 74     }
 75 
 76     /*繪制表盤*/
 77     function drawDial(ctx){
 78         let pi = Math.PI;
 79 
 80         ctx.clearRect(0, 0, 300, 300); //清除所有內容
 81         ctx.save();
 82 
 83         ctx.translate(150, 150); //一定坐標原點到原來的中心
 84         ctx.beginPath();
 85         ctx.arc(0, 0, 148, 0, 2 * pi); //繪制圓周
 86         ctx.stroke();
 87         ctx.closePath();
 88 
 89         for (let i = 0; i < 60; i++){//繪制刻度。
 90             ctx.save();
 91             ctx.rotate(-pi / 2 + i * pi / 30);  //旋轉坐標軸。坐標軸x的正方形從 向上開始算起
 92             ctx.beginPath();
 93             ctx.moveTo(110, 0);
 94             ctx.lineTo(140, 0);
 95             ctx.lineWidth = i % 5 ? 2 : 4;
 96             ctx.strokeStyle = i % 5 ? "blue" : "red";
 97             ctx.stroke();
 98             ctx.closePath();
 99             ctx.restore();
100         }
101         ctx.restore();
102     }
103 </script>
104 </body>
105 </html>

技術分享圖片

第156天:canvas(三)