Canvas 入门
Canvas 是什么
- Canvas 中文名叫 “画布”,是 HTML5 新增的一个标签。
- Canvas 允许开发者通过 JS在这个标签上绘制各种图案。
- Canvas 拥有多种绘制路径、矩形、圆形、字符以及图片的方法。
- Canvas 在某些情况下可以 “代替” 图片。
- Canvas 可用于动画、游戏、数据可视化、图片编辑器、实时视频处理等领域。
Canvas 和 SVG 的区别
Canvas | SVG |
---|---|
用JS动态生成元素(一个HTML元素) | 用XML描述元素(类似HTML元素那样,可用多个元素来描述一个图形) |
位图(受屏幕分辨率影响) | 矢量图(不受屏幕分辨率影响) |
不支持事件 | 支持事件 |
数据发生变化需要重绘 | 不需要重绘 |
前置知识
canvas 宽高
我们知道 canvas 是 HTML5 新增的一个标签。那么一个标签盒子基本都会有自己的宽高属性。而 canvas 的默认宽高为300px 150px。
我们可以通过内嵌方式设置 canvas 宽高
<canvas width="600" height="400"></canvas>
注意 canvas 宽高不能通过其它 css 方式设置
- 使用其它方式设置的宽高只是将 canvas 拉伸变形
- 使用 js 获取 canvas 的宽高,此时返回的是 canvas 的默认值。
兼容性
暂时只有 IE 9 以上才支持 canvas 。但好消息是 IE 已经有自己的墓碑了。
canvas 的坐标系
Canvas 使用的是 W3C 坐标系 ,也就是遵循我们屏幕、报纸的阅读习惯,从上往下,从左往右。
直线
画线
canvas 想要实现直线效果需用到下面三个 api:
- moveTo(x1, y1):起点坐标 (x, y)
- lineTo(x2, y2):下一个点的坐标 (x, y)
- stroke():将所有坐标用一条线连起来
- 画一条直线
const cnv = document.getElementById('cvs')
const cxt = cnv.getContext('2d')
// 绘制直线
cxt.moveTo(50, 100) // 起点坐标
cxt.lineTo(200, 50) // 下一个点的坐标
cxt.stroke() // 将上面的坐标用一条线连接起来
- 画多条直线
cxt.moveTo(20, 100)
cxt.lineTo(200, 100)
cxt.stroke()
cxt.moveTo(20, 120.5)
cxt.lineTo(200, 120.5)
cxt.stroke()
仔细观察一下,为什么两条线的粗细不一样的?
明明使用的方法都是一样的,只是第二条直线的 Y轴 的值是有小数点。
这是因为默认情况下 canvas 会将线条的中心点和像素的底部对齐,所以会导致显示效果是 2px 和非纯黑色问题。
线的中心点会和画布像素点的底部对齐,所以会线中间是黑色的,但由于一个像素就不能再切割了,所以会有半个像素被染色,就变成了浅灰色。
所以如果你设置的 Y轴 值是一个整数,就会出现上面那种情况。
线条样式
- lineWidth:线的粗细
- strokeStyle:线的颜色
- lineCap:线帽:默认: butt; 圆形: round; 方形: square
- lineJoin:拐角样式:尖角:miter(默认); 圆角: round; 斜角: bevel
// 绘制直线
cxt.moveTo(50, 50)
cxt.lineTo(200, 50)
// 修改直线的宽度
cxt.lineWidth = 20
// 修改直线的颜色
cxt.strokeStyle = 'pink'
// 修改直线两端样式
cxt.lineCap = 'round'
cxt.stroke()
虚线
使用 setLineDash([]) 方法可以将线条设置成虚线。
虚线分3种情况
- 只传1个值 [10]实线与空白都是 10px
- 有2个值 [10, 20]实线是 10px, 空白 20px
- 有3个以上的值 [10, 20, 5]实现和空白按顺序使用[]里面的值(循环使用)
cxt.lineWidth = 20
cxt.strokeStyle = 'pink'
cxt.moveTo(50, 30)
cxt.lineTo(200, 30)
cxt.setLineDash([10]) // 只传1个参数,实线与空白都是 10px
cxt.stroke()
cxt.beginPath()
cxt.moveTo(50, 70)
cxt.lineTo(200, 70)
cxt.setLineDash([10, 20]) // 2个参数,此时,实线是 10px, 空白 20px
cxt.stroke()
cxt.beginPath()
cxt.moveTo(50, 110)
cxt.lineTo(200, 110)
cxt.setLineDash([10, 20, 5]) // 传3个以上的参数,此例:10px实线,20px空白,5px实线,10px空白,20px实线,5px空白 ……
cxt.stroke()
新开路径
- beginPath()
在绘制多条线段的时,如果想让后面线段样式不影响前面的线段,就需要开辟新路径。
但是事实上仅靠 beginPath 并不能实现样式隔离
// 第一条线
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()
第二条线我们虽然开辟了新路径,但是还是继承了前面线条的样式。
所以想要实现样式互不影响,必须满足两个条件:
- 使用 beginPath 开辟新路径
- 设置新的样式
// 第一条线
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()
TIP
新开路径的线条如果不设置样式,就会受到前一条影响。如果后面设置样式而不开新路径,那么就会全部以后面设置的为主。
闭合路径
- closePath() 闭合路径有以下两个作用:
- 让线条自动闭合,
- 让闭合的连接处更加顺其自然
比如画一个三角形:
不使用闭合路径
cxt.moveTo(50, 30)
cxt.lineTo(200, 30)
cxt.lineTo(200, 140)
cxt.lineTo(50, 30) // 闭合
cxt.stroke()
使用闭合路径
cxt.moveTo(50, 30)
cxt.lineTo(200, 30)
cxt.lineTo(200, 140)
cxt.closePath()
cxt.stroke()
矩形
线性矩形
- strokeStyle:设置描边的属性(颜色、渐变、图案)
- strokeRect(x, y, width, height):描边矩形(x和y是矩形左上角起点;width 和 height 是矩形的宽高)
- strokeStyle 必须写在 strokeRect() 前面,不然样式不生效。
cxt.strokeStyle = 'pink'
cxt.strokeRect(30, 30, 200, 80)
填充矩形
- fillStyle:设置描边的属性(颜色、渐变、图案)
- fillRect(x, y, width, height):描边矩形(x和y是矩形左上角起点;width 和 height 是矩形的宽高)
- fillStyle 必须写在 strokeRect() 前面,不然样式不生效。
cxt.fillStyle = 'pink'
cxt.fillRect(30, 30, 200, 80)
TIP
同时使用 strokeRect() 和 fillRect() 可以实现矩形描边效果
rect() 生成矩形
strokeRect() 和 fillRect() 这两个方法调用后会立即绘制;rect() 方法被调用后,不会立刻绘制矩形,而是需要调用 stroke() 或 fill() 辅助渲染。
cxt.strokeStyle = 'red'
cxt.fillStyle = 'pink'
cxt.rect(30, 30, 200, 80)
cxt.stroke()
cxt.fill()
清空矩形
使用 clearRect() 方法可以清空指定区域。
clearRect(x, y, width, height)
其语法和创建 cxt.rect() 差不多。
cxt.fillStyle = 'pink' // 设置填充颜色
cxt.fillRect(30, 30, 200, 80) // 填充矩形
cxt.clearRect(40, 40, 100, 30) // 清空矩形
画圆
绘制圆形的方法是 arc()。
arc(x, y, r, sAngle, eAngle,counterclockwise)
- x 和 y: 圆心坐标
- r: 半径
- sAngle: 开始角度
- eAngle: 结束角度
- counterclockwise: 绘制方向(true: 逆时针; false: 顺时针),默认 false
实际开发中,为了让自己或者别的开发者更容易看懂弧度的数值,1°应该写成 Math.PI / 180。
- 100°: 100 * Math.PI / 180
- 110°: 110 * Math.PI / 180
- 241°: 241 * Math.PI / 180
cxt.beginPath()
cxt.arc(150, 80, 60, 0, 360 * Math.PI / 180)
cxt.closePath()
cxt.stroke()
TIP
注意:绘制圆形之前,必须先调用 beginPath() 方法!!! 在绘制完成之后,还需要调用 closePath() 方法!!!
弧线
arcTo() 可以实现画弧线。当然 arc() 也可以,只不过使用 arc() 情况下不能使用 closePath(),会带来额外的问题。
arcTo(cx, cy, x2, y2, radius)
- cx: 两切线交点的横坐标
- cy: 两切线交点的纵坐标
- x2: 结束点的横坐标
- y2: 结束点的纵坐标
- radius: 半径
其中,(cx, cy) 也叫控制点,(x2, y2) 也叫结束点。开始点则由 moveTo() 或者 lineTo() 提供。
cxt.moveTo(40, 40)
cxt.arcTo(120, 40, 120, 120, 80)
cxt.stroke()
字体
样式
cxt.font = 'font-style font-variant font-weight font-size/line-height font-family'
描边 strokeText()
strokeText(text, x, y, maxWidth)
- text: 字符串,要绘制的内容
- x: 横坐标,文本左边要对齐的坐标(默认左对齐)
- y: 纵坐标,文本底边要对齐的坐标
- maxWidth: 可选参数,表示文本渲染的最大宽度(px),如果文本超出 maxWidth 设置的值,文本会被压缩
可以通过 strokeStyle 设置字体颜色
填充 fillText
fillText(text, x, y, maxWidth)
- text: 字符串,要绘制的内容
- x: 横坐标,文本左边要对齐的坐标(默认左对齐)
- y: 纵坐标,文本底边要对齐的坐标
- maxWidth: 可选参数,表示文本渲染的最大宽度(px),如果文本超出 maxWidth 设置的值,文本会被压缩
可以通过 fillStyle 设置字体颜色
获取文本长度 measureText()
measureText().width 方法可以获取文本的长度,单位是 px 。
let text = '雷猴'
cxt.font = 'bold 40px Arial'
cxt.fillText(text, 40, 80)
console.log(cxt.measureText(text).width) // 80
字体对齐
水平对齐方式 textAlign
使用 textAlign 属性可以设置文字的水平对齐方式,一共有5个值可选
- start: 默认。在指定位置的横坐标开始。
- end: 在指定坐标的横坐标结束。
- left: 左对齐。
- right: 右对齐。
- center: 居中对齐。
垂直对齐方式 textBaseline
使用 textBaseline 属性可以设置文字的垂直对齐方式。
- alphabetic: 默认。文本基线是普通的字母基线。
- top: 文本基线是 em 方框的顶端。
- bottom: 文本基线是 em 方框的底端。
- middle: 文本基线是 em 方框的正中。
- hanging: 文本基线是悬挂基线
图片
使用 drawImage() 可以渲染图片
drawImage(image, dx, dy, dw, dh)
- image 图片源
- dx x 轴位置
- dy y 轴位置
- dw 图片宽度
- dh 图片高度
渲染图片的方式有2中,一种是在JS里加载图片再渲染,另一种是把DOM里的图片拿到 canvas 里渲染。
JS版
在 JS 里加载图片并渲染,有以下几个步骤:
- 创建 Image 对象
- 引入图片
- 等待图片加载完成
- 使用 drawImage() 方法渲染图片
// 1 创建 Image 对象
const image = new Image()
// 2 引入图片
image.src = './images/dog.jpg'
// 3 等待图片加载完成
image.onload = () => {
// 4 使用 drawImage() 方法渲染图片
cxt.drawImage(image, 30, 30)
}
DOM版
<img src="./images/dog.jpg" id="dogImg"/>
const image = document.getElementById('dogImg')
cxt.drawImage(image, 70, 70)
截取图片
截图图片同样使用drawImage() 方法,只不过传入的参数数量比之前都多,而且顺序也有点不一样了。
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
- image: 图片源
- sx: 开始截取的横坐标
- sy: 开始截取的纵坐标
- sw: 截取的宽度
- sh: 截取的高度
- dx: 图片左上角的横坐标位置
- dy: 图片左上角的纵坐标位置
- dw: 图片宽度
- dh: 图片高度