使用canvas可以在html5制作出各种绚丽的图表、动画,是制作微信/H5小游戏的基础。
canvas的数学计算公式
坐标系
在数学中坐标系是一个十字形的,原点在中间。而在canvas中原点在画布的左上角。
常用的三角函数
三角函数是初中数学开始学习的知识,但我已经忘记的差不多了,因此这里就多详细整理一下。
sin(α) = y/c
cos(α) = x/c
tan(α) = y/x
常用的反三角函数
sin(α) = y/c
=>α = arcsin(y/c)
cos(α) = x/c
=>α = arccos(c/c)
tan(α) = y/x
=>α = arctan(y/x)
getContext()
getContext() 方法返回canvas上下文环境,可以在创建渲染上下文的时候设置多个属性:
// 二维渲染
canvas.getContext("2d")
// 三维渲染(OpenGL ES 2.0)
canvas.getContext("webgl")
// 三维渲染(OpenGL ES 3.0)
canvas.getContext("webg2")
// 提供功能去替换指定 canvas 的ImageBitmap内容
canvas.getContext("bitmaprenderer")
通常情况下,我们使用 canvas.getContext("2d")
, 即:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
console.log(ctx); // CanvasRenderingContext2D { ... }
图形绘制API
移动笔触
moveTo(x, y) 将笔触移动到指定的坐标x以及y上。
路径
beginPath()
新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。
closePath()
闭合路径之后图形绘制命令又重新指向到上下文中。
stroke()
通过线条来绘制图形轮廓。
fill()
通过填充路径的内容区域生成实心的图形。调用fill时,会自动闭合路径。
clip()
将当前正在构建的路径转换为当前的裁剪路径。
线
lineTo(x, y) 绘制一条从当前位置到指定x以及y位置的直线。如果没有 moveTo 那么第一次 lineTo 的效果等于 moveTo。
线型
lineWidth = value 设置线条宽度。
lineCap = type 设置线条末端样式。
lineJoin = type 设定线条与线条间接合处的样式。
miterLimit = value 限制当两条线相交时交接处最大长度;所谓交接处长度(斜接长度)是指线条交接处内角顶点到外角顶点的长度。
getLineDash() 返回一个包含当前虚线样式,长度为非负偶数的数组。
setLineDash(segments) 设置当前虚线样式。
lineDashOffset = value 设置虚线样式的起始偏移量。
矩形
fillRect(x, y, width, height) 绘制一个填充的矩形
strokeRect(x, y, width, height) 绘制一个矩形的边框
clearRect(x, y, width, height) 清除指定矩形区域,让清除部分完全透明。
rect(x, y, width, height) 将一个矩形路径增加到当前路径上
实心圆
arc(x, y, radius, startAngle, endAngle, anticlockwise) 画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。
圆弧
arcTo(x1, y1, x2, y2, radius) 根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。
注意:
arc()
函数中表示角的单位是弧度,不是角度。角度与弧度的js表达式:弧度=(Math.PI/180)*角度
。arc()
函数如果不闭合路径并且不填充颜色,那么将显示为圆弧。
二次贝塞尔曲线及三次贝塞尔曲线
quadraticCurveTo(cp1x, cp1y, x, y) 绘制二次贝塞尔曲线,cp1x,cp1y为一个控制点,x,y为结束点。
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) 绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。
颜色
设置填充颜色
// 这些 fillStyle 的值均为 '橙色'
ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255,165,0)";
ctx.fillStyle = "rgba(255,165,0,1)";
设置轮廓颜色
ctx.strokeStyle = "orange";
设置透明度值
ctx.globalAlpha = 0.2;
渐变
createLinearGradient(xStart,yStart,xEnd,yEnd).addColorStop(offset,color) 线性渐变
createRadialGradient(xStart,yStart,radiusStart,xEnd,yEnd,radiusEnd).addColorStop(offset,color) 径向渐变
xstart
:渐变开始点x坐标
ystart
:渐变开始点y坐标
xEnd
:渐变结束点x坐标
yEnd
:渐变结束点y坐标
radiusStart
:发散开始圆的半径
radiusEnd
:发散结束圆的半径
offset
:设定的颜色离渐变结束点的偏移量(0~1)
color
:绘制时要使用的颜色
阴影
shadowOffsetX = float
shadowOffsetX 和 shadowOffsetY 用来设定阴影在 X 和 Y 轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,它们默认都为 0。
shadowOffsetY = float
shadowOffsetX 和 shadowOffsetY 用来设定阴影在 X 和 Y 轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,它们默认都为 0。
shadowBlur = float
shadowBlur 用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为 0。
shadowColor = color
shadowColor 是标准的 CSS 颜色值,用于设定阴影颜色效果,默认是全透明的黑色。
填充规则
// 参数: nonzero | evenodd
ctx.fill("evenodd")
“nonzero”是非零环绕原则,也是默认值,“evenodd”为奇偶原则。
通常在填充相交或嵌套路径时使用。
文本
fillText(text, x, y [, maxWidth])
在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的.
strokeText(text, x, y [, maxWidth])
在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的.
文本样式
font = value
当前我们用来绘制文本的样式. 这个字符串使用和 CSS font 属性相同的语法. 默认的字体是 10px sans-serif。
textAlign = value
文本对齐选项. 可选的值包括:start, end, left, right or center. 默认值是 start。
textBaseline = value
基线对齐选项. 可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默认值是 alphabetic。
direction = value
文本方向。可能的值包括:ltr, rtl, inherit。默认值是 inherit。
预测量文本宽度
var text = ctx.measureText("foo");
text.width; // 16;
图片操作
嵌入图片的操作
// 1. new image()
var img = new Image(); // 创建img元素
img.onload = function(){
// 执行drawImage语句
}
img.src = 'myImage.png'; // 设置图片源地址
// 2. data: url
img.src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
drawImage(image, x, y, width, height,x2, y2, width2, height2)
其中 image 是 image 或者 canvas 对象;x 和 y 是其在目标 canvas 里的起始坐标;width 和 height,这两个参数用来控制 当向canvas画入时应该缩放的大小;x2/y2/width2/height2定义切片的目标显示位置和大小。
Path2D对象
Path2D对象可以把svg等内容画在canvas上。
new Path2D(); // 空的Path对象
new Path2D(path); // 克隆Path对象
new Path2D(d); // 从SVG建立Path对象
var p = new Path2D("M10 10 h 80 v 80 h -80 Z"); // 使用svg路径
图案样式
var img = new Image();
img.src = 'someimage.png';
var ptrn = ctx.createPattern(img,'repeat');
注意:
与 drawImage 有点不同,你需要确认 image 对象已经装载完毕,否则图案可能效果不对的。
状态的保存和恢复
save()、restore()
save 和 restore 方法是用来保存和恢复 canvas 状态的,都没有参数。Canvas 的状态就是当前画面应用的所有样式和变形的一个快照。
移动原点和canvas位置
translate(x, y)
translate 方法接受两个参数。x 是左右偏移量,y 是上下偏移量,如右图所示。
旋转
rotate(angle)
这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。
缩放
scale(x, y)
scale 方法接受两个参数。x,y 分别是横轴和纵轴的缩放因子,它们都必须是正值。值比 1.0 小表示缩小,比 1.0
变形
transform(m11, m12, m21, m22, dx, dy)
m11
:水平方向的缩放
m12
:水平方向的倾斜偏移
m21
:竖直方向的倾斜偏移
m22
:竖直方向的缩放
dx
:水平方向的移动
dy
:竖直方向的移动
// 将当前的变形矩阵重置为单位矩阵
setTransform(m11, m12, m21, m22, dx, dy)
// 重置当前变形为单位矩阵
resetTransform()
组合
globalCompositeOperation = type
这个属性设定了在画新图形时采用的遮盖策略,其值是一个标识遮盖方式的字符串。
source-over
这是默认设置,并在现有画布上下文之上绘制新图形。source-in
新图形只在新图形和目标画布重叠的地方绘制。其他的都是透明的。source-out
在不与现有画布内容重叠的地方绘制新图形source-atop
新图形只在与现有画布内容重叠的地方绘制。destination-over
在现有的画布内容后面绘制新的图形。destination-in
现有的画布内容保持在新图形和现有画布内容重叠的位置。其他的都是透明的。destination-out
现有内容保持在新图形不重叠的地方。destination-atop
现有的画布只保留与新图形重叠的部分,新的图形是在画布内容后面绘制的。lighter
两个重叠图形的颜色是通过颜色值相加来确定的。copy
只显示新图形。xor
图像中,那些重叠和正常绘制之外的其他地方是透明的。multiply
将顶层像素与底层相应像素相乘,结果是一幅更黑暗的图片。screen
像素被倒转,相乘,再倒转,结果是一幅更明亮的图片。overlay
multiply和screen的结合,原本暗的地方更暗,原本亮的地方更亮。darken
保留两个图层中最暗的像素。lighten
保留两个图层中最亮的像素。color-dodge
将底层除以顶层的反置。color-burn
将反置的底层除以顶层,然后将结果反过来。hard-light
屏幕相乘(A combination of multiply and screen)类似于叠加,但上下图层互换了。soft-light
用顶层减去底层或者相反来得到一个正值。difference
一个柔和版本的强光(hard-light)。纯黑或纯白不会导致纯黑或纯白。exclusion
和difference相似,但对比度较低。hue
保留了底层的亮度(luma)和色度(chroma),同时采用了顶层的色调(hue)。saturation
保留底层的亮度(luma)和色调(hue),同时采用顶层的色度(chroma)。color
保留了底层的亮度(luma),同时采用了顶层的色调(hue)和色度(chroma)。luminosity
保持底层的色调(hue)和色度(chroma),同时采用顶层的亮度(luma)。
动画
canvas动画通过重绘出每一帧画面,达到连续的动画效果,重绘是相当费时的,而且性能很依赖于电脑的速度。
动画的基本步骤
- 清空 canvas
除非接下来要画的内容会完全充满 canvas (例如背景图),否则你需要清空所有。最简单的做法就是用 clearRect 方法。
- 保存 canvas 状态
如果你要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下。
- 绘制动画图形(animated shapes)
这一步才是重绘动画帧。
- 恢复 canvas 状态
如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。
更新画布
window.setInterval(function, delay)
当设定好间隔时间后,function会定期执行。
window.setTimeout(function, delay)
在设定好的时间之后执行函数
window.requestAnimationFrame(callback)
告诉浏览器你希望执行一个动画,并在重绘之前,请求浏览器执行一个特定的函数来更新动画。
ImageData对象
ImageData对象中存储着canvas对象真实的像素数据,它包含以下几个只读属性:
width
图片宽度,单位是像素
height
图片高度,单位是像素
data
Uint8ClampedArray类型的一维数组,包含着RGBA格式的整型数据,范围在0至255之间(包括255)。
性能优化
offscreenCanvas 离屏画布
当有许多相似图形或重复图像时使用,比如说需要同一个图像的不同尺寸
myEntity.offscreenCanvas = document.createElement("canvas");
myEntity.offscreenCanvas.width = myEntity.width;
myEntity.offscreenCanvas.height = myEntity.height;
myEntity.offscreenContext = myEntity.offscreenCanvas.getContext("2d");
myEntity.render(myEntity.offscreenContext);
用整数坐标代替浮点数坐标
ctx.drawImage(myImage, 0.3, 0.5); // no
使用Math.floor()对所有坐标取整
let x = Math.floor(0.3);
let y = Math.floor(0.5);
ctx.drawImage(myImage, x, y);
使用多层画布去画一个复杂的场景
用多个画布元素去创建不同层次。
用CSS设置大的背景图
可以用div把canvas包裹起来,然后用css设置div的背景图片。
用CSS transforms特性缩放画布
CSS transforms 特性由于调用GPU,因此更快捷。不要将小画布放大,而是去将大画布缩小。
关闭透明度
如果不需要透明度,可以在声明渲染的时候先关闭它。
var ctx = canvas.getContext('2d', { alpha: false });
其他的一些优化小技巧
将画布的函数调用集合到一起(例如,画一条折线,而不要画多条分开的直线)
避免不必要的画布状态改变
渲染画布中的不同点,而非整个新状态
尽可能避免 shadowBlur特性
尽可能避免text rendering
使用不同的办法去清除画布(clearRect() vs. fillRect() vs. 调整canvas大小)
有动画,请使用window.requestAnimationFrame() 而非window.setInterval()
请谨慎使用大型物理库