前端之學(xué)習(xí) canvas 的基礎(chǔ)語法
前言
今天坞生,學(xué)習(xí)了 canvas 的一些語法知識和使用计济,在這里分享給大家一起學(xué)習(xí)交流
首先粱年,我們應(yīng)該了解 canvas 是什么钦睡,有什么用逆皮,文章最后振诬,我們將使用 canvas 繪制一個(gè)刮刮卡的中獎特效蘸吓!
什么是 canvas
定義:是 HTML5 提供的一種新標(biāo)簽, ie9 才開始支持的群扶,Canvas 是一個(gè)矩形區(qū)域的畫布透葛,可以用 JS 控制每一個(gè)像素在上面繪畫笨使。canvas 標(biāo)簽使用 JavaScript 在網(wǎng)頁上繪制圖像,本身不具備繪圖功能僚害。canvas 擁有多種繪制路徑硫椰、矩形、圓形萨蚕、字符以及添加圖像的方法靶草。
vscode 代碼提示
在了解 canvas 之后,大家一定迫不及待地想加入 canvas 的學(xué)習(xí)之路吧岳遥。
- 首先奕翔,我們使用 vscode 新建一個(gè) html 文件,
并在body
標(biāo)簽中使用 canvas 標(biāo)簽浩蓉,給定一定的大小派继,這樣,canvas 畫布就建好了
<canvas class="canvas" width="600" height="400"></canvas>
- 在
script
標(biāo)簽中捻艳,開始使用我們神奇的畫筆驾窟,工欲善其事必先利其器,開始之前认轨,我們使用/** @type {HTMLCanvasElement} */
,vscode 會給出 canvas 相關(guān) api 的智能提示绅络,讓我們更順暢的完成我們的代碼
// 獲取畫布
/** @type {HTMLCanvasElement} */
let canvas = document.querySelector('.canvas')
// 獲取畫布上下文對象
let ctx = canvas.getContext('2d')
繪制二次貝塞爾曲線
首先,我們使用貝塞爾曲線繪制一個(gè)聊天氣泡
在 canvas 中,一般相鄰兩個(gè)值為一組對象恩急,代表 x 坐標(biāo)和 y 坐標(biāo)杉畜,而瀏覽器左上角即為原點(diǎn)位置
比如:
ctx.moveTo(200, 300)
中 200 和 300 分別代表 x 軸和 y 軸坐標(biāo),即表示一個(gè)點(diǎn)
ctx.quadraticCurveTo(150, 300, 150, 200)
中兩兩為一組衷恭,即代表了兩個(gè)點(diǎn)的坐標(biāo)
二次貝塞爾曲線通過和起始點(diǎn)的三個(gè)點(diǎn)既可以畫出一條圓滑的曲線,如下圖:
// 畫筆移動到指定位置
ctx.moveTo(200, 300)
// 開始畫筆
ctx.beginPath()
// 繪制多條貝塞爾曲線
ctx.quadraticCurveTo(150, 300, 150, 200)
ctx.quadraticCurveTo(150, 100, 300, 100)
ctx.quadraticCurveTo(450, 100, 450, 200)
ctx.quadraticCurveTo(450, 300, 250, 300)
ctx.quadraticCurveTo(250, 350, 150, 350)
ctx.quadraticCurveTo(200, 350, 200, 300)
// 填充顏色
ctx.stroke()
// 結(jié)束畫筆
ctx.closePath()
這樣此叠,一個(gè)聊天氣泡框就畫好了,后面我們還會學(xué)習(xí)如何修改顏色或填充顏色
繪制三次貝塞爾曲線
三次貝塞爾曲線和二次貝塞爾曲線類似匾荆,不同的是每次使用四個(gè)點(diǎn)完成一條曲線的繪畫拌蜘,使用的方法名也不同
接下來,我們使用三次貝塞爾曲線繪畫一個(gè)愛心
一段三次貝塞爾曲線如下:
//開始畫筆
ctx.beginPath()
//三次貝塞爾曲線畫一個(gè)愛心
ctx.moveTo(300, 200)
ctx.bezierCurveTo(350, 150, 500, 200, 300, 350)
//開始畫筆
ctx.beginPath()
//三次貝塞爾曲線畫一個(gè)愛心
ctx.moveTo(300, 200)
ctx.bezierCurveTo(350, 150, 500, 200, 300, 350)
ctx.bezierCurveTo(100, 200, 250, 150, 300, 200)
ctx.stroke()
// 結(jié)束畫筆
ctx.closePath()
繪制文字
-
font 設(shè)置文字大小和字體
//設(shè)置文字大小和字體 ctx.font = "30px Microsoft Yahei"
-
strokeStyle 設(shè)置文字顏色
// 設(shè)置文字顏色 ctx.strokeStyle = "#ffcc2a"
-
fillText 設(shè)置文字內(nèi)容和位置
// 設(shè)置文字內(nèi)容和位置 ctx.fillText("Hello World", 100, 100)
-
textAlign 設(shè)置文字對齊方式
//設(shè)置文字對齊方式 start left center right end ctx.textAlign = "start"
-
textBaseline 文本基線對齊
//文本基線對齊 start top middle bottom end ctx.textBaseline = "middle"
-
direction 文字方向
//文字方向 rtl ltr ctx.direction = "rtl"
-
strokeText 設(shè)置文字輪廓
//設(shè)置文字輪廓 ctx.strokeText("Hello World", 300, 100)
預(yù)測量文字寬度
/*
預(yù)測量文字寬度
actualBoundingBoxAscent 實(shí)際文字上邊距
actualBoundingBoxDescent 實(shí)際文字下邊距
actualBoundingBoxLeft 實(shí)際文字左邊距
actualBoundingBoxRight 實(shí)際文字右邊距
fontBoundingBoxAscent 字體上邊距
fontBoundingBoxDescent 字體下邊距
width 文字寬度
*/
let result = ctx.measureText("Hello World")
console.log(result)
繪制圖片
繪制圖片有三種方式:
- 第一種:默認(rèn)繪制圖片的長寬
let img = new Image() img.src = "./imgs/girl.webp" img.onload = () => { // 第一種繪制圖片方式 (默認(rèn)繪制圖片的長寬) ctx.drawImage(img, 0, 0) }
- 第二種繪制圖片方式: 第一組坐標(biāo)參數(shù):繪制起點(diǎn)位置牙丽,第二組坐標(biāo)參數(shù):繪制長寬
let img = new Image()
img.src = "./imgs/girl.webp"
img.onload = () => {
//第二種繪制圖片方式 (第一組:繪制起點(diǎn)位置简卧,第二組:繪制長寬)
ctx.drawImage(img, 0, 0, 200, 400)
}
- 第三種繪制圖片方式 第一組坐標(biāo)參數(shù):裁剪起點(diǎn)位置,第二組坐標(biāo)參數(shù):裁剪長寬烤芦,第三組坐標(biāo)參數(shù):繪制起點(diǎn)位置举娩,第四組坐標(biāo)參數(shù):繪制長寬
let img = new Image()
img.src = "./imgs/girl.webp"
img.onload = () => {
//第三種繪制圖片方式(第一組:裁剪起點(diǎn)位置,第二組:裁剪長寬构罗,第三組:繪制起點(diǎn)位置铜涉,第四組:繪制長寬)
ctx.drawImage(img, 50, 50, 300, 150, 0, 0, 200, 200)
}
繪制視頻
在 body
標(biāo)簽中添加 video
標(biāo)簽和 button
按鈕控制視頻播放
<video class="video" src="./video/video.mp4" controls></video>
<button class="btn">播放/暫停</button>
//獲取視頻對象
let video = document.querySelector('.video')
//獲取按鈕
let btn = document.querySelector('.btn')
btn.onclick = () => {
//播放或暫停視頻
video.paused ? video.play() : video.pause()
render()
}
function render() {
//畫出視頻圖像
ctx.drawImage(video, 0, 0, 600, 400)
//requestAnimationFrame 會把每一幀中的所有DOM操作集中起來,在一次重繪或回流中就完成遂唧,并且重繪或回流的時(shí)間間隔緊緊跟隨瀏覽器的刷新頻率芙代,一般來說,這個(gè)頻率為每秒60幀盖彭。循環(huán)調(diào)用 render纹烹,就可以實(shí)現(xiàn)播放視頻的效果
requestAnimationFrame(render)
}
位移變換
首先,給 canvas 添加 border 樣式召边,并且繪制一個(gè)矩形
<style>
.canvas {
border: 1px solid #000;
}
</style>
<script lang="js">
/** @type {HTMLCanvasElement} */
let canvas = document.querySelector('.canvas')
//獲取畫筆上下文對象
let ctx = canvas.getContext('2d')
ctx.fillRect(0, 0, 100, 50)
</script>
canvas 的位移變化都是坐標(biāo)系的改變铺呵,即瀏覽器左上角原點(diǎn)形成的坐標(biāo)系的變化
//坐標(biāo)系位移
ctx.translate(100, 100)
// 縮放
ctx.scale(2, 2)
// 旋轉(zhuǎn)
ctx.rotate(Math.PI / 4)
通過矩陣的方式位移變換
//平移 1,0,0,1分別代表x軸和y軸的縮放比例,200,200分別代表x軸和y軸的位移
ctx.transform(1, 0, 0, 1, 200, 200)
//旋轉(zhuǎn) transform(Math.cos(Math.PI / 4), Math.sin(Math.PI / 4), -Math.sin(Math.PI / 4), Math.cos(Math.PI / 4), 0, 0)
ctx.transform(1, 1, -1, 1, 0, 0)
//縮放 2,2分別代表x軸和y軸的縮放比例
ctx.transform(2, 0, 0, 2, 0, 0)
線段和虛線樣式設(shè)置
繪制線段
ctx.moveTo(200, 200)
// 繪制直線
ctx.lineTo(350, 250)
// 繪制直線
ctx.lineTo(440, 200)
繪制虛線
繪制虛線先繪制線段隧熙,再設(shè)置讓線段分割成為虛線
ctx.moveTo(200, 200)
// 繪制直線
ctx.lineTo(350, 250)
// 繪制直線
ctx.lineTo(440, 200)
//虛線樣式
ctx.setLineDash([20, 20])
//虛線偏移量
ctx.lineDashOffset = index
使用 render 方法讓虛線移動
let index = 0
//線段移動
function render() {
//清除上次繪畫的矩形
ctx.clearRect(0, 0, 600, 400)
index++
if (index > 400) {
index = 0
}
ctx.moveTo(200, 200)
// 繪制直線
ctx.lineTo(350, 250)
// 繪制直線
ctx.lineTo(440, 200)
// 設(shè)置線條樣式
ctx.lineWidth = 1
// 設(shè)置線條端點(diǎn)樣式片挂,round為圓形,square為方形贞盯,butt為直角
ctx.lineCap = 'butt'
//設(shè)置線段連接處樣式音念,round為圓形,bevel為斜角躏敢,mitter為直角
ctx.linejoin = 'mitter'
// 設(shè)置線段連接處最大長度
ctx.miterLimit = 6
//虛線樣式
ctx.setLineDash([20, 20])
//虛線偏移量
ctx.lineDashOffset = index
ctx.stroke()
//循環(huán)調(diào)用闷愤,實(shí)現(xiàn)動畫效果
requestAnimationFrame(render)
}
render()
陰影
陰影屬性和 css 的陰影類似
- shadowOffsetX: 陰影的水平偏移量
- shadowOffsetY: 陰影的垂直偏移量
- shadowBlur: 陰影的模糊程度
- shadowColor: 陰影的顏色
// path2D 對象 用于存儲路徑
let heartPath = new Path2D()
//二次貝塞曲線畫一個(gè)愛心
heartPath.moveTo(300, 200)
heartPath.bezierCurveTo(350, 150, 500, 200, 300, 350)
heartPath.bezierCurveTo(100, 200, 250, 150, 300, 200)
// shadowOffsetX: 陰影的水平偏移量 shadowOffsetY: 陰影的垂直偏移量 shadowBlur: 陰影的模糊程度 shadowColor: 陰影的顏色
ctx.shadowOffsetX = 10
ctx.shadowOffsetY = 10
ctx.shadowBlur = 5
ctx.shadowColor = 'rgba(255, 200, 200, 1)'
ctx.stroke(heartPath)
裁剪圖片
繪制圖片后,使用 ctx.clip()則可對圖片進(jìn)行裁剪
如下:原本圖片被愛心形狀裁剪
// path2D對象 用于創(chuàng)建路徑
let heart = new Path2D()
//二次貝塞曲線畫一個(gè)愛心
heart.moveTo(300, 200)
heart.bezierCurveTo(350, 150, 500, 200, 300, 350)
heart.bezierCurveTo(100, 200, 250, 150, 300, 200)
//clip() 方法從原始畫布中剪切任意形狀和尺寸的區(qū)域
ctx.clip(heart)
let img = new Image()
img.src = "./imgs/girl.webp"
img.onload = () => {
//第三種繪制圖片方式(第一組:裁剪起點(diǎn)位置父丰,第二組:裁剪長寬,第三組:繪制起點(diǎn)位置,第四組:繪制長寬)
ctx.drawImage(img, 80, 70, 250, 250, 200, 180, 200, 300)
ctx.lineWidth = 5
ctx.strokeStyle = "red"
ctx.stroke(heart)
}
ctx.stroke(heart)
合成圖像
使用合成圖像蛾扇,做一個(gè)刮刮卡抽獎效果
- 首先畫布 canvas 樣式和刮刮卡都定位到左上角攘烛,使用 z-index 使畫布居于抽獎文字上層
- 繪制刮刮卡圖片,遮蓋文字
- 鼠標(biāo)按下時(shí)镀首,開啟繪畫坟漱,globalCompositeOperation 屬性設(shè)置或返回如何將一個(gè)源(新的)圖像繪制到目標(biāo)(已有)的圖像上,由于屬性很多更哄,具體參考canvas 中的合成------globalCompositeOperation 一文 ,這里使用
destination-out
從畫布中刪除刮刮卡圖像芋齿,畫筆繪畫時(shí)繪畫弧形模仿橡皮差效果 - 鼠標(biāo)抬起時(shí),停止繪畫
<style>
* {
margin: 0;
padding: 0;
}
.ggk {
width: 600px;
height: 400px;
line-height: 400px;
text-align: center;
font-weight: 900;
overflow: hidden;
font-size: 30px;
position: absolute;
left: 0;
top: 0;
z-index: -1;
}
.canvas {
border: 1px solid #000;
position: absolute;
left: 0;
top: 0;
z-index: 1;
}
</style>
<body>
<div class="ggk">謝謝惠顧</div>
<canvas class="canvas" width="600" height="400"></canvas>
</body>
//繪制刮刮卡圖片
let img = new Image()
img.src = './imgs/ggk.webp'
img.onload = () => {
ctx.drawImage(img, 0, 0, 600, 400)
}
// isDraw 用來判斷是否在畫布上繪制
let isDraw = false
canvas.onmousedown = (e) => {
isDraw = true
}
canvas.onmouseup = (e) => {
isDraw = false
}
// onmousemove 在鼠標(biāo)指針移動時(shí),不斷繪畫
canvas.onmousemove = (e) => {
if (isDraw) {
// 獲取鼠標(biāo)指針的坐標(biāo)
let x = e.pageX
let y = e.pageY
// globalCompositeOperation 屬性設(shè)置或返回如何將一個(gè)源(新的)圖像繪制到目標(biāo)(已有)的圖像上
// destination-out 從目標(biāo)圖像中刪除源圖像
ctx.globalCompositeOperation = 'destination-out'
// arc() 方法使用一個(gè)中心點(diǎn)和半徑成翩,為畫布的當(dāng)前子路徑添加一條弧觅捆。
ctx.arc(x, y, 20, 0, Math.PI * 2)
ctx.fill()
}
}
let random = Math.random()
if (random < 0.5) {
let ggk = document.querySelector('.ggk')
ggk.innerHTML = '恭喜你中獎了'
}
刮刮卡刮獎前
刮刮卡刮獎后
原創(chuàng)不易,覺得文章還不錯(cuò)或者感興趣可以點(diǎn)贊或者收藏一下哦麻敌!歡迎留言評論和關(guān)注一下栅炒!