最近在工作中,有个项目一直需要用到canvas相关知识,之前对这块不是很熟练,借着十一假期来进行学习学习
1. 基础
1.1 创建canvas
html
<canvas id="canvas"></canvas>
在 canvas 上属性 width和height直接指定大小,或用下面的方式
javascript
var canvas = document.getElementById("canvas")
canvas.width = 1024
canvas.height = 768
var ctx = canvas.getContext("2d");
下面所有例子的代码省略了上面的穿件canvas代码
1.2 坐标
左上角为原点,向右为x轴正方向,向下为y轴正方向
image.png这个和数学中的坐标系有点区别
image.png
2. 绘制线条
2.1 步骤
- 使用moveTo定义笔触开始的点坐标,坐标的表示见 1.2 图形表示
- 使用lineTo定义笔触将要到达的点
- 使用 stroke 进行绘制
ctx.moveTo(100, 100) // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100) // 将要绘制到的位置
ctx.stroke() // 绘制
第1,2步是设置状态,第三步才是进行绘制操作。
此外,对于线的状态设置还有
ctx.lineWidth = 2
ctx.strokeStyle = '#f00'
3. 绘制图形
3.1 根据线->图形。
在初级数学中,我们都知道线可以组成面,线闭合起来就能形成一个图形,那么有了 2 中的知识,我们就能绘制出一个图形了。just do it
3.1.1绘制一个三角形
ctx.moveTo(100, 100) // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100) // 将要绘制到的位置 1
ctx.lineTo(100, 300) // 将要绘制到的位置 2
ctx.lineTo(100, 100) // 将要绘制到的位置 3 回到笔触起点
ctx.stroke() // 绘制线条
image.png
3.1.2 填充
在上面我们在设置状态(确定顶点坐标)之后,用了canvas的 stroke() 这个api,它表示绘制线条,其实我们还有其他的api,填充
fill
- stroke
- fill
ctx.moveTo(100, 100) // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100) // 将要绘制到的位置 1
ctx.lineTo(100, 300) // 将要绘制到的位置 2
ctx.lineTo(100, 100) // 将要绘制到的位置 3 回到笔触起点
ctx.fill() // 填充
image.png
这时候我们就得到一个实心的图形啦!
哎?怎么是黑色的?因为默认就是黑色的。线条能设置颜色,那么我们填充也能设置颜色,使用下面的api
- fillStyle
ctx.moveTo(100, 100) // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100) // 将要绘制到的位置 1
ctx.lineTo(100, 300) // 将要绘制到的位置 2
ctx.lineTo(100, 100) // 将要绘制到的位置 3 回到笔触起点
ctx.fillStyle = '#f00' // 设置填充样式
ctx.fill() // 填充
image.png
所以综上所述,只要我们知道图形的顶点坐标,我们再用线连接起来,就能绘制大部分的图形了!
3.2 绘制矩形
矩形,规规矩矩,所以canvas中有特定的api进行绘制,提供以下三个api
strokeRect(x, y, width, height) // 绘制一个矩形的边框
fillRect(x, y, width, height) // 绘制一个填充的矩形
clearRect(x, y, width, height) // 清除指定矩形区域,让清除部分完全透明
我们使用上面三个分别尝试
3.2.1 strokeRect
// ctx.strokeStyle = '#f00' // 设置矩形线条颜色
ctx.strokeRect(100, 100, 200, 200) // 左上角坐标为(100,100),宽高分别为 200, 200 的线状矩形
image.png
3.2.2 fillRect
// ctx.fillStyle = '#f00' // 设置矩形线条颜色
ctx.fillRect(100, 100, 200, 200) // 左上角坐标为(100,100),宽高分别为 200, 200 的填充矩形
image.png
3.2.3 clearRect
清除指定矩形区域,让清除部分完全透明
我们在 3.2.2的基础上写
// ctx.fillStyle = '#f00' // 设置矩形线条颜色
ctx.fillRect(100, 100, 200, 200) // 左上角坐标为(100,100),宽高分别为 200, 200 的填充矩形
ctx.clearRect(100, 100, 150, 150)
image.png
可以看到,在3.2.2基础上,某个区域被清除了。
3.2.4 绘制多个图形/线条,状态问题(重要!!!)
canvas是基于状态进行绘制,先设定状态,再进行绘制。在一个canvas画布上进行多个图形/线条绘制是正常操作,我们看下面代码
// 绘制一个边线宽为5, 颜色为红色的三角形
ctx.moveTo(100, 100) // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100) // 将要绘制到的位置 1
ctx.lineTo(100, 300) // 将要绘制到的位置 2
ctx.lineTo(100, 100) // 将要绘制到的位置 3 回到笔触起点
ctx.strokeStyle = '#f00'
ctx.lineWidth = 5
ctx.stroke() // 绘制
// 绘制一条绿色的直线,线宽默认
ctx.moveTo(400, 100)
ctx.lineTo(100, 400)
ctx.strokeStyle = '#0f0'
ctx.stroke()
运行结果如下
image.png
哎?从上述代码中,我们明明是先绘制一个边线宽为5, 颜色为红色的三角形,然后再绘制一条绿色的,线宽默认的直线。
但是我们发现了啥,三角形的边颜色变成了绿色,直线线宽变成了5
原因如下:
- canvas是基于状态进行绘制,先设定状态,再进行绘制
- 绘制三角形的时候,我们设置了状态 1.线颜色为红色, 2. 线宽为5
- 绘制直线的时候,我们设置了状态 1. 线颜色为绿色
- 这时候,我们后面的状态就会将前面的状态进行覆盖!及 1. 线颜色为绿色, 2. 线宽为5
真相大白!这是新手经常会遇到的问题。这个就类似于作用域,是不是?
所以我们要对每个图形状态进行状态的限定。用到两个api
- beginPath
- closePath
将每一段状态用beginPath 和 closePath 进行括起来
直接上代码
// 绘制一个三角形
ctx.beginPath()
ctx.moveTo(100, 100) // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100) // 将要绘制到的位置 1
ctx.lineTo(100, 300) // 将要绘制到的位置 2
ctx.lineTo(100, 100) // 将要绘制到的位置 3 回到笔触起点
ctx.strokeStyle = '#f00'
ctx.lineWidth = 5
ctx.closePath()
ctx.stroke() // 绘制
// 绘制一条直线
ctx.beginPath()
ctx.moveTo(400, 100)
ctx.lineTo(100, 400)
ctx.strokeStyle = '#0f0'
ctx.closePath()
ctx.stroke()
运行结果
上述代码,其实不需要closePath也能得到同样的效果,closePath会自动将一个不封闭的图形封闭。
// 绘制一个三角形
ctx.beginPath()
ctx.moveTo(100, 100) // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100) // 将要绘制到的位置 1
ctx.lineTo(100, 300) // 将要绘制到的位置 2
// ctx.lineTo(100, 100) // 将要绘制到的位置 3 回到笔触起点 注意此时我注释了这个状态,不是一个封闭的三角形了
ctx.strokeStyle = '#f00'
ctx.lineWidth = 5
ctx.closePath() // 自动封闭图形
ctx.stroke() // 绘制
// 绘制一条直线
ctx.beginPath()
ctx.moveTo(400, 100)
ctx.lineTo(100, 400)
ctx.strokeStyle = '#0f0'
// ctx.closePath() // 可以不需要
ctx.stroke()
3.2.4 绘制圆/圆弧 arc
- 重要api
ctx.arc(centerx, centery, radius, startingAngle, endingAngle, anticlockwise = false)
上面👆的api, 参数分别表示:圆心x, 圆心y,半径r, 起始角度, 结束角度, 逆时针旋转 (默认false)
- 角度示意图
- 例子
// 圆心x,圆心y,半径r, 起始角度, 结束角度, 逆时针旋转 (默认false)
ctx.arc(300, 300, 100, 0, 2 * Math.PI, false)
ctx.stroke()
image.png
4. 颜色覆盖、透明
css中使用 rgba来实现颜色及其透明度,在canvas同样适用
ctx.beginPath()
ctx.fillStyle = 'rgba(255,100,100, 0.5)'
ctx.fillRect(100, 100, 100, 100)
ctx.closePath()
ctx.beginPath()
ctx.fillStyle = 'rgba(100,100,100, 0.5)'
ctx.fillRect(150, 150, 100, 100)
ctx.closePath()
运行结果
image.png
5. 图形变换和状态保存
5.1 通过translate讲解 save和restore
canvas中translate(x,y)可以讲绘制的图形平移到(x,y) 的位置
ctx.fillStyle = 'rgba(255,100,100, 0.5)'
ctx.translate(100, 100) // 从0,0 平移到 100,100
ctx.fillRect(0, 0, 100, 100)
ctx.fillStyle = 'rgba(100,100,100, 0.5)'
ctx.translate(150, 150) // // 希望从0,0 平移 150,150, 但是和上面的状态叠加了 变成了 translate(250,250)
ctx.fillRect(0, 0, 100, 100)
结果
image.png第二个灰色的矩形,我们希望从(0,0)平移(150,150)个横纵像素, 按理说和第一个粉色的矩形有重合,但是和上面的状态叠加了 变成了 translate(250,250)。
这时候我们就需要用到 save和restore这两个api,它们是成对出现的,利用它们更改上面的代码
ctx.save()
ctx.fillStyle = 'rgba(255,100,100, 0.5)'
ctx.translate(100, 100) // 从0,0 平移到 100,100
ctx.fillRect(0, 0, 100, 100)
ctx.restore()
ctx.save()
ctx.fillStyle = 'rgba(100,100,100, 0.5)'
ctx.translate(150, 150) // // 希望从0,0 平移到 150,150, 但是和上面的状态叠加了 变成了 translate(250,250)
ctx.fillRect(0, 0, 100, 100)
ctx.restore()
结果如下:
image.png5.2 tranlate、rotate、scale
5.2.1 translate
对图形进行平移
5.1 章节已经演示过
5.2.2 rotate
旋转
rotate(度数)
ctx.save()
ctx.fillStyle = 'rgba(100,100,100, 0.5)'
ctx.fillRect(200, 200, 100, 100)
ctx.restore()
ctx.save()
ctx.fillStyle = 'rgba(255,100,100, 0.5)'
ctx.rotate(30 / 180 * Math.PI) // 以(0,0)的点为圆心, 以(0,0)到左上角的顶点坐标的距离为半径,顺时针旋转30度
ctx.fillRect(200, 200, 100, 100)
ctx.restore()
以(0,0)的点为圆心, 以(0,0)到左上角的顶点坐标的距离为半径,顺时针旋转30度。如图,粉色的图形即为灰色图形旋转后的位置
image.png5.2.3 scale
缩放
特性
scale()
用于修改元素的大小。可以通过向量形式定义的缩放值来放大或缩小元素,同时可以在不同的方向设置不同的缩放值。该变换通过一个二维向量确定在一个方向缩放的多少。如果缩放向量的两个坐标是相等的,那么所讲是均等的,或者说是各向同的,同时元素的形状是被保持的。---- MDN
我们看下面的代码,蓝色部分可以看作是灰色部分放大两倍的。但是我们看看结果
ctx.fillStyle = '#98CCFB'
ctx.fillRect(50, 50, 200, 100)
ctx.fillStyle = '#CCCCCC'
ctx.scale(2,2)
ctx.fillRect(50, 50, 200, 100)
结果
image.png我们发现,他们的顶点位置竟然也发生了变化,变成了(100,100),可是咱们代码中,顶点位置都是(50,50)。这就是scale值得注意的地方,它会对起始位置的坐标也会进行缩放操作。下面是示意图:
image.png如图蓝色图形被放大2倍,它的顶点位置也会被相应缩放,当大了两倍。
副作用
从上面,我们可以看见scale的副作用之一就是会对起始坐标的位置进行缩放。我们接着上面的例子进行改造下,我们换成strokeRect进行绘制图形。
ctx.save()
ctx.fillStyle = '#98CCFB'
ctx.strokeStyle = '#000'
ctx.lineWidth = 5
ctx.strokeRect(50, 50, 200, 100)
ctx.restore()
ctx.save()
ctx.fillStyle = '#CCCCCC'
ctx.strokeStyle = '#000'
ctx.lineWidth = 5
ctx.scale(2,2)
ctx.strokeRect(50, 50, 200, 100)
ctx.restore()
运行结果如下:
image.png我们会发现,进行放大的图形,它的lineWidth也放大了!这时候我们知道了它的另一个副作用,会缩放边框。
总结:
- scale会缩放起始坐标
- scale会缩放边的粗细
网友评论