關注Vue社區,回復「加群」
加入我們一起學習,天天進步
作者:德育處主任
https://juejin.cn/post/7116784455561248775
本文簡介
點讚 + 關注 + 收藏 = 學會了
在前端領域,如果只是懂Vue或者React,未來在職場的競爭力可能會比較弱。
根據我多年在家待業經驗來看,前端未來在數據可視化和AI這兩個領域會比較香,而Canvas是數據可視化在前端方面的基礎技術。
本文就用光的速度將canvas給入門了。

01.gif
要入門一個技術,前期最重要是快!所以本文只講入門內容,能應付簡單項目。深入的知識點會在其他文章講解。
Canvas 是什麼?
就上面的描述而言可能有點難懂,你可以打開AntV旗下的圖形編輯引擎做對比。G6[1]是使用canvas開發的,X6[2]是使用svg開發的。
我的建議是:如果要展示的數據量比較大,比如一條數據就是一個元素節點,那使用canvas會比較合適;如果用戶操作的交互比較多,而且對清晰度有要求(矢量圖),那麼使用svg會比較合適。
起步
學習前端一定要動手敲代碼,然後看效果展示。
起步階段會用幾句代碼說明canvas如何使用,本例會畫一條直線。
畫條直線02.png<!--1、創建canvas元素--><canvasid="c"width="300"height="200"style="border:1pxsolid#ccc;"></canvas><script>//2、獲取canvas對象constcnv=document.getElementById('c')//3、獲取canvas上下文環境對象constcxt=cnv.getContext('2d')//4、繪製圖形cxt.moveTo(100,100)//起點坐標(x,y)cxt.lineTo(200,100)//終點坐標(x,y)cxt.stroke()//將起點和終點連接起來</script>複製代碼
moveTo、lineTo和stroke方法暫時可以不用管,它們的作用是繪製圖形,這些方法在後面會講到~
注意點1、默認寬高
canvas有默認的 寬度(300px) 和 高度(150px)
如果不在canvas上設置寬高,那canvas元素的默認寬度是300px,默認高度是150px。
2、設置 canvas 寬高
canvas元素提供了width和height兩個屬性,可設置它的寬高。
需要注意的是,這兩個屬性只需傳入數值,不需要傳入單位(比如px等)。
<canvaswidth="600"height="400"></canvas>複製代碼3、不能通過 CSS 設置畫布的寬高
使用css設置canvas的寬高,會出現內容被拉伸的後果!!!

03.png<style>#c{width:400px;height:400px;border:1pxsolid#ccc;}</style><canvasid="c"></canvas><script>//1、獲取canvas對象constcnv=document.getElementById('c')//2、獲取canvas上下文環境對象constcxt=cnv.getContext('2d')//3、繪製圖形cxt.moveTo(100,100)//起點cxt.lineTo(200,100)//終點cxt.stroke()//將起點和終點連接起來console.log(cnv.width)//獲取 canvas 的寬度,輸出:300console.log(cnv.height)//獲取 canvas 的高度,輸出:150</script>複製代碼
canvas的默認寬度是300px,默認高度是150px。
最後出現的效果如上圖所示。
4、線條默認寬度和顏色
線條的默認寬度是1px,默認顏色是黑色。
但由於默認情況下canvas會將線條的中心點和像素的底部對齊,所以會導致顯示效果是2px和非純黑色問題。
5、IE兼容性高
暫時只有IE 9以上才支持canvas。但好消息是IE已經有自己的墓碑了。
如需兼容IE 7 和 8,可以使用ExplorerCanvas[3]。但即使是使用了ExplorerCanvas仍然會有所限制,比如無法使用fillText()方法等。
基礎圖形坐標系
在繪製基礎圖形之前,需要先搞清除Canvas使用的坐標系。
Canvas使用的是W3C 坐標系,也就是遵循我們屏幕、報紙的閱讀習慣,從上往下,從左往右。

04.jpg
W3C 坐標系和數學直角坐標系的X軸是一樣的,只是Y軸的反向相反。
W3C 坐標系的Y軸正方向向下。
直線一條直線
最簡單的起步方式是畫一條直線。這裡所說的 「直線」 是幾何學裡的 「線段」 的意思。
需要用到這3個方法:
起步階段可以先這樣理解。

05.png<canvasid="c"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//繪製直線cxt.moveTo(50,100)//起點坐標cxt.lineTo(200,50)//下一個點的坐標cxt.stroke()//將上面的坐標用一條線連接起來</script>複製代碼
上面的代碼所呈現的效果,可以看下圖解釋(手不太聰明,畫得不是很標準,希望能看懂)

06.jpg多條直線
如需畫多條直線,可以用會上面那幾個方法。

07.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.moveTo(20,100)cxt.lineTo(200,100)cxt.stroke()cxt.moveTo(20,120.5)cxt.lineTo(200,120.5)cxt.stroke()</script>複製代碼
仔細觀察一下,為什麼兩條線的粗細不一樣的?
明明使用的方法都是一樣的,只是第二條直線的Y軸的值是有小數點。
答:默認情況下canvas會將線條的中心點和像素的底部對齊,所以會導致顯示效果是2px和非純黑色問題。

08.jpg
上圖每個格子代表1px。
線的中心點會和畫布像素點的底部對齊,所以會線中間是黑色的,但由於一個像素就不能再切割了,所以會有半個像素被染色,就變成了淺灰色。
所以如果你設置的Y軸值是一個整數,就會出現上面那種情況。
設置樣式09.png<canvasid="c"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//繪製直線cxt.moveTo(50,50)cxt.lineTo(200,50)//修改直線的寬度cxt.lineWidth=20//修改直線的顏色cxt.strokeStyle='pink'//修改直線兩端樣式cxt.lineCap='round'//默認:butt;圓形:round;方形:squarecxt.stroke()</script>複製代碼新開路徑
開闢新路徑的方法:
在繪製多條線段的同時,還要設置線段樣式,通常需要開闢新路徑。
要不然樣式之間會相互污染。
比如這樣

10.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//第一條線cxt.moveTo(20,100)cxt.lineTo(200,100)cxt.lineWidth=10cxt.strokeStyle='pink'cxt.stroke()//第二條線cxt.moveTo(20,120.5)cxt.lineTo(200,120.5)cxt.stroke()</script>複製代碼
如果不想相互污染,需要做2件事:
如果上面2步卻了其中1步都會有影響。
只使用beginPath()11.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//第一條線cxt.moveTo(20,100)cxt.lineTo(200,100)cxt.lineWidth=10cxt.strokeStyle='pink'cxt.stroke()//第二條線cxt.beginPath()//重新開啟一個路徑cxt.moveTo(20,120.5)cxt.lineTo(200,120.5)cxt.stroke()</script>複製代碼
第一條線的樣式會影響之後的線。
但如果使用了beginPath(),後面的線段不會影響前面的線段。

12.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//第一條線cxt.moveTo(20,100)cxt.lineTo(200,100)cxt.stroke()//第二條線cxt.beginPath()//重新開啟一個路徑cxt.moveTo(20,120.5)cxt.lineTo(200,120.5)cxt.lineWidth=4cxt.strokeStyle='red'cxt.stroke()</script>複製代碼設置新線段的樣式,沒使用beginPath()的情況
這個情況會反過來,後面的線能影響前面的線。

13.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//第一條線cxt.moveTo(20,100)cxt.lineTo(200,100)cxt.lineWidth=10cxt.strokeStyle='pink'cxt.stroke()//第二條線cxt.moveTo(20,120.5)cxt.lineTo(200,120.5)cxt.lineWidth=4cxt.strokeStyle='red'cxt.stroke()</script>複製代碼正確的做法
在設置beginPath()的同時,也各自設置樣式。這樣就能做到相互不影響了。

14.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.moveTo(20,100)cxt.lineTo(200,100)cxt.lineWidth=10cxt.strokeStyle='pink'cxt.stroke()cxt.beginPath()//重新開啟一個路徑cxt.moveTo(20,120.5)cxt.lineTo(200,120.5)cxt.lineWidth=4cxt.strokeStyle='red'cxt.stroke()</script>複製代碼折線
和直線差不多,都是使用moveTo()、lineTo()和stroke()方法可以繪製折線。

15.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.moveTo(50,200)cxt.lineTo(100,50)cxt.lineTo(200,200)cxt.lineTo(250,50)cxt.stroke()</script>複製代碼
畫這種折線,最好在草稿紙上畫一個坐標系,自己計算並描繪一下每個點大概在什麼什麼位置,最後在canvas中看看效果。
矩形
根據前面的基礎,我們可以使用線段來描繪矩形,但canvas也提供了rect()等方法可以直接生成矩形。
使用線段描繪矩形
可以使用前面畫線段的方法來繪製矩形

16.pngcanvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//繪製矩形cxt.moveTo(50,50)cxt.lineTo(200,50)cxt.lineTo(200,120)cxt.lineTo(50,120)cxt.lineTo(50,50)//需要閉合,又或者使用closePath()方法進行閉合,推薦使用closePath()cxt.stroke()</script>複製代碼
上面的代碼幾個點分別對應下圖。

17.jpg使用strokeRect()描邊矩形18.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//strokeStyle屬性//strokeRect(x,y,width,height)方法cxt.strokeStyle='pink'cxt.strokeRect(50,50,200,100)</script>複製代碼
上面的代碼可以這樣理解

19.jpg使用fillRect()填充矩形
fillRect()和strokeRect()方法差不多,但fillRect()的作用是填充。
需要注意的是,fillStyle必須寫在fillRect()之前,不然樣式不生效。

20.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//fillStyle屬性//fillRect(x,y,width,height)方法cxt.fillStyle='pink'cxt.fillRect(50,50,200,100)//fillRect(x,y,width,height)</script>複製代碼同時使用strokeRect()和fillRect()
同時使用strokeRect()和fillRect()會產生描邊和填充的效果

21.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.strokeStyle='red'cxt.strokeRect(50,50,200,100)//strokeRect(x,y,width,height)cxt.fillStyle='yellow'cxt.fillRect(50,50,200,100)//fillRect(x,y,width,height)</script>複製代碼使用rect()生成矩形
rect()和fillRect() 、strokeRect()的用法差不多,唯一的區別是:
strokeRect()和fillRect()這兩個方法調用後會立即繪製;rect()方法被調用後,不會立刻繪製矩形,而是需要調用stroke()或fill()輔助渲染。

22.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.strokeStyle='red'cxt.fillStyle='pink'cxt.rect(50,50,200,100)//rect(x,y,width,height)cxt.stroke()cxt.fill()</script>複製代碼
等價公式:
cxt.strokeStyle='red',cxt.rect(50,50,200,100)cxt.stroke()//等價於cxt.strokeStyle='red'cxt.strokerect(50,50,200,100)//-----------------------------cxt.fillStyle='hotpink'cxt.rect(50,50,200,100)cxt.fill()//等價於cxt.fillStyle='yellowgreen'cxt.fillRect(50,50,200,100)複製代碼使用clearRect()清空矩形
使用clearRect()方法可以清空指定區域。
clearRect(x,y,width,height)複製代碼
其語法和創建cxt.rect()差不多。

23.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.fillStyle='pink'//設置填充顏色cxt.fillRect(50,50,200,200)//填充矩形cxt.clearRect(60,60,180,90)//清空矩形</script>複製代碼清空畫布
canvas畫布元素是矩形,所以可以通過下面的代碼把整個畫布清空掉。
//省略部分代碼cxt.clearRect(0,0,cnv.width,cnv.height)複製代碼
要清空的區域:從畫布左上角開始,直到畫布的寬和畫布的高為止。
多邊形
Canvas要畫多邊形,需要使用moveTo()、lineTo()和closePath()。
三角形
雖然三角形是常見圖形,但canvas並沒有提供類似rect()的方法來繪製三角形。
需要確定三角形3個點的坐標位置,然後使用stroke()或者fill()方法生成三角形。

24.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.moveTo(50,50)cxt.lineTo(200,50)cxt.lineTo(200,200)//注意點:如果使用 lineTo 閉合圖形,是不能很好閉合拐角位的。cxt.lineTo(50,50)//閉合cxt.stroke()</script>複製代碼
注意,默認情況下不會自動從最後一個點連接到起點。最後一步需要設置一下cxt.lineTo(50, 50),讓它與cxt.moveTo(50, 50)一樣。這樣可以讓路徑回到起點,形成一個閉合效果。
但這樣做其實是有點問題的,而且也比較麻煩,要記住起始點坐標。
上面的閉合操作,如果遇到設置了lineWidth或者lineJoin就會有問題,比如:

25.png//省略部分代碼cxt.lineWidth=20複製代碼
當線段變粗後,起始點和結束點的鏈接處,拐角就出現「不正常」現象。
如果需要真正閉合,可以使用closePath()方法。

26.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.moveTo(50,50)cxt.lineTo(200,50)cxt.lineTo(200,200)//手動閉合cxt.closePath()cxt.lineJoin='miter'//線條連接的樣式。miter:默認; bevel:斜面; round:圓角cxt.lineWidth=20cxt.stroke()</script>複製代碼
使用cxt.closePath()可以自動將終點和起始點連接起來,此時看上去就正常多了。
菱形
有一組鄰邊相等的平行四邊形是菱形

27.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.moveTo(150,50)cxt.lineTo(250,100)cxt.lineTo(150,150)cxt.lineTo(50,100)cxt.closePath()cxt.stroke()</script>複製代碼
要繪製直線類型的圖形,在草稿紙上標記出起始點和每個拐角的點,然後再連線即可。相對曲線圖形來說,直線圖形是比較容易的。
圓形
繪製圓形的方法是arc()。
語法:
arc(x,y,r,sAngle,eAngle,counterclockwise)複製代碼28.jpg
開始角度和結束角度,都是以弧度為單位。例如 180°就寫成Math.PI,360°寫成Math.PI * 2,以此類推。
在實際開發中,為了讓自己或者別的開發者更容易看懂弧度的數值,1°應該寫成Math.PI / 180。
注意:繪製圓形之前,必須先調用beginPath()方法!!!在繪製完成之後,還需要調用closePath()方法!!!

29.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.beginPath()cxt.arc(150,150,80,0,360*Math.PI/180)cxt.closePath()cxt.stroke()</script>複製代碼半圓
如果使用arc()方法畫圓時,沒做到剛好繞完一周(360°)就直接閉合路徑,就會出現半圓的狀態。

30.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.beginPath()cxt.arc(150,150,100,0,180*Math.PI/180)//順時針cxt.closePath()cxt.stroke()</script>複製代碼
上面的代碼中,cxt.arc最後一個參數沒傳,默認是false,所以是順時針繪製。

31.jpg
如果希望半圓的弧面在上方,可以將cxt.arc最後一個參數設置成true

32.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.beginPath()cxt.arc(150,150,100,0,180*Math.PI/180,true)cxt.closePath()cxt.stroke()</script>複製代碼弧線
使用arc()方法畫半圓時,如果最後不調用closePath()方法,就不會出現閉合路徑。也就是說,那是一條弧線。
在canvas中,畫弧線有2中方法:arc()和arcTo()。
arc() 畫弧線
如果想畫一條0° ~ 30°的弧線,可以這樣寫

33.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.beginPath()cxt.arc(150,150,100,0,30*Math.PI/180)cxt.stroke()</script>複製代碼
原理如下圖所示,紅線代表畫出來的那條弧線。

34.jpgarcTo() 畫弧線
arcTo()的使用方法會更加複雜,如果初學看不太懂的話可以先跳過,看完後面的再回來補補。
語法:
arcTo(cx, cy, x2, y2, radius)複製代碼
其中,(cx, cy)也叫控制點,(x2, y2)也叫結束點。
是不是有點奇怪,為什麼沒有x1和y1?
(x1, y1)是開始點,通常是由moveTo()或者lineTo()提供。
arcTo()方法利用開始點、控制點和結束點形成的夾角,繪製一段與夾角的兩邊相切並且半徑為radius的圓弧。

35.jpg
舉個例子

36.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.moveTo(40,40)cxt.arcTo(120,40,120,120,80)cxt.stroke()</script>複製代碼基礎樣式
前面學完基礎圖形,接下來可以開始了解一下如何設置元素的基礎樣式。
描邊 stroke()
前面的案例中,其實已經知道使用stroke()方法進行描邊了。這裡就不再多講這個方法。
線條寬度 lineWidth
lineWidth默認值是1,默認單位是px。
語法:
lineWidth=線寬複製代碼37.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//線寬10cxt.beginPath()cxt.moveTo(50,50)cxt.lineTo(250,50)cxt.lineWidth=10//設置線寬cxt.stroke()//線寬20cxt.beginPath()cxt.moveTo(50,150)cxt.lineTo(250,150)cxt.lineWidth=20//設置線寬cxt.stroke()//線寬30cxt.beginPath()cxt.moveTo(50,250)cxt.lineTo(250,250)cxt.lineWidth=30//設置線寬cxt.stroke()</script>複製代碼線條顏色 strokeStyle
使用strokeStyle可以設置線條顏色
語法:
strokeStyle=顏色值複製代碼38.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.moveTo(50,50)cxt.lineTo(250,50)cxt.lineWidth=20cxt.strokeStyle='pink'//設置顏色cxt.stroke()</script>複製代碼
為了展示方便,我將lineWidth設為 20。
線帽 lineCap
線帽指的是線段的開始和結尾處的樣式,使用lineCap可以設置
語法:
lineCap='屬性值'複製代碼
屬性值包括:

39.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//設置線寬,方便演示cxt.lineWidth=16//默認線帽buttcxt.beginPath()cxt.moveTo(50,60)cxt.lineTo(250,60)cxt.stroke()//方形線帽squarecxt.beginPath()cxt.lineCap='square'cxt.moveTo(50,150)cxt.lineTo(250,150)cxt.stroke()//圓形線帽roundcxt.beginPath()cxt.lineCap='round'cxt.moveTo(50,250)cxt.lineTo(250,250)cxt.stroke()</script>複製代碼
使用square和round的話,會使線條變得稍微長一點點,這是給線條增加線帽的部分,這個長度在日常開發中需要注意。
線帽只對線條的開始和結尾處產生作用,對拐角不會產生任何作用。
拐角樣式 lineJoin
如果需要設置拐角樣式,可以使用lineJoin。
語法:
lineJoin='屬性值'複製代碼
屬性值包括:

40.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.lineWidth=20//默認,尖角cxt.moveTo(50,40)cxt.lineTo(200,40)cxt.lineTo(200,90)cxt.stroke()//斜角bevelcxt.beginPath()cxt.moveTo(50,140)cxt.lineTo(200,140)cxt.lineTo(200,190)cxt.lineJoin='bevel'cxt.stroke()//圓角roundcxt.beginPath()cxt.moveTo(50,240)cxt.lineTo(200,240)cxt.lineTo(200,290)cxt.lineJoin='round'cxt.stroke()</script>複製代碼虛線 setLineDash()
使用setLineDash()方法可以將描邊設置成虛線。
語法:
setLineDash([])複製代碼
需要傳入一個數組,且元素是數值型。
虛線分3種情況

41.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.lineWidth=20cxt.strokeStyle='pink'cxt.moveTo(50,50)cxt.lineTo(200,50)cxt.setLineDash([10])//只傳1個參數,實線與空白都是10pxcxt.stroke()cxt.beginPath()cxt.moveTo(50,100)cxt.lineTo(200,100)cxt.setLineDash([10,20])//2個參數,此時,實線是10px,空白20pxcxt.stroke()cxt.beginPath()cxt.moveTo(50,150)cxt.lineTo(200,150)cxt.setLineDash([10,20,5])//傳3個以上的參數,此例:10px實線,20px空白,5px實線,10px空白,20px實線,5px空白……cxt.stroke()</script>複製代碼
此外,還可以始終cxt.getLineDash()獲取虛線不重複的距離;
用cxt.lineDashOffset設置虛線的偏移位。
填充
使用fill()可以填充圖形,根據前面的例子應該掌握了如何使用fill()

42.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.fillStyle='pink'cxt.rect(50,50,200,100)cxt.fill()</script>複製代碼
可以使用fillStyle設置填充顏色,默認是黑色。
非零環繞填充
在使用fill()方法填充時,需要注意一個規則:非零環繞填充。
在使用moveTo和lineTo描述圖形時,如果是按順時針繪製,計數器會加1;如果是逆時針,計數器會減1。
當圖形所處的位置,計數器的結果為0時,它就不會被填充。
這樣說有點複雜,先看看例子

43.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//外層矩形cxt.moveTo(50,50)cxt.lineTo(250,50)cxt.lineTo(250,250)cxt.lineTo(50,250)cxt.closePath()//內層矩形cxt.moveTo(200,100)cxt.lineTo(100,100)cxt.lineTo(100,200)cxt.lineTo(200,200)cxt.closePath()cxt.fill()</script>複製代碼
請看看上面的代碼,我畫了2個矩形,它們都沒有用beginPath()方法開闢新路徑。

44.png
內層矩形是逆時針繪製的,所以內層的值是-1,它又經過外層矩形,而外層矩形是順時針繪製,所以經過外層時值+1,最終內層的值為0,所以不會被填充。
文本
Canvas提供了一些操作文本的方法。
為了方便演示,我們先了解一下在Canvas中如何給本文設置樣式。
樣式 font
和CSS設置font差不多,Canvas也可以通過font設置樣式。
語法:
cxt.font='font-stylefont-variantfont-weightfont-size/line-heightfont-family'複製代碼
如果需要設置字號font-size,需要同事設置font-family。
cxt.font='30px宋體'複製代碼描邊 strokeText()
使用strokeText()方法進行文本描邊
語法:
strokeText(text, x, y, maxWidth)複製代碼45.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.font='60pxArial'//將字號設置成60px,方便觀察cxt.strokeText('雷猴',30,90)</script>複製代碼設置描邊顏色 strokeStyle
使用strokeStyle設置描邊顏色。

46.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.font='60pxArial'//將字號設置成60px,方便觀察cxt.strokeStyle='pink'//設置文本描邊顏色cxt.strokeText('雷猴',30,90)</script>複製代碼填充 fillText
使用fillText()可填充文本。
語法和strokeText()一樣。
fillText(text,x,y,maxWidth)複製代碼47.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.font='60pxArial'cxt.fillText('雷猴',30,90)</script>複製代碼設置填充顏色 fillStyle
使用fillStyle可以設置文本填充顏色。

48.png<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')cxt.font='60pxArial'cxt.fillStyle='pink'cxt.fillText('雷猴',30,90)</script>複製代碼獲取文本長度 measureText()
measureText().width方法可以獲取文本的長度,單位是px。
<canvasid="c"width="300"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')lettext='雷猴'cxt.font='bold40pxArial'cxt.fillText(text,40,80)console.log(cxt.measureText(text).width)//80</script>複製代碼水平對齊方式 textAlign
使用textAlign屬性可以設置文字的水平對齊方式,一共有5個值可選

49.png
紅線是輔助參考線。
<canvasid="c"width="400"height="400"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//豎向的輔助線(參考線,在畫布中間)cxt.moveTo(200,0)cxt.lineTo(200,400)cxt.strokeStyle='red'cxt.stroke()cxt.font='30pxArial'//橫坐標開始位對齊cxt.textAlign='start'//默認值,cxt.fillText('雷猴start',200,40)//橫坐標結束位對齊cxt.textAlign='end'//結束對齊cxt.fillText('雷猴end',200,100)//左對齊cxt.textAlign='left'//左對齊cxt.fillText('雷猴left',200,160)//右對齊cxt.textAlign='right'//右對齊cxt.fillText('雷猴right',200,220)//居中對齊cxt.textAlign='center'//右對齊cxt.fillText('雷猴center',200,280)</script>複製代碼
從上面的例子看,start和left的效果好像是一樣的,end和right也好像是一樣的。
在大多數情況下,它們的確一樣。但在某些國家或者某些場合,閱讀文字的習慣是從右往左時,start就和right一樣了,end和left也一樣。這是需要注意的地方。
垂直對齊方式 textBaseline
使用textBaseline屬性可以設置文字的垂直對齊方式。
在使用textBaseline前,需要自行了解css的文本基線。

50.png
用一張網圖解釋一下基線
textBaseline可選屬性:

51.png
紅線是輔助參考線。
<canvasid="c"width="800"height="300"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//橫向的輔助線(參考線,在畫布中間)cxt.moveTo(0,150)cxt.lineTo(800,150)cxt.strokeStyle='red'cxt.stroke()cxt.font='20pxArial'//默認alphabeticcxt.textBaseline='alphabetic'cxt.fillText('雷猴alphabetic',10,150)//默認topcxt.textBaseline='top'cxt.fillText('雷猴top',200,150)//默認bottomcxt.textBaseline='bottom'cxt.fillText('雷猴bottom',320,150)//默認middlecxt.textBaseline='middle'cxt.fillText('雷猴middle',480,150)//默認hangingcxt.textBaseline='hanging'cxt.fillText('雷猴hanging',640,150)</script>複製代碼
注意:在繪製文字的時候,默認是以文字的左下角作為參考點進行繪製
圖片
在Canvas中可以使用drawImage()方法繪製圖片。
渲染圖片
渲染圖片的方式有2中,一種是在JS里加載圖片再渲染,另一種是把DOM里的圖片拿到canvas里渲染。
渲染的語法:
drawImage(image,dx,dy)複製代碼
在JS里加載圖片並渲染,有以下幾個步驟:

52.png<canvasid="c"width="500"height="500"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')//1創建Image對象constimage=newImage()//2引入圖片image.src='./images/dog.jpg'//3等待圖片加載完成image.onload=()=>{//4使用drawImage()方法渲染圖片cxt.drawImage(image,30,30)}</script>複製代碼DOM版53.png<style>#dogImg{display:none;}</style><imgsrc="./images/dog.jpg"id="dogImg"/><canvasid="c"width="500"height="500"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')constimage=document.getElementById('dogImg')cxt.drawImage(image,70,70)</script>複製代碼
因為圖片是從DOM里獲取到的,所以一般來說,只要在window.onload這個生命周期內使用drawImage都可以正常渲染圖片。
本例使用了css的方式,把圖片的display設置成none。因為我不想被<img>影響到本例講解。
實際開發過程中按照實際情況設置即可。
設置圖片寬高
前面的例子都是直接加載圖片,圖片默認的寬高是多少就加載多少。
如果需要指定圖片寬高,可以在前面的基礎上再添加兩個參數:
drawImage(image,dx,dy,dw,dh)複製代碼
image、 dx、 dy的用法和前面一樣。
dw用來定義圖片的寬度,dh定義圖片的高度。

54.png<canvasid="c"width="500"height="500"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')constimage=newImage()image.src='./images/dog.jpg'image.onload=()=>{cxt.drawImage(image,30,30,100,100)}</script>複製代碼
我把圖片的尺寸設為 100px * 100px,圖片看上去比之前就小了很多。
截取圖片
截圖圖片同樣使用drawImage()方法,只不過傳入的參數數量比之前都多,而且順序也有點不一樣了。
drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh)複製代碼
以上參數缺一不可

55.png<canvasid="c"width="500"height="500"style="border:1pxsolid#ccc;"></canvas><script>constcnv=document.getElementById('c')constcxt=cnv.getContext('2d')constimage=newImage()image.src='./images/dog.jpg'image.onload=()=>{cxt.drawImage(image,0,0,100,100,30,30,200,200)}</script>複製代碼總結
本文主要講解了在Canvas中繪製一些基礎圖形,還有一些基礎樣式設置。
還有更多高級的玩法會在之後的文章中講到,比如漸變、投影、濾鏡等等。
代碼倉庫
⭐雷猴 Canvas[4]
推薦閱讀
👍《Fabric.js 從入門到膨脹》[5]
👍《『Three.js』起飛!》[6]
👍《console.log也能插圖!!!》[7]
👍《純css實現117個Loading效果》[8]
👍《視差特效的原理和實現方法》[9]
👍《這18個網站能讓你的頁面背景炫酷起來》[10]
點讚 + 關注 + 收藏 = 學會了
❤️ 看完兩件事
如果你覺得這篇內容對你挺有啟發,我想邀請你幫我兩個小忙:
點個「在看」,讓更多的人也能看到這篇內容(喜歡不點在看,都是耍流氓 -_-)
關注公眾號「Vue社區」,每周重點攻克一個前端面試重難點,
公眾號後台回復「電子書」即可免費獲取 27本 精選的前端電子書!
