今天,学习了 canvas 的一些语法知识和使用,在这里分享给大家一起学习交流
首先,我们应该了解 canvas 是什么,有什么用,文章最后,我们将使用 canvas 绘制一个刮刮卡的中奖特效!
定义:是 HTML5 提供的一种新标签, ie9 才开始支持的,Canvas 是一个矩形区域的画布,可以用 JS 控制每一个像素在上面绘画。canvas 标签使用 JavaScript 在网页上绘制图像,本身不具备绘图功能。canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。
在了解 canvas 之后,大家一定迫不及待地想加入 canvas 的学习之路吧。
body
标签中使用 canvas 标签,给定一定的大小,这样,canvas 画布就建好了
script
标签中,开始使用我们神奇的画笔,工欲善其事必先利其器,开始之前,我们使用 /** @type {HTMLCanvasElement} */
,vscode 会给出 canvas 相关 api 的智能提示,让我们更顺畅的完成我们的代码 // 获取画布
/** @type {HTMLCanvasElement} */
let canvas = document.querySelector('.canvas')
// 获取画布上下文对象
let ctx = canvas.getContext('2d')
首先,我们使用贝塞尔曲线绘制一个聊天气泡
在 canvas 中,一般相邻两个值为一组对象,代表 x 坐标和 y 坐标,而浏览器左上角即为原点位置
比如:
ctx.moveTo(200, 300)
中 200 和 300 分别代表 x 轴和 y 轴坐标,即表示一个点
ctx.quadraticCurveTo(150, 300, 150, 200)
中两两为一组,即代表了两个点的坐标
二次贝塞尔曲线通过和起始点的三个点既可以画出一条圆滑的曲线,如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bIfBgoG9-1675793087508)(https://yhyblog-2023-2-8.oss-cn-hangzhou.aliyuncs.com/Snipaste_2023-02-08_00-25-27.png “贝塞尔曲线”)]
// 画笔移动到指定位置
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()
// 结束画笔
ctx.closePath()
这样,一个聊天气泡框就画好了,后面我们还会学习如何修改颜色或填充颜色
三次贝塞尔曲线和二次贝塞尔曲线类似,不同的是每次使用四个点完成一条曲线的绘画,使用的方法名也不同
接下来,我们使用三次贝塞尔曲线绘画一个爱心
一段三次贝塞尔曲线如下:
//开始画笔
ctx.beginPath()
//三次贝塞尔曲线画一个爱心
ctx.moveTo(300, 200)
ctx.bezierCurveTo(350, 150, 500, 200, 300, 350)
//开始画笔
ctx.beginPath()
//三次贝塞尔曲线画一个爱心
ctx.moveTo(300, 200)
ctx.bezierCurveTo(350, 150, 500, 200, 300, 350)
ctx.bezierCurveTo(100, 200, 250, 150, 300, 200)
ctx.stroke()
// 结束画笔
ctx.closePath()
font 设置文字大小和字体
//设置文字大小和字体
ctx.font = "30px Microsoft Yahei"
strokeStyle 设置文字颜色
// 设置文字颜色
ctx.strokeStyle = "#ffcc2a"
fillText 设置文字内容和位置
// 设置文字内容和位置
ctx.fillText("Hello World", 100, 100)
textAlign 设置文字对齐方式
//设置文字对齐方式 start left center right end
ctx.textAlign = "start"
textBaseline 文本基线对齐
//文本基线对齐 start top middle bottom end
ctx.textBaseline = "middle"
direction 文字方向
//文字方向 rtl ltr
ctx.direction = "rtl"
strokeText 设置文字轮廓
//设置文字轮廓
ctx.strokeText("Hello World", 300, 100)
预测量文字宽度
/*
预测量文字宽度
actualBoundingBoxAscent 实际文字上边距
actualBoundingBoxDescent 实际文字下边距
actualBoundingBoxLeft 实际文字左边距
actualBoundingBoxRight 实际文字右边距
fontBoundingBoxAscent 字体上边距
fontBoundingBoxDescent 字体下边距
width 文字宽度
*/
let result = ctx.measureText("Hello World")
console.log(result)
绘制图片有三种方式:
let img = new Image()
img.src = "./imgs/girl.webp"
img.onload = () => {
// 第一种绘制图片方式 (默认绘制图片的长宽)
ctx.drawImage(img, 0, 0)
}
let img = new Image()
img.src = "./imgs/girl.webp"
img.onload = () => {
//第二种绘制图片方式 (第一组:绘制起点位置,第二组:绘制长宽)
ctx.drawImage(img, 0, 0, 200, 400)
}
let img = new Image()
img.src = "./imgs/girl.webp"
img.onload = () => {
//第三种绘制图片方式(第一组:裁剪起点位置,第二组:裁剪长宽,第三组:绘制起点位置,第四组:绘制长宽)
ctx.drawImage(img, 50, 50, 300, 150, 0, 0, 200, 200)
}
在 body
标签中添加 video
标签和 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操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。循环调用 render,就可以实现播放视频的效果
requestAnimationFrame(render)
}
首先,给 canvas 添加 border 样式,并且绘制一个矩形
canvas 的位移变化都是坐标系的改变,即浏览器左上角原点形成的坐标系的变化
//坐标系位移
ctx.translate(100, 100)
// 缩放
ctx.scale(2, 2)
// 旋转
ctx.rotate(Math.PI / 4)
通过矩阵的方式位移变换
//平移 1,0,0,1分别代表x轴和y轴的缩放比例,200,200分别代表x轴和y轴的位移
ctx.transform(1, 0, 0, 1, 200, 200)
//旋转 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)
绘制线段
ctx.moveTo(200, 200)
// 绘制直线
ctx.lineTo(350, 250)
// 绘制直线
ctx.lineTo(440, 200)
绘制虚线
绘制虚线先绘制线段,再设置让线段分割成为虚线
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)
// 设置线条样式
ctx.lineWidth = 1
// 设置线条端点样式,round为圆形,square为方形,butt为直角
ctx.lineCap = 'butt'
//设置线段连接处样式,round为圆形,bevel为斜角,mitter为直角
ctx.linejoin = 'mitter'
// 设置线段连接处最大长度
ctx.miterLimit = 6
//虚线样式
ctx.setLineDash([20, 20])
//虚线偏移量
ctx.lineDashOffset = index
ctx.stroke()
//循环调用,实现动画效果
requestAnimationFrame(render)
}
render()
阴影属性和 css 的阴影类似
// path2D 对象 用于存储路径
let heartPath = new Path2D()
//二次贝塞曲线画一个爱心
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()则可对图片进行裁剪
如下:原本图片被爱心形状裁剪
// path2D对象 用于创建路径
let heart = new Path2D()
//二次贝塞曲线画一个爱心
heart.moveTo(300, 200)
heart.bezierCurveTo(350, 150, 500, 200, 300, 350)
heart.bezierCurveTo(100, 200, 250, 150, 300, 200)
//clip() 方法从原始画布中剪切任意形状和尺寸的区域
ctx.clip(heart)
let img = new Image()
img.src = "./imgs/girl.webp"
img.onload = () => {
//第三种绘制图片方式(第一组:裁剪起点位置,第二组:裁剪长宽,第三组:绘制起点位置,第四组:绘制长宽)
ctx.drawImage(img, 80, 70, 250, 250, 200, 180, 200, 300)
ctx.lineWidth = 5
ctx.strokeStyle = "red"
ctx.stroke(heart)
}
ctx.stroke(heart)
使用合成图像,做一个刮刮卡抽奖效果
destination-out
** 从画布中删除刮刮卡图像,画笔绘画时绘画弧形模仿橡皮差效果
谢谢惠顾
//绘制刮刮卡图片
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 在鼠标指针移动时,不断绘画
canvas.onmousemove = (e) => {
if (isDraw) {
// 获取鼠标指针的坐标
let x = e.pageX
let y = e.pageY
// globalCompositeOperation 属性设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上
// destination-out 从目标图像中删除源图像
ctx.globalCompositeOperation = 'destination-out'
// arc() 方法使用一个中心点和半径,为画布的当前子路径添加一条弧。
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 = '恭喜你中奖了'
}
刮刮卡刮奖前
刮刮卡刮奖后
原创不易,觉得文章还不错或者感兴趣可以点赞或者收藏一下哦!欢迎留言评论和关注一下!