本文簡介
點贊 + 關注 + 收藏 = 學會了
在前端領域世分,如果只是懂 Vue
或者 React
,未來在職場的競爭力可能會比較弱狭魂。
根據(jù)我多年在家待業(yè)經(jīng)驗來看罚攀,前端未來在 數(shù)據(jù)可視化
和 AI
這兩個領域會比較香,而 Canvas
是數(shù)據(jù)可視化在前端方面的基礎技術雌澄。
本文就用光的速度將 canvas
給入門了。
要入門一個技術杯瞻,前期最重要是快镐牺!所以本文只講入門內(nèi)容,能應付簡單項目魁莉。深入的知識點會在其他文章講解睬涧。
Canvas 是什么?
-
Canvas
中文名叫 “畫布”旗唁,是HTML5
新增的一個標簽畦浓。 -
Canvas
允許開發(fā)者通過JS
在這個標簽上繪制各種圖案。 -
Canvas
擁有多種繪制路徑检疫、矩形讶请、圓形、字符以及圖片的方法屎媳。 -
Canvas
在某些情況下可以 “代替” 圖片夺溢。 -
Canvas
可用于動畫论巍、游戲、數(shù)據(jù)可視化风响、圖片編輯器嘉汰、實時視頻處理等領域。
Canvas 和 SVG 的區(qū)別
Canvas | SVG |
---|---|
用JS動態(tài)生成元素(一個HTML元素) | 用XML描述元素(類似HTML元素那樣状勤,可用多個元素來描述一個圖形) |
位圖(受屏幕分辨率影響) | 矢量圖(不受屏幕分辨率影響) |
不支持事件 | 支持事件 |
數(shù)據(jù)發(fā)生變化需要重繪 | 不需要重繪 |
就上面的描述而言可能有點難懂鞋怀,你可以打開 AntV
旗下的圖形編輯引擎做對比。G6 是使用 canvas
開發(fā)的持搜,X6 是使用 svg
開發(fā)的密似。
我的建議是:如果要展示的數(shù)據(jù)量比較大,比如一條數(shù)據(jù)就是一個元素節(jié)點朵诫,那使用 canvas
會比較合適辛友;如果用戶操作的交互比較多,而且對清晰度有要求(矢量圖)剪返,那么使用 svg
會比較合適废累。
起步
學習前端一定要動手敲代碼,然后看效果展示脱盲。
起步階段會用幾句代碼說明 canvas
如何使用邑滨,本例會畫一條直線。
畫條直線
- 在
HTML
中創(chuàng)建canvas
元素 - 通過
js
獲取canvas
標簽 - 從
canvas
標簽中獲取到繪圖工具 - 通過繪圖工具钱反,在
canvas
標簽上繪制圖形
<!-- 1掖看、創(chuàng)建 canvas 元素 -->
<canvas
id="c"
width="300"
height="200"
style="border: 1px solid #ccc;"
></canvas>
<script>
// 2、獲取 canvas 對象
const cnv = document.getElementById('c')
// 3面哥、獲取 canvas 上下文環(huán)境對象
const cxt = 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
兩個屬性特石,可設置它的寬高。
需要注意的是鳖链,這兩個屬性只需傳入數(shù)值姆蘸,不需要傳入單位(比如 px
等)。
<canvas width="600" height="400"></canvas>
3、不能通過 CSS 設置畫布的寬高
使用 css
設置 canvas
的寬高乞旦,會出現(xiàn) 內(nèi)容被拉伸 的后果T裟隆!兰粉!
<style>
#c {
width: 400px;
height: 400px;
border: 1px solid #ccc;
}
</style>
<canvas id="c"></canvas>
<script>
// 1故痊、獲取canvas對象
const cnv = document.getElementById('c')
// 2、獲取canvas上下文環(huán)境對象
const cxt = cnv.getContext('2d')
// 3玖姑、繪制圖形
cxt.moveTo(100, 100) // 起點
cxt.lineTo(200, 100) // 終點
cxt.stroke() // 將起點和終點連接起來
console.log(cnv.width) // 獲取 canvas 的寬度愕秫,輸出:300
console.log(cnv.height) // 獲取 canvas 的高度,輸出:150
</script>
canvas
的默認寬度是300px焰络,默認高度是150px戴甩。
- 如果使用
css
修改canvas
的寬高(比如本例變成 400px * 400px),那寬度就由 300px 拉伸到 400px闪彼,高度由 150px 拉伸到 400px甜孤。 - 使用
js
獲取canvas
的寬高,此時返回的是canvas
的默認值畏腕。
最后出現(xiàn)的效果如上圖所示缴川。
4、線條默認寬度和顏色
線條的默認寬度是 1px
描馅,默認顏色是黑色遭居。
但由于默認情況下 canvas
會將線條的中心點和像素的底部對齊茫舶,所以會導致顯示效果是 2px
和非純黑色問題氛驮。
5箭窜、IE兼容性高
暫時只有 IE 9
以上才支持 canvas
。但好消息是 IE
已經(jīng)有自己的墓碑了嘹狞。
如需兼容 IE 7 和 8
岂膳,可以使用 ExplorerCanvas 。但即使是使用了 ExplorerCanvas
仍然會有所限制磅网,比如無法使用 fillText()
方法等闷营。
基礎圖形
坐標系
在繪制基礎圖形之前,需要先搞清除 Canvas
使用的坐標系知市。
Canvas
使用的是 W3C 坐標系 ,也就是遵循我們屏幕速蕊、報紙的閱讀習慣嫂丙,從上往下,從左往右规哲。
W3C 坐標系 和 數(shù)學直角坐標系 的 X軸
是一樣的跟啤,只是 Y軸
的反向相反。
W3C 坐標系 的 Y軸
正方向向下。
直線
一條直線
最簡單的起步方式是畫一條直線隅肥。這里所說的 “直線” 是幾何學里的 “線段” 的意思竿奏。
需要用到這3個方法:
-
moveTo(x1, y1)
:起點坐標 (x, y) -
lineTo(x2, y2)
:下一個點的坐標 (x, y) -
stroke()
:將所有坐標用一條線連起來
起步階段可以先這樣理解。
<canvas id="c" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// 繪制直線
cxt.moveTo(50, 100) // 起點坐標
cxt.lineTo(200, 50) // 下一個點的坐標
cxt.stroke() // 將上面的坐標用一條線連接起來
</script>
上面的代碼所呈現(xiàn)的效果腥放,可以看下圖解釋(手不太聰明泛啸,畫得不是很標準,希望能看懂)
多條直線
如需畫多條直線秃症,可以用會上面那幾個方法候址。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = 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軸
的值是有小數(shù)點。
答:默認情況下 canvas
會將線條的中心點和像素的底部對齊聚请,所以會導致顯示效果是 2px
和非純黑色問題荠雕。
上圖每個格子代表 1px
。
線的中心點會和畫布像素點的底部對齊驶赏,所以會線中間是黑色的炸卑,但由于一個像素就不能再切割了,所以會有半個像素被染色母市,就變成了淺灰色矾兜。
所以如果你設置的 Y軸
值是一個整數(shù),就會出現(xiàn)上面那種情況患久。
設置樣式
-
lineWidth
:線的粗細 -
strokeStyle
:線的顏色 -
lineCap
:線帽:默認:butt
; 圓形:round
; 方形:square
<canvas id="c" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// 繪制直線
cxt.moveTo(50, 50)
cxt.lineTo(200, 50)
// 修改直線的寬度
cxt.lineWidth = 20
// 修改直線的顏色
cxt.strokeStyle = 'pink'
// 修改直線兩端樣式
cxt.lineCap = 'round' // 默認: butt; 圓形: round; 方形: square
cxt.stroke()
</script>
新開路徑
開辟新路徑的方法:
beginPath()
在繪制多條線段的同時椅寺,還要設置線段樣式,通常需要開辟新路徑蒋失。
要不然樣式之間會相互污染返帕。
比如這樣
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// 第一條線
cxt.moveTo(20, 100)
cxt.lineTo(200, 100)
cxt.lineWidth = 10
cxt.strokeStyle = 'pink'
cxt.stroke()
// 第二條線
cxt.moveTo(20, 120.5)
cxt.lineTo(200, 120.5)
cxt.stroke()
</script>
如果不想相互污染,需要做2件事:
- 使用
beginPath()
方法篙挽,重新開一個路徑 - 設置新線段的樣式(必須項)
如果上面2步卻了其中1步都會有影響荆萤。
只使用 beginPath()
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// 第一條線
cxt.moveTo(20, 100)
cxt.lineTo(200, 100)
cxt.lineWidth = 10
cxt.strokeStyle = 'pink'
cxt.stroke()
// 第二條線
cxt.beginPath() // 重新開啟一個路徑
cxt.moveTo(20, 120.5)
cxt.lineTo(200, 120.5)
cxt.stroke()
</script>
第一條線的樣式會影響之后的線。
但如果使用了 beginPath()
铣卡,后面的線段不會影響前面的線段链韭。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = 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 = 4
cxt.strokeStyle = 'red'
cxt.stroke()
</script>
設置新線段的樣式,沒使用 beginPath()
的情況
這個情況會反過來煮落,后面的線能影響前面的線敞峭。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// 第一條線
cxt.moveTo(20, 100)
cxt.lineTo(200, 100)
cxt.lineWidth = 10
cxt.strokeStyle = 'pink'
cxt.stroke()
// 第二條線
cxt.moveTo(20, 120.5)
cxt.lineTo(200, 120.5)
cxt.lineWidth = 4
cxt.strokeStyle = 'red'
cxt.stroke()
</script>
正確的做法
在設置 beginPath()
的同時,也各自設置樣式蝉仇。這樣就能做到相互不影響了旋讹。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.moveTo(20, 100)
cxt.lineTo(200, 100)
cxt.lineWidth = 10
cxt.strokeStyle = 'pink'
cxt.stroke()
cxt.beginPath() // 重新開啟一個路徑
cxt.moveTo(20, 120.5)
cxt.lineTo(200, 120.5)
cxt.lineWidth = 4
cxt.strokeStyle = 'red'
cxt.stroke()
</script>
折線
和 直線 差不多殖蚕,都是使用 moveTo()
、lineTo()
和 stroke()
方法可以繪制折線沉迹。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.moveTo(50, 200)
cxt.lineTo(100, 50)
cxt.lineTo(200, 200)
cxt.lineTo(250, 50)
cxt.stroke()
</script>
畫這種折線睦疫,最好在草稿紙上畫一個坐標系,自己計算并描繪一下每個點大概在什么什么位置鞭呕,最后在 canvas
中看看效果蛤育。
矩形
根據(jù)前面的基礎,我們可以 使用線段來描繪矩形琅拌,但 canvas
也提供了 rect()
等方法可以直接生成矩形缨伊。
使用線段描繪矩形
可以使用前面畫線段的方法來繪制矩形
canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = 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>
上面的代碼幾個點分別對應下圖刻坊。
使用 strokeRect()
描邊矩形
-
strokeStyle
:設置描邊的屬性(顏色、漸變党晋、圖案) -
strokeRect(x, y, width, height)
:描邊矩形(x和y是矩形左上角起點谭胚;width 和 height 是矩形的寬高) -
strokeStyle
必須寫在strokeRect()
前面,不然樣式不生效未玻。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// strokeStyle 屬性
// strokeRect(x, y, width, height) 方法
cxt.strokeStyle = 'pink'
cxt.strokeRect(50, 50, 200, 100)
</script>
上面的代碼可以這樣理解
使用 fillRect()
填充矩形
fillRect()
和 strokeRect()
方法差不多灾而,但 fillRect()
的作用是填充。
需要注意的是扳剿,fillStyle
必須寫在 fillRect()
之前旁趟,不然樣式不生效。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = 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()
會產(chǎn)生描邊和填充的效果
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = 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()
的用法差不多锡搜,唯一的區(qū)別是:
strokeRect()
和 fillRect()
這兩個方法調用后會立即繪制;rect()
方法被調用后瞧掺,不會立刻繪制矩形耕餐,而是需要調用 stroke()
或 fill()
輔助渲染。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = 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()
方法可以清空指定區(qū)域辟狈。
clearRect(x, y, width, height)
其語法和創(chuàng)建 cxt.rect()
差不多肠缔。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = 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)
要清空的區(qū)域:從畫布左上角開始明未,直到畫布的寬和畫布的高為止。
多邊形
Canvas
要畫多邊形壹蔓,需要使用 moveTo()
亚隅、 lineTo()
和 closePath()
。
三角形
雖然三角形是常見圖形庶溶,但 canvas
并沒有提供類似 rect()
的方法來繪制三角形煮纵。
需要確定三角形3個點的坐標位置,然后使用 stroke()
或者 fill()
方法生成三角形偏螺。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = 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
就會有問題销部,比如:
// 省略部分代碼
cxt.lineWidth = 20
當線段變粗后,起始點和結束點的鏈接處制跟,拐角就出現(xiàn)“不正尘俗”現(xiàn)象。
如果需要真正閉合雨膨,可以使用 closePath()
方法擂涛。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = 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 = 20
cxt.stroke()
</script>
使用 cxt.closePath()
可以自動將終點和起始點連接起來聊记,此時看上去就正常多了撒妈。
菱形
有一組鄰邊相等的平行四邊形是菱形
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = 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
開始角度和結束角度附鸽,都是以弧度為單位。例如 180°就寫成 Math.PI
瞒瘸,360°寫成 Math.PI * 2
坷备,以此類推。
在實際開發(fā)中情臭,為了讓自己或者別的開發(fā)者更容易看懂弧度的數(shù)值省撑,1°應該寫成 Math.PI / 180
赌蔑。
- 100°:
100 * Math.PI / 180
- 110°:
110 * Math.PI / 180
- 241°:
241 * Math.PI / 180
注意:繪制圓形之前,必須先調用 beginPath()
方法>癸M薰摺! 在繪制完成之后肥败,還需要調用 closePath()
方法V呵场!馒稍!
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.beginPath()
cxt.arc(150, 150, 80, 0, 360)
cxt.closePath()
cxt.stroke()
</script>
半圓
如果使用 arc()
方法畫圓時皿哨,沒做到剛好繞完一周(360°)就直接閉合路徑,就會出現(xiàn)半圓的狀態(tài)纽谒。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.beginPath()
cxt.arc(150, 150, 100, 0, 180 * Math.PI / 180) // 順時針
cxt.closePath()
cxt.stroke()
</script>
上面的代碼中证膨,cxt.arc
最后一個參數(shù)沒傳,默認是 false
佛舱,所以是順時針繪制椎例。
如果希望半圓的弧面在上方,可以將 cxt.arc
最后一個參數(shù)設置成 true
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.beginPath()
cxt.arc(150, 150, 100, 0, 180 * Math.PI / 180, true)
cxt.closePath()
cxt.stroke()
</script>
弧線
使用 arc()
方法畫半圓時请祖,如果最后不調用 closePath()
方法订歪,就不會出現(xiàn)閉合路徑。也就是說肆捕,那是一條弧線刷晋。
在 canvas
中,畫弧線有2中方法:arc()
和 arcTo()
慎陵。
arc() 畫弧線
如果想畫一條 0° ~ 30°
的弧線眼虱,可以這樣寫
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.beginPath()
cxt.arc(150, 150, 100, 0, 30 * Math.PI / 180)
cxt.stroke()
</script>
原理如下圖所示,紅線代表畫出來的那條弧線席纽。
arcTo() 畫弧線
arcTo()
的使用方法會更加復雜捏悬,如果初學看不太懂的話可以先跳過,看完后面的再回來補補润梯。
語法:
arcTo(cx, cy, x2, y2, radius)
-
cx
: 兩切線交點的橫坐標 -
cy
: 兩切線交點的縱坐標 -
x2
: 結束點的橫坐標 -
y2
: 結束點的縱坐標 -
radius
: 半徑
其中过牙,(cx, cy)
也叫控制點,(x2, y2)
也叫結束點纺铭。
是不是有點奇怪寇钉,為什么沒有 x1
和 y1
?
(x1, y1)
是開始點舶赔,通常是由 moveTo()
或者 lineTo()
提供扫倡。
arcTo()
方法利用 開始點、控制點和結束點形成的家教竟纳,繪制一段與家教的兩邊相切并且半徑為 radius
的圓弧撵溃。
舉個例子
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.moveTo(40, 40)
cxt.arcTo(120, 40, 120, 80, 80)
cxt.stroke()
</script>
基礎樣式
前面學完基礎圖形疚鲤,接下來可以開始了解一下如何設置元素的基礎樣式。
描邊 stroke()
前面的案例中征懈,其實已經(jīng)知道使用 stroke()
方法進行描邊了石咬。這里就不再多講這個方法。
線條寬度 lineWidth
lineWidth
默認值是 1
卖哎,默認單位是 px
。
語法:
lineWidth = 線寬
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// 線寬 10
cxt.beginPath()
cxt.moveTo(50, 50)
cxt.lineTo(250, 50)
cxt.lineWidth = 10 // 設置線寬
cxt.stroke()
// 線寬 20
cxt.beginPath()
cxt.moveTo(50, 150)
cxt.lineTo(250, 150)
cxt.lineWidth = 20 // 設置線寬
cxt.stroke()
// 線寬 30
cxt.beginPath()
cxt.moveTo(50, 250)
cxt.lineTo(250, 250)
cxt.lineWidth = 30 // 設置線寬
cxt.stroke()
</script>
線條顏色 strokeStyle
使用 strokeStyle
可以設置線條顏色
語法:
strokeStyle = 顏色值
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.moveTo(50, 50)
cxt.lineTo(250, 50)
cxt.lineWidth = 20
cxt.strokeStyle = 'pink' // 設置顏色
cxt.stroke()
</script>
為了展示方便删性,我將 lineWidth
設為 20亏娜。
線帽 lineCap
線帽指的是線段的開始和結尾處的樣式,使用 lineCap
可以設置
語法:
lineCap = '屬性值'
屬性值包括:
-
butt
: 默認值蹬挺,無線帽 -
square
: 方形線帽 -
round
: 圓形線帽
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// 設置線寬维贺,方便演示
cxt.lineWidth = 16
// 默認線帽 butt
cxt.beginPath()
cxt.moveTo(50, 60)
cxt.lineTo(250, 60)
cxt.stroke()
// 方形線帽 square
cxt.beginPath()
cxt.lineCap = 'square'
cxt.moveTo(50, 150)
cxt.lineTo(250, 150)
cxt.stroke()
// 圓形線帽 round
cxt.beginPath()
cxt.lineCap = 'round'
cxt.moveTo(50, 250)
cxt.lineTo(250, 250)
cxt.stroke()
</script>
使用 square
和 round
的話,會使線條變得稍微長一點點巴帮,這是給線條增加線帽的部分溯泣,這個長度在日常開發(fā)中需要注意。
線帽只對線條的開始和結尾處產(chǎn)生作用榕茧,對拐角不會產(chǎn)生任何作用垃沦。
拐角樣式 lineJoin
如果需要設置拐角樣式,可以使用 lineJoin
用押。
語法:
lineJoin = '屬性值'
屬性值包括:
-
miter
: 默認值肢簿,尖角 -
round
: 圓角 -
bevel
: 斜角
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.lineWidth = 20
// 默認,尖角
cxt.moveTo(50, 40)
cxt.lineTo(200, 40)
cxt.lineTo(200, 90)
cxt.stroke()
// 斜角 bevel
cxt.beginPath()
cxt.moveTo(50, 140)
cxt.lineTo(200, 140)
cxt.lineTo(200, 190)
cxt.lineJoin = 'bevel'
cxt.stroke()
// 圓角 round
cxt.beginPath()
cxt.moveTo(50, 240)
cxt.lineTo(200, 240)
cxt.lineTo(200, 290)
cxt.lineJoin = 'round'
cxt.stroke()
</script>
虛線 setLineDash()
使用 setLineDash()
方法可以將描邊設置成虛線蜻拨。
語法:
setLineDash([])
需要傳入一個數(shù)組池充,且元素是數(shù)值型。
虛線分3種情況
- 只傳1個值
- 有2個值
- 有3個以上的值
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.lineWidth = 20
cxt.strokeStyle = 'pink'
cxt.moveTo(50, 50)
cxt.lineTo(200, 50)
cxt.setLineDash([10]) // 只傳1個參數(shù)缎讼,實線與空白都是 10px
cxt.stroke()
cxt.beginPath()
cxt.moveTo(50, 100)
cxt.lineTo(200, 100)
cxt.setLineDash([10, 20]) // 2個參數(shù)收夸,此時,實線是 10px, 空白 20px
cxt.stroke()
cxt.beginPath()
cxt.moveTo(50, 150)
cxt.lineTo(200, 150)
cxt.setLineDash([10, 20, 5]) // 傳3個以上的參數(shù)血崭,此例:10px實線卧惜,20px空白,5px實線功氨,10px空白序苏,20px實線,5px空白 ……
cxt.stroke()
</script>
此外捷凄,還可以始終 cxt.getLineDash()
獲取虛線不重復的距離忱详;
用 cxt.lineDashOffset
設置虛線的偏移位。
填充
使用 fill()
可以填充圖形跺涤,根據(jù)前面的例子應該掌握了如何使用 fill()
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.fillStyle = 'pink'
cxt.rect(50, 50, 200, 100)
cxt.fill()
</script>
可以使用 fillStyle
設置填充顏色匈睁,默認是黑色监透。
非零環(huán)繞填充
在使用 fill()
方法填充時,需要注意一個規(guī)則:非零環(huán)繞填充航唆。
在使用 moveTo
和 lineTo
描述圖形時胀蛮,如果是按順時針繪制,計數(shù)器會加1糯钙;如果是逆時針粪狼,計數(shù)器會減1。
當圖形所處的位置任岸,計數(shù)器的結果為0時再榄,它就不會被填充。
這樣說有點復雜享潜,先看看例子
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// 外層矩形
cxt.moveTo(50, 50)
cxt.lineTo(250, 50)
cxt.lineTo(250, 250)
cxt.lineTo(50, 250)
cxt.closePath()
// 內(nèi)層矩形
cxt.moveTo(200, 100)
cxt.lineTo(100, 100)
cxt.lineTo(100, 200)
cxt.lineTo(200, 200)
cxt.closePath()
cxt.fill()
</script>
請看看上面的代碼困鸥,我畫了2個矩形,它們都沒有用 beginPath()
方法開辟新路徑剑按。
內(nèi)層矩形是逆時針繪制的疾就,所以內(nèi)層的值是 -1
,它又經(jīng)過外層矩形艺蝴,而外層矩形是順時針繪制猬腰,所以經(jīng)過外層時值 +1
,最終內(nèi)層的值為 0
吴趴,所以不會被填充漆诽。
文本
Canvas
提供了一些操作文本的方法。
為了方便演示锣枝,我們先了解一下在 Canvas
中如何給本文設置樣式厢拭。
樣式 font
和 CSS
設置 font
差不多,Canvas
也可以通過 font
設置樣式撇叁。
語法:
cxt.font = 'font-style font-variant font-weight font-size/line-height font-family'
如果需要設置字號 font-size
供鸠,需要同事設置 font-family
。
cxt.font = '30px 宋體'
描邊 strokeText()
使用 strokeText()
方法進行文本描邊
語法:
strokeText(text, x, y, maxWidth)
-
text
: 字符串陨闹,要繪制的內(nèi)容 -
x
: 橫坐標楞捂,文本左邊要對齊的坐標(默認左對齊) -
y
: 縱坐標,文本底邊要對齊的坐標 -
maxWidth
: 可選參數(shù)趋厉,表示文本渲染的最大寬度(px)寨闹,如果文本超出maxWidth
設置的值,文本會被壓縮君账。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.font = '60px Arial' // 將字號設置成 60px繁堡,方便觀察
cxt.strokeText('雷猴', 30, 90)
</script>
設置描邊顏色 strokeStyle
使用 strokeStyle
設置描邊顏色。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.font = '60px Arial' // 將字號設置成 60px,方便觀察
cxt.strokeStyle = 'pink' // 設置文本描邊顏色
cxt.strokeText('雷猴', 30, 90)
</script>
填充 fillText
使用 fillText()
可填充文本椭蹄。
語法和 strokeText()
一樣闻牡。
fillText(text, x, y, maxWidth)
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.font = '60px Arial'
cxt.fillText('雷猴', 30, 90)
</script>
設置填充顏色 fillStyle
使用 fillStyle
可以設置文本填充顏色。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
cxt.font = '60px Arial'
cxt.fillStyle = 'pink'
cxt.fillText('雷猴', 30, 90)
</script>
獲取文本長度 measureText()
measureText().width
方法可以獲取文本的長度绳矩,單位是 px
罩润。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
let text = '雷猴'
cxt.font = 'bold 40px Arial'
cxt.fillText(text, 40, 80)
console.log(cxt.measureText(text).width) // 80
</script>
水平對齊方式 textAlign
使用 textAlign
屬性可以設置文字的水平對齊方式,一共有5個值可選
-
start
: 默認翼馆。在指定位置的橫坐標開始割以。 -
end
: 在指定坐標的橫坐標結束。 -
left
: 左對齊应媚。 -
right
: 右對齊拳球。 -
center
: 居中對齊。
紅線是輔助參考線珍特。
<canvas id="c" width="400" height="400" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// 豎向的輔助線(參考線,在畫布中間)
cxt.moveTo(200, 0)
cxt.lineTo(200, 400)
cxt.strokeStyle = 'red'
cxt.stroke()
cxt.font = '30px Arial'
// 橫坐標開始位對齊
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
也好像是一樣的。
在大多數(shù)情況下酬姆,它們的確一樣嗜桌。但在某些國家或者某些場合,閱讀文字的習慣是 從右往左 時辞色,start
就和 right
一樣了骨宠,end
和 left
也一樣。這是需要注意的地方相满。
垂直對齊方式 textBaseline
使用 textBaseline
屬性可以設置文字的垂直對齊方式层亿。
在使用 textBaseline
前,需要自行了解 css
的文本基線立美。
用一張網(wǎng)圖解釋一下基線
textBaseline
可選屬性:
-
alphabetic
: 默認匿又。文本基線是普通的字母基線。 -
top
: 文本基線是em
方框的頂端建蹄。 -
bottom
: 文本基線是em
方框的底端碌更。 -
middle
: 文本基線是em
方框的正中幻馁。 -
hanging
: 文本基線是懸掛基線猪勇。
紅線是輔助參考線创南。
<canvas id="c" width="800" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// 橫向的輔助線(參考線屋剑,在畫布中間)
cxt.moveTo(0, 150)
cxt.lineTo(800, 150)
cxt.strokeStyle = 'red'
cxt.stroke()
cxt.font = '20px Arial'
// 默認 alphabetic
cxt.textBaseline = 'alphabetic'
cxt.fillText('雷猴 alphabetic', 10, 150)
// 默認 top
cxt.textBaseline = 'top'
cxt.fillText('雷猴 top', 200, 150)
// 默認 bottom
cxt.textBaseline = 'bottom'
cxt.fillText('雷猴 bottom', 320, 150)
// 默認 middle
cxt.textBaseline = 'middle'
cxt.fillText('雷猴 middle', 480, 150)
// 默認 hanging
cxt.textBaseline = 'hanging'
cxt.fillText('雷猴 hanging', 640, 150)
</script>
注意:在繪制文字的時候铲咨,默認是以文字的左下角作為參考點進行繪制
圖片
在 Canvas
中可以使用 drawImage()
方法繪制圖片打洼。
渲染圖片
渲染圖片的方式有2中严拒,一種是在JS里加載圖片再渲染霹期,另一種是把DOM里的圖片拿到 canvas
里渲染。
渲染的語法:
drawImage(image, dx, dy)
-
image
: 要渲染的圖片對象快压。 -
dx
: 圖片左上角的橫坐標位置圆仔。 -
dy
: 圖片左上角的縱坐標位置。
JS版
在 JS
里加載圖片并渲染蔫劣,有以下幾個步驟:
- 創(chuàng)建
Image
對象 - 引入圖片
- 等待圖片加載完成
- 使用
drawImage()
方法渲染圖片
<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
// 1 創(chuàng)建 Image 對象
const image = new Image()
// 2 引入圖片
image.src = './images/dog.jpg'
// 3 等待圖片加載完成
image.onload = () => {
// 4 使用 drawImage() 方法渲染圖片
cxt.drawImage(image, 30, 30)
}
</script>
DOM版
<style>
#dogImg {
display: none;
}
</style>
<img src="./images/dog.jpg" id="dogImg"/>
<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
const image = document.getElementById('dogImg')
cxt.drawImage(image, 70, 70)
</script>
因為圖片是從 DOM
里獲取到的坪郭,所以一般來說,只要在 window.onload
這個生命周期內(nèi)使用 drawImage
都可以正常渲染圖片脉幢。
本例使用了 css
的方式歪沃,把圖片的 display
設置成 none
。因為我不想被 <img>
影響到本例講解嫌松。
實際開發(fā)過程中按照實際情況設置即可沪曙。
設置圖片寬高
前面的例子都是直接加載圖片,圖片默認的寬高是多少就加載多少萎羔。
如果需要指定圖片寬高液走,可以在前面的基礎上再添加兩個參數(shù):
drawImage(image, dx, dy, dw, dh)
image、 dx贾陷、 dy
的用法和前面一樣缘眶。
dw
用來定義圖片的寬度,dy
定義圖片的高度髓废。
<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
const image = new Image()
image.src = './images/dog.jpg'
image.onload = () => {
cxt.drawImage(image, 30, 30, 100, 100)
}
</script>
我把圖片的尺寸設為 100px * 100px巷懈,圖片看上去比之前就小了很多。
截取圖片
截圖圖片同樣使用drawImage()
方法慌洪,只不過傳入的參數(shù)數(shù)量比之前都多顶燕,而且順序也有點不一樣了。
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
以上參數(shù)缺一不可
-
image
: 圖片對象 -
sx
: 開始截取的橫坐標 -
sy
: 開始截取的縱坐標 -
sw
: 截取的寬度 -
sh
: 截取的高度 -
dx
: 圖片左上角的橫坐標位置 -
dy
: 圖片左上角的縱坐標位置 -
dw
: 圖片寬度 -
dh
: 圖片高度
<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv = document.getElementById('c')
const cxt = cnv.getContext('2d')
const image = new Image()
image.src = './images/dog.jpg'
image.onload = () => {
cxt.drawImage(image, 0, 0, 100, 100, 30, 30, 200, 200)
}
</script>
總結
本文主要講解了在 Canvas
中繪制一些基礎圖形冈爹,還有一些基礎樣式設置涌攻。
還有更多高級的玩法會在之后的文章中講到,比如漸變犯助、投影癣漆、濾鏡等等。
代碼倉庫
推薦閱讀
點贊 + 關注 + 收藏 = 學會了
點贊 + 關注 + 收藏 = 學會了