学习canvas基础的总结

625 阅读8分钟

简单介绍canvas

<canvas><img>标签很像,但是没有src和alt属性,只有width和height属性。 css定义的尺寸与canvas的尺寸比例不一致时,会出现扭曲。

canvas起初是空白的,要找到渲染上下文来绘制。 getContext() 该方法用来获取渲染上下文和绘制功能。

var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');// 需要检查支持性,是否支持canvas.getContext

矩形

fillRect(x, y, width, height);// 填充矩形
strokeRect(x, y, width, height);//矩形边框
clearRect(x, y, width,height);// 清除指定矩形区域,清除部分为透明的

绘制路径:

  • 创建路径的起始点
  • 使用画图命令,画出路径
  • 路径封闭
  • 路径生成,绘制图形(描边,填充)

beginPath()
新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。
列表清空重置,重新绘制新的图形。

closePath()
闭合路径之后图形绘制命令又重新指向到上下文中。
非必需,当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合。

stroke()
通过线条来绘制图形。如果用线条绘制必须用closePath()来闭合曲线。

fill()
通过填充路径的内容区域生成实心的图形。

moveTo(x, y)
将笔触移动到x,y坐标点处。

lineTo(x, y)
绘制直线。

arc(x, y, radius, startAngle, endAngle, anticlockwise)
圆心坐标,半径,起始角度,结束角度,false顺时针,true逆时针。
三点钟方向为0°(弧度),弧度=(Math.PI/180)*角度。

arcTo(x1, y1, x2, y2, radius)
根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。

rect(x, y, width, height)
绘制一个左上角坐标为(x,y),宽高为width以及height的矩形。笔触自动置回(0,0)。

quadraticCurveTo(cp1x, cp1y, x,y)
绘制二次贝塞尔曲线,cp1x,cp1y为一个控制点,x,y为结束点。

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。蓝色是起始点和结束点,红色是控制点。

path2D对象:用来缓存和记录绘画命令,可快速回顾绘制路径。

Path2D()会返回一个新初始化的Path2D对象(可能将某一个路径作为变量——创建一个它的副本,或者将一个包含SVG path数据的字符串作为变量)。

new Path2D(); // 空的Path对象
new Path2D(path); // 克隆Path对象
new Path2D(d); // 从SVG建立Path对象
Path2D.addPath(path [, transform]);// 添加了一条路径到当前路径(可能添加了一个变换矩阵)。

新的Path2D API有另一个强大的特点,就是使用SVG path data来初始化canvas上的路径。这将使你获取路径时可以以SVG或canvas的方式来重用它们。

var p = new Path2D("M10 10 h 1 80 v 80 h -80 Z");// 将先移动到点 (M10 10) 然后再水平移动80个单位(h 80),然后下移80个单位 (v80),接着左移80个单位 (h -80),再回到起点处 ( z)。

透明度:TransparencyValue

globalAlpha = transparencyValue;// 有效值(0.0~1.0),完全透明~完全不透明

线性样式

lineWidth = value
设置线条宽度。属性值必须为正数。默认值是1.0。

lineCap = type
设置线条末端样式。type的值有buttroundsquare。默认是 butt

lineJoin = type
设定线条与线条间接合处的样式。round, bevelmiter。默认是 miter

miterLimit = value
限制当两条线相交时交接处最大长度。
所谓交接处长度(斜接长度)是指线条交接处内角顶点到外角顶点的长度。

getLineDash()
返回一个包含当前虚线样式,长度为非负偶数的数组。

setLineDash(segments)
设置当前虚线样式。

lineDashOffset = value
设置虚线样式的起始偏移量。
setLineDash方法和 lineDashOffset 属性来制定虚线样式。
setLineDash方法接受一个数组,来指定线段与间隙的交替;
lineDashOffset 属性设置起始偏移量。

渐变:以用线性或者径向的渐变来填充或描边

新建一个 canvasGradient 对象,并且赋给图形的 fillStyle 或 strokeStyle 属性。

createLinearGradient(x1, y1, x2, y2)
createLinearGradient 方法接受 4 个参数,表示渐变的起点 (x1,y1) 与终点 (x2,y2)
createRadialGradient(x1, y1, r1, x2, y2, r2)
createRadialGradient 方法接受 6 个参数,前三个定义一个以 (x1,y1) 为原点,半径为 r1 的圆,后三个参数则定义另一个以(x2,y2) 为原点,半径为 r2 的圆。
gradient.addColorStop(position, color)
addColorStop 方法接受 2 个参数,position 参数必须是一个 0.0 与 1.0 之间的数值,表示渐变中颜色所在的相对位置。

图案样式patterns

createPattern(image, type)
该方法接受两个参数。Image 可以是一个 Image对象的引用,或者另一个 canvas对象。
Type 必须是下面的字符串值之一:repeatrepeat-xrepeat-yno-repeat

阴影shadow

shadowOffsetX = float;
shadowOffsetY = float

shadowOffsetXshadowOffsetY 用来设定阴影在X和Y轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,它们默认都为 0。

shadowBlur = float

shadowBlur 用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为 0。

shadowColor = color

shadowColor 是标准的 CSS 颜色值,用于设定阴影颜色效果,默认是全透明的黑色。

canvas填充规则:

  • nonzero:non-zero winding rule, 默认值. 填充
  • evenodd: even-odd winding rule,镂空

动画:

  1. 清空canvas:clearRect();
  2. 保存canvas状态
  3. 绘制动画图形:重绘动画帧
  4. 恢复canvas状态:如果已经保存了 canvas的状态,可以先恢复它,然后重绘下一帧。

你可以使用setInterval()方法,它就可以定期执行指定代码。如果我们需要做一个游戏,我们可以使用键盘或者鼠标事件配合上setTimeout()方法来实现。通过设置事件监听,我们可以捕捉用户的交互,并执行相应的动作。 采用window.requestAnimationFrame()实现动画效果。这个方法提供了更加平缓并更加有效率的方式来执行动画,当系统准备好了重绘条件的时候,才调用绘制动画帧。一般每秒钟回调函数执行60次,也有可能会被降低。

变形transformations:

save()restore(): save 和 restore 方法是用来保存和恢复 canvas 状态的,canvas的状态保存在栈中。
tanslate(x, y); 左右,上下平移。
rotate旋转。
scale缩放。
transform(m11, m12, m21, m22, dx, dy) 这个方法是将当前的变形矩阵乘上一个基于自身参数的矩阵,在这里我们用下面的矩阵

m11   m21   dx    
m12   m22   dy  
0     0     1    
 m11:水平方向的缩放     
 m12:水平方向的倾斜偏移     
 m21:竖直方向的倾斜偏移     
 m22:竖直方向的缩放 
 dx:水平方向的移动
 dy:竖直方向的移动
 setTransform(m11, m12, m21, m22, dx, dy)
 resetTransform()

canvas优化

使用requestAnimationFrame进行动画循环

setTimeoutsetInterval并非是专为连续循环产生的 API,所以可能无法达到流畅的动画表现,故用 requestAnimationFrame,可能需要polyfill

const raf = window.requestAnimationFrame
  || window.webkitRequestAnimationFrame
  || window.mozRequestAnimationFrame
  || window.oRequestAnimationFrame
  || window.msRequestAnimationFrame
  || function(callback) {
    window.setTimeout(callback, 1000 / 60)
  }

利用剪辑区域来处理动画背景或其他不变的图像

背景比较复杂可以使用剪辑区域技术,通过每帧较少的绘制来获得较好的性能。

利用剪辑区域技术来恢复上一帧动画所占背景图的执行步骤:

  • 调用 context.save(),保存屏幕 canvas的状态
  • 通过调用 beginPath来开始一段新的路径
  • 在 context对象上调用 arc()、rect()等方法来设置路径
  • 调用 context.clip()方法,将当前路径设置为屏幕 canvas的剪辑区域
  • 擦除屏幕 canvas中的图像(实际上只会擦除剪辑区域所在的这一块范围)
  • 将背景图像绘制到屏幕canvas上(绘制操作实际上只会影响剪辑区域所在的范围,所以每帧绘制图像像素数更少)
  • 恢复屏幕 canvas的状态参数,重置剪辑区域

离屏缓冲区(离屏canvas)

先绘制到一个离屏 canvas中,然后再通过 drawImage把离屏 canvas 画到主 canvas中,就是把离屏 canvas当成一个缓存区。把需要重复绘制的画面数据进行缓存起来,减少调用 canvas的 API的消耗。

const cacheCanvas = document.createElement('canvas')
const cacheCtx = cacheCanvas.getContext('2d')
cacheCtx.width = 200
cacheCtx.height = 200
// 绘制到主canvas上
ctx.drawImage(0, 0)

必要时,可以使用多个离屏 canvas另外,离屏canvas不再使用时,最好把手动将引用重置为 null,避免因为 js和dom之间存在的关联,导致垃圾回收机制无法正常工作,占用资源。

尽量利用CSS

尽量少的调用 canvas API

相比于正常的js操作,频繁的调用canvasAPI更加消耗资源。

避免阻塞

在进行某些耗时操作,例如计算大量数据,一帧中包含了太多的绘制状态,大规模的 DOM操作等,可能会导致页面卡顿,影响用户体验,可以通过以下两种手段:

  • web worker
    web worker最常用的场景就是大量的频繁计算,减轻主线程压力,如果遇到大规模的计算,可以通过此 API分担主线程压力,此 API兼容性已经很不错了,既然 canvas可以用,那 web worker也就完全可以考虑使用。

  • 分解任务
    满足:
    1.循环处理操作并不要求同步
    2.数据并不要求按照顺序处理

      根据任务总量分配:   
      根据运行时间分配:
    

以上内容均是看文档学习归纳的常用的基础的知识点,觉得有用的小伙伴点个小心心吧