1. 程式人生 > >HTML5畫布Canvas線段、矩形、弧形及貝塞爾曲線等簡單圖形繪製

HTML5畫布Canvas線段、矩形、弧形及貝塞爾曲線等簡單圖形繪製

HTML5中最有意思的就是這個canvas了 
通過它我們可以畫自己想要的圖形 
它也是十分重要的技術 
應用於遊戲、圖表等等 
或者繪製各種酷炫的東西 
這裡給大家分享一個網站 傳送門 
裡面都是canvas技術繪製的圖形

畫布建立

canvas是html的一個標籤 
是一個圖形容器 
首先要在html頁面新增一個canvas元素

<canvas id="myCanvas" width=500 height=500></canvas>
  • 1
  • 1

為了後面我們的使用,我添加了id 
width與height定義了畫布的大小 
不過要注意,這與css樣式的width與height不同 
舉個例子,如果我在css樣式中新增這樣的樣式

#myCanvas {
    border: 1px solid black;
    width: 250px;
    height: 250px;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

定義border邊框是為了讓我們清楚畫布的位置 
如果不設定css樣式的width和height,那麼畫布大小就是我們在canvas標籤上定義的寬500和高500 
但是這裡我定義了樣式寬高,所以我們的畫布在網頁中實際的大小相當於被縮小了1倍 
(canvas的最大畫布座標仍然是500×500)

html文件中我們要做的只是新增一個canvas標籤 
下面我們需要使用指令碼來繪製圖形

獲取元素與環境物件

獲取元素我們都知道

var myCanvas = document
.getElementById('myCanvas');
  • 1
  • 1

下面的程式碼用於獲取上下文物件/環境物件

var ctx = myCanvas.getContext('2d');
  • 1
  • 1

getContext用於指定一個繪圖環境 
這裡我們使用最常用的二維繪圖 
使用getContext(‘2d’) 
(getContext(‘webgl’)用於3D繪圖) 
獲取到的ctx是一個物件(CanvasRenderingContext2D物件) 
用以到處二位繪圖API

再通俗的說,canvas元素物件是我們的“畫布” 
而通過getContext(‘2d’)獲取的環境物件就是我們的“畫筆” 
下面我們所有的操作都需要是用“畫筆”,也就是這個上下文物件(這裡就是變數ctx)

線段繪製

下面我們就來繪製最簡單的圖形——線段 
現在網頁上已經有了一個500×500的畫布 
我們先來畫一條斜線

ctx.moveTo(100, 100);
ctx.lineTo(400, 400);
ctx.stroke();
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

moveTo()用來指定繪製線段的起點 
lineTo()用來指定繪製線段的終點(或者是途經點) 
stroke()先不說,現在只要知道沒有它畫不出來 

瞭解了這些,我們現在可以畫一個三角形 
只要新增兩行程式碼

ctx.moveTo(100, 100);
ctx.lineTo(400, 400);
ctx.lineTo(100, 400); //增
ctx.lineTo(100, 100); //增
ctx.stroke();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

看樣子似乎不錯 
現在再教大家如何改變線段寬度 
改變lineWidth

ctx.lineWidth = 50;  //增
ctx.moveTo(100, 100);
ctx.lineTo(400, 400);
ctx.lineTo(100, 400);
ctx.lineTo(100, 100);
ctx.stroke();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

這回發現了問題 
當我們把線段變粗後 
起點與終點不能很好的連線 
這時我們需要另一個函式closePath()

ctx.lineWidth = 50;
ctx.moveTo(100, 100);
ctx.lineTo(400, 400);
ctx.lineTo(100, 400);
ctx.closePath(); //改
ctx.stroke();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

closePath()就是為了解決這樣閉合圖形的問題 
它使我們繪製了完美的閉合三角形

最後解釋一下stroke() 
它表示繪製空心圖形 
除此之外還有繪製實心圖形的fill()

ctx.lineWidth = 50;
ctx.moveTo(100, 100);
ctx.lineTo(400, 400);
ctx.lineTo(100, 400);
ctx.closePath();
ctx.fill(); //改
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

沒有它們中的一個,我們就不能繪製出圖形啦 
可以理解為前面都是定義規則,而stroke()或fill()是真正的執行官

還有一點要注意 
fill()和stroke()都是作用在當前的所有子路徑 
如果在完成一條路徑之後想要開始另一條路徑 
必須使用beginPath()方法 
看下面的例子(strokeStyle/fillStyle用於設定我們線段的顏色)

ctx.lineWidth = 60;
ctx.strokeStyle = 'red';
ctx.moveTo(100, 100);
ctx.lineTo(100, 400);
ctx.stroke();

ctx.lineWidth = 20;
ctx.strokeStyle = 'blue';
ctx.moveTo(400, 100);
ctx.lineTo(400, 400);
ctx.stroke();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

最終的結果可能和大家想的不一樣 
這裡寫圖片描述

因為引擎認為它們是一條路徑 
所有下面的屬性覆蓋了上面的屬性 
解決辦法就是這樣

//一條路徑
ctx.lineWidth = 60;
ctx.strokeStyle = 'red';
ctx.moveTo(100, 100);
ctx.lineTo(100, 400);
ctx.stroke();

ctx.beginPath(); //增

//另一條路徑
ctx.lineWidth = 20;
ctx.strokeStyle = 'blue';
ctx.moveTo(400, 100);
ctx.lineTo(400, 400);
ctx.stroke();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

矩形繪製

使用我們上面學到的方法也可以輕鬆實現一個矩形

ctx.moveTo(100, 100);
ctx.lineTo(400, 100);
ctx.lineTo(400, 400);
ctx.lineTo(100, 400);
ctx.closePath();
ctx.stroke();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

不過這程式碼過於冗長 
我們有更簡單的方法

ctx.rect(100, 100, 300, 300);
ctx.stroke();
  • 1
  • 2
  • 1
  • 2

前倆個引數就是起始畫布座標,後兩個引數表示矩形的寬和高 
不過我們還有更簡單的

ctx.strokeRect(100, 100, 300, 300);
  • 1
  • 1

大家一定猜到了,還有一個畫實心矩形的fillRect()

ctx.fillRect(100, 100, 300, 300);
  • 1
  • 1

有了strokeRect()和fillRect()我們就完全沒必要使用rect()了

矩形擦除

clearRect()可以讓我們擦除畫布上的矩形區域 
這就類似於我們的橡皮擦 
比如說我在實心矩形中間挖走一塊矩形區域

ctx.fillRect(100, 100, 300, 300);
ctx.clearRect(150, 150, 200, 200);
  • 1
  • 2
  • 1
  • 2

現在我們可以利用這個方法實現一個矩形下落的效果

var width = 500,
    height = 500,
    y = 50;
var timer = setInterval(function(){
    ctx.clearRect(0, 0, width, height);
    ctx.fillRect(100, y++, 50, 50);
    if(y > 400){
        clearInterval(timer);
    }
}, 5);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

 
重點就是 
每次更新矩形之前,清除一下畫布

弧形繪製

繪製弧形使用方法 
arc(x, y, r, 起始弧度, 結束弧度, 弧線方向) 
這裡要知道畫布的座標系和瀏覽器座標系是一樣的 
x正半軸朝右 
y正半軸朝下 
引數中x,y表示弧心(或者理解為圓心) 
r就是半徑 
弧線方向0為順時針,1為逆時針 
下面我們來使用這個方法 
先來畫一段弧

ctx.arc(250, 250, 200, 0, Math.PI/2, 0);
ctx.stroke();
  • 1
  • 2
  • 1
  • 2

畫一個圓

ctx.arc(250, 250, 200, 0, 2*Math.PI, 0);
ctx.stroke();
  • 1
  • 2
  • 1
  • 2

畫一個吃豆人

ctx.moveTo(250, 250);
ctx.lineTo(450, 250);
ctx.arc(250, 250, 200, 0, Math.PI/3, 1);
ctx.closePath();
ctx.stroke();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

繪製弧形除此之外,還有一個方法 
arcTo(x1, y1, x2, y2, r) 
r是弧的半徑 
它的使用方法參照下面的圖片 

配合我們的線段繪製 
可以畫出圓角矩形

ctx.moveTo(250, 100);
ctx.arcTo(400, 100, 400, 400, 30);
ctx.arcTo(400, 400, 100, 400, 30);
ctx.arcTo(100, 400, 100, 100, 30);
ctx.arcTo(100, 100, 400, 100, 30);
ctx.closePath();
ctx.stroke();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

貝塞爾曲線

還有更高階的方法就是可以繪製貝塞爾曲線 
簡單瞭解就好,不常用 
二次貝塞爾曲線 quadraticCurveTo(x1, y1, ex, ey) 
三次貝塞爾曲線 bezierCurveTo(x1, y1, x2, y2, ex, ey) 
其中x1,y1,x2,y2分別是第一個和第二個控制點的座標 
ex和ey是終點座標 
這裡我就盜用網上大神做的圖片了

二次貝塞爾曲線: 
這裡寫圖片描述

ctx.moveTo(40,340); 
ctx.lineTo(260,80); 
ctx.lineTo(360,300); 
ctx.stroke();  
/*繪製二次貝塞爾曲線*/
ctx.beginPath(); 
ctx.moveTo(40,340); 
ctx.quadraticCurveTo(260,80,360,300); 
ctx.strokeStyle = "#f40"; 
ctx.stroke(); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

三次貝塞爾曲線: 

ctx.moveTo(50, 350); 
ctx.lineTo(120, 160); 
ctx.lineTo(300, 60); 
ctx.lineTo(340, 300); 
ctx.stroke(); 
/*繪製三次貝塞爾曲線 */
ctx.beginPath(); 
ctx.moveTo(50, 350); 
ctx.bezierCurveTo(120, 160, 300, 60, 340, 300); 
ctx.strokeStyle = "#f40"; 
ctx.stroke(); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11