Canvas

Canvas

<canvas> 是 HTML5 新增的,一个可以使用脚本(通常为 JavaScript) 在其中绘制图像的 HTML 元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至可以进行实时视频处理和渲染。

1.介绍

  • canvas 标签通过脚本(通常是 JavaScript)来绘制图形(比如图表和其他图像)。
  • canvas 标签只是图形容器,您必须使用脚本来绘制图形。
1<template>
2  <canvas ref="canvasDom" />
3</template>
4
5<script>
6import { ref, onMounted, onUnmounted } from 'vue'
7
8export default {
9  setup() {
10    const canvasDom = ref()
11
12    onMounted(() => {
13      // 画布
14      const canvas = canvasDom.value
15      // 画笔
16      const ctx = canvas.value.getctx('2d')
17      onUnmounted(() => {})
18    })
19
20    return { canvasDom }
21  }
22}
23
</script>
24
25<style lang="less" scoped>
26canvas {
27  background-color#000000;
28}
29
</style>

默认我们看到的是一个300px * 150px的画布
实际上canvas有两个尺寸,一个是css样式,与普通元素的宽度高度设置一致,表示<canvas>标签页面版式上的尺寸,另一个是<canvas>画布的内部尺寸,它表示的是画笔绘制的时候实际上用的尺寸
如果我们直接设置内部属性canvas.width、canvas.heigth,则外部css样式则会跟随,保证绘制效果比例一致,而如果只设置css属性尺寸,则内部尺寸不会跟随,而是拉伸内部尺寸,使内部元素填满css尺寸
考虑到元素可能需要自适应页面而不是固定,可以同时设置内外尺寸并保持一致

1<script>
2import { ref, onMounted, onUnmounted } from 'vue'
3
4export default {
5  setup() {
6    const canvasDom = ref()
7
8    onMounted(() => {
9      // 画布
10      const canvas = canvasDom.value
11      // 画笔
12      const ctx = canvas.getctx('2d')
13
14      const resizeFn = () => {
15        // canvas内部尺寸变化后,需要重新绘制
16        const canvasData = ctx.getImageData(00, canvas.width, canvas.height)
17        canvas.width = canvas.getBoundingClientRect().width
18        canvas.height = canvas.getBoundingClientRect().height
19        ctx.putImageData(canvasData, 00)
20      }
21      resizeFn()
22
23      window.addEventListener('resize', () => {
24        const newWidth = canvas.getBoundingClientRect().width
25        const newHeight = canvas.getBoundingClientRect().height
26        if (canvas.width !== newWidth || canvas.height !== newHeight) resizeFn()
27      })
28      onUnmounted(() => {
29        window.addEventListener('resize', resizeFn)
30      })
31    })
32
33    return { canvasDom }
34  }
35}
36
</script>
37
38<style lang="less" scoped>
39canvas {
40  width50%;
41  height50%;
42  background-color#000000;
43}
44
</style>

2.基础Api

画笔方法
ctx.moveTo(x, y) 移动画笔
ctx.lineTo(x, y) 笔画停点
ctx.fill() 填充绘制
ctx.stroke() 描边绘制 绘制可同时使用
ctx.beginPath() 分次绘制 否则会重复绘制
ctx.closePath() 闭合图形 可以替代最后一笔直线
ctx.rect(x,y,width,height) 创建矩形 后面需要绘制
ctx.fillRect(x,y,width,height) 绘制填充矩形
ctx.strokeRect(x,y,width,height) 绘制描边矩形

画笔属性
lineWidth: 宽度
lineCap: 起始样式
lineJoin: 链接样式(miterLimit)
miterLimit: 斜接长度
strokeStyle: 描边样式
fillStyle: 填充样式

2.创建样式

  • 可以将颜色值直接赋值给画笔style属性,支持透明设置

  • 创建渐变
    grd = ctx.createLinearGradient(a, b, c, d) 渐变是针对范围的,绘制的时候裁剪出来,渐变线的起点和终点不一定要在图像内,颜色断点的位置也是一样的。但是如果图像的范围大于渐变线,那么在渐变线范围之外,就会自动填充离端点最近的断点的颜色。
    grd = ctx.createRadialGradient(a, b, c, d) 渐变圆
    style = grd.addColorStop(stop, color) 添加渐变断点 得到的样式可以直接赋值给画笔样式style属性

  • 创建纹理
    pattern = ctx.createPattern(img, repeat-style)
    img对象可以是图片,canvas或者video对象,pattern可以直接赋值给画笔样式style属性

选择图片时一定要选择那种左右互通,上下互通的图片做为纹理,这样看上去才不会有不自然的短接处。还需要注意图片异步加载的时间,否则将导致绘制时找不到图片源

3.自由路径

标准圆弧
ctx.arc(x,y,radius,startAngle,endAngle,anticlockwise)

前面三个参数,分别是圆心坐标与圆半径。startAngle、endAngle使用的是弧度值,不是角度值

复杂圆弧
ctx.arcTo(x1,y1,x2,y2,radius)

5个参数,分别是两个切点的坐标和圆弧半径。这个方法是依据切线画弧线,即由两个切线确定一条弧线。
以给定的半径绘制一条弧线,圆弧的起点与当前路径的位置到(x1, y1)点的直线相切,圆弧的终点与(x1, y1)点到(x2, y2)的直线相切。
总之就是在一个角中放一个标准圆,注意一下,起点一定在路径上但不一定在圆弧上,也不一定时切点,终点不一定在路径上,也不一定时切点

ps钢笔绘制图形是基于贝塞尔曲线原理的

贝塞尔曲线原理:n阶贝塞尔曲线,链接所有的起始点与控制点(n+1个点)的n条线段上,同时取得线段上的n个x%处的点,再次链接得到n-1条线段,由此反复,直至最后一条线段上的x%处的点的轨迹连接得到的曲线

二次贝塞尔曲线
ctx.quadraticCurveTo(cpx,cpy,x,y)
二次贝塞尔曲线

三次贝塞尔曲线
ctx.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)
三次贝塞尔曲线

4.变换

变换并非变换的是图像,而是整个坐标系、整个画布

平移变换
ctx.translate(x, y)

平移x, y的距离

旋转变换
ctx.rotate(deg)

需要注意的是,这个的旋转是以坐标系的原点(0,0)为圆心进行的顺时针旋转

缩放变换
ctx.scale(sx,sy)

两个参数,分别是水平方向和垂直方向上对象的缩放倍数

  • 缩放时,图像左上角坐标的位置也会对应缩放。
  • 缩放时,图像线条的粗细也会对应缩放。

存储状态
ctx.save()
重置状态
ctx.restore()

变换的实质是坐标变形,因为变换的状态会叠加,所以在变换之前应该保存状态并且在变换之后还原,避免坐标系错乱

1ctx.save()
2ctx.translate(xy)
3ctx.rotate(deg)
4ctx.scale(i,i)
5ctx.strokeRect(50,50,150,100)
6ctx.restore()

矩阵变换
ctx.transform(a,b,c,d,e,f)

参数 意义
a 水平缩放(1)
b 水平倾斜(0)
c 垂直倾斜(0)
d 垂直缩放(1)
e 水平位移(0)
f 垂直位移(0)

使用ctx.transform (1,0,0,1,dx,dy)代替ctx.translate(dx,dy)
使用ctx.transform(sx,0,0,sy,0,0)代替ctx.scale(sx, sy)
使用ctx.transform(0,b,c,0,0,0)来实现倾斜效果
使用ctx.transform(Math.cos(θMath.PI/180),Math.sin(θMath.PI/180),
-Math.sin(θMath.PI/180),Math.cos(θMath.PI/180),0,0)可以替代ctx.rotate(θ)(不好用)

ctx.setTransform(a,b,c,d,e,f)
在绘制前会自动restore

5.文本输入

画笔属性
font 设置或返回文本内容的当前字体属性

与css类似[font-style] [font-variant] [font-weight] [font-size/line-height] [font-family]

textAlign 设置或返回文本内容的当前对齐方式
textBaseline 设置或返回在绘制文本时使用的当前文本垂直对齐基线

画笔方法
ctx.fillText(String,x,y,[maxlen]) 在画布上绘制“被填充的”文本
ctx.strokeText(String,x,y,[maxlen]) 在画布上绘制文本(无填充)

fillText()与strokeText()的参数表是一样的,接受4个参数,分别是String,x,y与maxlen,其中String是指要显示的字符串,之后x与y是指显示的坐标,最后一个maxlen是可以缺省的数值型参数,代表显示的最大宽度,单位是像素。如果文本的长度超过了这个maxlen,Canvas就会将显示文本横向压缩。通常为了保证字体的美观,我们不设置maxlen。

ctx.measureText(text) 返回包含指定文本对象的宽度

因为Canvas 文本API只支持单行显示,如果文案过长需要换行显示,则需要计算文案尺寸

6.其他

画笔属性
ctx.shadowColor:阴影颜色。
ctx.shadowOffsetX:阴影x轴位移。正值向右,负值向左。
ctx.shadowOffsetY:阴影y轴位移。正值向下,负值向上。
ctx.shadowBlur:阴影模糊滤镜。数据越大,扩散程度越大。
ctx.globalAlpha:透明
ctx.globalCompositeOperation:图像合成

画笔方法
ctx.clip():裁剪区域

需要注意的是裁剪是对画布进行的,裁切后的画布不能恢复到原来的大小,也就是说画布是越切越小的,要想保证最后仍然能在canvas最初定义的大小下绘图需要注意save()和restore()。

ctx.drawImage(img,sx,sy,swidth,sheight,x,y,width,height):绘制图像
img 规定要使用的图像、画布或视频。
sx 可选。开始剪切的 x 坐标位置。
sy 可选。开始剪切的 y 坐标位置。
swidth 可选。被剪切图像的宽度。
sheight 可选。被剪切图像的高度。
x 在画布上放置图像的 x 坐标位置。
y 在画布上放置图像的 y 坐标位置。
width 可选。要使用的图像的宽度。(伸展或缩小图像)
height 可选。要使用的图像的高度。(伸展或缩小图像)

ctx.clearRect(x,y,w,h):橡皮擦,清空指定矩形上的画布上的像素
ctx.isPointInPath():判断指定的点是否在当前路径中

ImageData = ctx.createImageData():创建新的、空白的 ImageData 对象
ImageData = ctx.getImageData():返回 ImageData 对象,该对象为画布上指定的矩形复制像素数据
ctx.putImageData(ImageData, x, y):把图像数据(从指定的 ImageData 对象)放回画布上

canvas.toDataURL():canvas保存为base64编码格式的图片

7.canvas动画

canvas动画原理就是重复绘制动画的每一帧,连续的帧就形成动画
一般会采用 requestAnimationFrame()实现动画效果。这个方法提供了更加平缓并更加有效率的方式来执行动画,当系统准备好了重绘条件的时候,才调用绘制动画帧。一般每秒钟回调函数执行60次,也有可能会被降低。

1draw() {
2    // 清空 canvas
3    ctx.clearRect()
4    // 保存 canvas 状态
5    ctx.save()
6    // 绘制动画图形(animated shapes)
7    ctx.[api](time)
8    // 恢复 canvas 状态
9    ctx.restore()
10    requestAnimationFrame(draw)
11}
12requestAnimationFrame(draw)

posted @ 2022-04-02 09:23  前端订阅  阅读(88)  评论(0编辑  收藏  举报