close

關注Vue社區,回復「加群」

加入我們一起學習,天天進步

作者:德育處主任

https://juejin.cn/post/7116784455561248775

本文簡介

點讚 + 關注 + 收藏 = 學會了

在前端領域,如果只是懂Vue或者React,未來在職場的競爭力可能會比較弱。

根據我多年在家待業經驗來看,前端未來在數據可視化和AI這兩個領域會比較香,而Canvas是數據可視化在前端方面的基礎技術。

本文就用光的速度將canvas給入門了。

01.gif

要入門一個技術,前期最重要是快!所以本文只講入門內容,能應付簡單項目。深入的知識點會在其他文章講解。

Canvas 是什麼?

Canvas中文名叫 「畫布」,是HTML5新增的一個標籤。
Canvas允許開發者通過JS在這個標籤上繪製各種圖案。
Canvas擁有多種繪製路徑、矩形、圓形、字符以及圖片的方法。
Canvas在某些情況下可以 「代替」 圖片。
Canvas可用於動畫、遊戲、數據可視化、圖片編輯器、實時視頻處理等領域。
Canvas 和 SVG 的區別
CanvasSVG用JS動態生成元素(一個HTML元素)用XML描述元素(類似HTML元素那樣,可用多個元素來描述一個圖形)位圖(受屏幕分辨率影響)矢量圖(不受屏幕分辨率影響)不支持事件支持事件數據發生變化需要重繪不需要重繪

就上面的描述而言可能有點難懂,你可以打開AntV旗下的圖形編輯引擎做對比。G6[1]是使用canvas開發的,X6[2]是使用svg開發的。

我的建議是:如果要展示的數據量比較大,比如一條數據就是一個元素節點,那使用canvas會比較合適;如果用戶操作的交互比較多,而且對清晰度有要求(矢量圖),那麼使用svg會比較合適。

起步

學習前端一定要動手敲代碼,然後看效果展示。

起步階段會用幾句代碼說明canvas如何使用,本例會畫一條直線。

畫條直線

在HTML中創建canvas元素
通過js獲取canvas標籤
從canvas標籤中獲取到繪圖工具
通過繪圖工具,在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。

如果使用css修改canvas的寬高(比如本例變成 400px * 400px),那寬度就由 300px 拉伸到 400px,高度由 150px 拉伸到 400px。
使用js獲取canvas的寬高,此時返回的是canvas的默認值。

最後出現的效果如上圖所示。

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個方法:

moveTo(x1, y1):起點坐標 (x, y)
lineTo(x2, y2):下一個點的坐標 (x, y)
stroke():將所有坐標用一條線連起來

起步階段可以先這樣理解。

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軸值是一個整數,就會出現上面那種情況。

設置樣式

lineWidth:線的粗細
strokeStyle:線的顏色
lineCap:線帽:默認:butt; 圓形:round; 方形:square
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>複製代碼新開路徑

開闢新路徑的方法:

beginPath()

在繪製多條線段的同時,還要設置線段樣式,通常需要開闢新路徑。

要不然樣式之間會相互污染。

比如這樣

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件事:

使用beginPath()方法,重新開一個路徑
設置新線段的樣式(必須項)

如果上面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()描邊矩形

strokeStyle:設置描邊的屬性(顏色、漸變、圖案)
strokeRect(x, y, width, height):描邊矩形(x和y是矩形左上角起點;width 和 height 是矩形的寬高)
strokeStyle必須寫在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)複製代碼

x和y: 圓心坐標
r: 半徑
sAngle: 開始角度
eAngle: 結束角度
counterclockwise: 繪製方向(true: 逆時針; false: 順時針),默認 false
28.jpg

開始角度和結束角度,都是以弧度為單位。例如 180°就寫成Math.PI,360°寫成Math.PI * 2,以此類推。

在實際開發中,為了讓自己或者別的開發者更容易看懂弧度的數值,1°應該寫成Math.PI / 180。

100°:100 * Math.PI / 180
110°:110 * Math.PI / 180
241°:241 * 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: 結束點的縱坐標
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='屬性值'複製代碼

屬性值包括:

butt: 默認值,無線帽
square: 方形線帽
round: 圓形線帽

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='屬性值'複製代碼

屬性值包括:

miter: 默認值,尖角
round: 圓角
bevel: 斜角

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種情況

只傳1個值
有2個值
有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)複製代碼

text: 字符串,要繪製的內容
x: 橫坐標,文本左邊要對齊的坐標(默認左對齊)
y: 縱坐標,文本底邊要對齊的坐標
maxWidth: 可選參數,表示文本渲染的最大寬度(px),如果文本超出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個值可選

start: 默認。在指定位置的橫坐標開始。
end: 在指定坐標的橫坐標結束。
left: 左對齊。
right: 右對齊。
center: 居中對齊。

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可選屬性:

alphabetic: 默認。文本基線是普通的字母基線。
top: 文本基線是em方框的頂端。
bottom: 文本基線是em方框的底端。
middle: 文本基線是em方框的正中。
hanging: 文本基線是懸掛基線。

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)複製代碼

image: 要渲染的圖片對象。
dx: 圖片左上角的橫坐標位置。
dy: 圖片左上角的縱坐標位置。
JS版

在JS里加載圖片並渲染,有以下幾個步驟:

創建Image對象
引入圖片
等待圖片加載完成
使用drawImage()方法渲染圖片

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)複製代碼

以上參數缺一不可

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本 精選的前端電子書!

點個在看支持我吧,轉發就更好了
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()