Canvas 笔记
简介
Canvas API 提供了一个通过JavaScript 和 HTML的<canvas>元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
Canvas API主要聚焦于2D图形。而同样使用<canvas>元素的 WebGL API 则用于绘制硬件加速的2D和3D图形。
canvas 元素
<canvas id="canvas" width="150" height="150"></canvas>
<canvas> 看起来和 <img> 元素很相像,唯一的不同就是它并没有 src 和 alt 属性。实际上,<canvas> 标签只有两个属性—— width和height。这些都是可选的,并且同样利用 DOM properties 来设置。当没有设置宽度和高度的时候,canvas会初始化宽度为300像素和高度为150像素。该元素可以使用CSS来定义大小,但在绘制时图像会伸缩以适应它的框架尺寸:如果CSS的尺寸与初始画布的比例不一致,它会出现扭曲。
注意: 如果你绘制出来的图像是扭曲的, 尝试用width和height属性为<canvas>明确规定宽高,而不是使用CSS。
替换内容
<canvas> 元素与 <img> 标签的不同之处在于,就像<video>,<audio>,或者 <picture>元素一样,很容易定义一些替代内容。由于某些较老的浏览器(尤其是IE9之前的IE浏览器)或者文本浏览器不支持HTML元素"canvas",在这些浏览器上你应该总是能展示替代内容。
这非常简单:我们只是在<canvas>标签中提供了替换内容。不支持<canvas>的浏览器将会忽略容器并在其中渲染后备内容。而支持<canvas>的浏览器将会忽略在容器中包含的内容,并且只是正常渲染canvas。
<canvas id="canvas" width="150" height="150">
浏览器不支持canvas
</canvas>
<canvas id="canvas" width="150" height="150">
<img src="images/浏览器不支持.png" width="150" height="150" alt=""/>
</canvas>
与 <img> 元素不同,<canvas> 元素需要结束标签(</canvas>)。如果结束标签不存在,则文档的其余部分会被认为是替代内容,将不会显示出来。
如果不需要替代内容,一个简单的 <canvas id="foo" ...></canvas> 在所有支持canvas的浏览器中都是完全兼容的。
渲染上下文
<canvas> 元素创造了一个固定大小的画布,它公开了一个或多个渲染上下文,其可以用来绘制和处理要展示的内容。我们将会将注意力放在2D渲染上下文中。其他种类的上下文也许提供了不同种类的渲染方式;比如, WebGL 使用了基于OpenGL ES的3D上下文 ("experimental-webgl") 。
canvas起初是空白的。为了展示,首先脚本需要找到渲染上下文,然后在它的上面绘制。<canvas> 元素有一个叫做 getContext() 的方法,这个方法是用来获得渲染上下文和它的绘画功能。getContext()只有一个参数,上下文的格式。对于2D图像而言,如本教程,你可以使用 CanvasRenderingContext2D。
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
代码的第一行通过使用 document.getElementById() 方法来为 <canvas> 元素得到DOM对象。一旦有了元素对象,你可以通过使用它的getContext() 方法来访问绘画上下文。
基础示例
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.moveTo(200, 50);
ctx.lineTo(300, 50);
ctx.stroke();
绘制基本图形
绘制矩形
fillRect(x, y, width, height)
strokeRect(x, y, width, height)
clearRect(x, y, width, height)
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.fillRect(50, 100, 100, 100);
ctx.clearRect(75, 125, 50, 50);
ctx.strokeRect(85, 135, 30, 30);
绘制路径
图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。一个路径,甚至一个子路径,都是闭合的。使用路径绘制图形需要一些额外的步骤。
1. 首先,你需要创建路径起始点。
2. 然后你使用画图命令去画出路径。
3. 之后你把路径封闭。
4. 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。
beginPath()
closePath()
stroke()
fill()
移动笔触
一个非常有用的函数,而这个函数实际上并不能画出任何东西,也是上面所描述的路径列表的一部分,这个函数就是moveTo()。或者你可以想象一下在纸上作业,一支钢笔或者铅笔的笔尖从一个点到另一个点的移动过程。
moveTo(x, y)
线
绘制直线,需要用到的方法lineTo()。
lineTo(x, y)
圆弧
绘制圆弧或者圆,我们使用arc()方法。当然可以使用arcTo()。
arcTo(x1, y1, x2, y2, radius)
arc(x, y, radius, startAngle, endAngle, anticlockwise)
详细介绍一下arc方法,该方法有六个参数:
x,y为绘制圆弧所在圆上的圆心坐标。
radius为半径。
startAngle以及endAngle参数用弧度定义了开始以及结束的弧度。这些都是以x轴为基准。
参数anticlockwise为一个布尔值。为true时,是逆时针方向,否则顺时针方向。
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.arc(100, 100, 50, 0, Math.PI * 2, true);
ctx.stroke();
二次贝塞尔曲线及三次贝塞尔曲线
一个十分有用的路径类型就是贝塞尔曲线。二次及三次贝塞尔曲线都十分有用,一般用来绘制复杂有规律的图形。
quadraticCurveTo(cp1x, cp1y, x, y)
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
二次贝塞尔曲线有一个开始点、一个结束点以及一个控制点,而三次贝塞尔曲线有两个控制点。
参数x、y在这两个方法中都是结束点坐标。cp1x,cp1y为坐标中的第一个控制点,cp2x,cp2y为坐标中的第二个控制点。
Path2D 对象
正如我们在前面例子中看到的,你可以使用一系列的路径和绘画命令来把对象“画”在画布上。为了简化代码和提高性能,Path2D对象已可以在较新版本的浏览器中使用,用来缓存或记录绘画命令,这样你将能快速地回顾路径。
new Path2D();
new Path2D(path);
new Path2D(d);
Path2D.addPath(path [, transform])
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const rectangle = new Path2D();
rectangle.rect(10, 10, 50, 50);
ctx.stroke(rectangle);
ctx.fill(rectangle);
SVG paths
新的Path2D API有另一个强大的特点,就是使用SVG path data来初始化canvas上的路径。这将使你获取路径时可以以SVG或canvas的方式来重用它们。
这条路径将先移动到点 (M10 10) 然后再水平移动80个单位(h 80),然后下移80个单位 (v 80),接着左移80个单位 (h -80),再回到起点处 (z)。
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
var p = new Path2D("M10 10 h 80 v 80 h -80 Z");
ctx.fill(p);
样式和颜色
色彩 Colors
如果我们想要给图形上色,有两个重要的属性可以做到:
fillStyle = color
strokeStyle = color
ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255,165,0)";
ctx.fillStyle = "rgba(255,165,0,1)";
注意: 一旦您设置了 strokeStyle 或者 fillStyle 的值,那么这个新值就会成为新绘制的图形的默认值。如果你要给每个图形上不同的颜色,你需要重新设置 fillStyle 或 strokeStyle 的值。
透明度 Transparency
除了可以绘制实色图形,我们还可以用 canvas 来绘制半透明的图形。通过设置 globalAlpha 属性或者使用一个半透明颜色作为轮廓或填充的样式。
globalAlpha = 1.0
线型 Line styles
可以通过一系列属性来设置线的样式
lineWidth = value
lineCap = type
lineJoin = type
miterLimit = value
getLineDash()
setLineDash(segments)
lineDashOffset = value
lineWidth
属性
这个属性设置当前绘线的粗细。属性值必须为正数。默认值是1.0。
线宽是指给定路径的中心到两边的粗细。换句话说就是在路径的两边各绘制线宽的一半。因为画布的坐标并不和像素直接对应,当需要获得精确的水平或垂直线的时候要特别注意。
lineCap
属性
属性 lineCap 的值决定了线段端点显示的样子。
它可以为下面的三种的其中之一:butt,round 和 square。
默认是 butt。
lineJoin
属性
lineJoin 的属性值决定了图形中两线段连接处所显示的样子。
它可以是这三种之一:round, bevel 和 miter。
默认是 miter。
miterLimit
属性
miterLimit 属性就是用来设定外延交点与连接点的最大距离,如果交点距离大于此值,连接效果会变成了 bevel。注意,最大斜接长度(即交点距离)是当前坐标系测量线宽与此miterLimit属性值(HTML <canvas>默认为10.0)的乘积,所以miterLimit可以单独设置,不受显示比例改变或任何仿射变换的影响:它只影响线条边缘的有效绘制形状
使用虚线
用 setLineDash 方法和 lineDashOffset 属性来制定虚线样式. setLineDash 方法接受一个数组,来指定线段与间隙的交替;lineDashOffset 属性设置起始偏移量.
ctx.setLineDash([4, 2]);
渐变 Gradients
就好像一般的绘图软件一样,我们可以用线性或者径向的渐变来填充或描边。我们用下面的方法新建一个 canvasGradient 对象,并且赋给图形的 fillStyle 或 strokeStyle 属性。
createLinearGradient(x1, y1, x2, y2)
createRadialGradient(x1, y1, r1, x2, y2, r2)
gradient.addColorStop
const lineargradient = ctx.createRadialGradient(100, 100, 0, 100, 100, 50);
lineargradient.addColorStop(0, '#fff');
lineargradient.addColorStop(1, '#50c5f1');
ctx.fillStyle = lineargradient;
ctx.arc(100, 100, 50, 0, Math.PI * 2, true);
ctx.fill();
图案样式 Patterns
createPattern(image, type)
注意: 用 canvas 对象作为 Image 参数在 Firefox 1.5 (Gecko 1.8) 中是无效的。
注意:与 drawImage 有点不同,你需要确认 image 对象已经装载完毕,否则图案可能效果不对的。
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
img.onload = () => {
const ptrn = ctx.createPattern(img, 'repeat');
ctx.fillStyle = ptrn;
ctx.fillRect(0, 0, 400, 400);
}
阴影 Shadows
shadowOffsetX = float
shadowOffsetY = float
shadowBlur = float
shadowColor = color
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
ctx.shadowColor = 'rgb(193, 193, 193)';
ctx.shadowBlur = 3;
ctx.font = "40px 微软雅黑";
ctx.fillText('Hello Canvas', 100, 100);
绘制文本
canvas提供了两种方法来渲染文本
fillText(text, x, y [, maxWidth])
strokeText(text, x, y [, maxWidth])
有样式的文本
font = value
textAlign = value
textBaseline = value
direction = value
预测量文本宽度
当你需要获得更多的文本细节时,下面的方法可以给你测量文本的方法。
measureText()
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const text = ctx.measureText("Hello");
console.log(text.width);
图像
canvas更有意思的一项特性就是图像操作能力。可以用于动态的图像合成或者作为图形的背景,以及游戏界面(Sprites)等等。浏览器支持的任意格式的外部图片都可以使用,比如PNG、GIF或者JPEG。 你甚至可以将同一个页面中其他canvas元素生成的图片作为图片源。
获取需要绘制的图片
canvas的API可以使用下面这些类型中的一种作为图片的源:
HTMLImageElement
HTMLVideoElement
HTMLCanvasElement
ImageBitmap
绘制图片
一旦获得了源图对象,我们就可以使用 drawImage 方法将它渲染到 canvas 里。
drawImage 方法有三种形态:
drawImage(image, x, y)
drawImage(image, x, y, width, height)
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
控制图像的缩放行为
过度缩放图像可能会导致图像模糊或像素化。
您可以通过使用绘图环境的imageSmoothingEnabled属性来控制是否在缩放图像时使用平滑算法。
默认值为true,即启用平滑缩放。
您也可以像这样禁用此功能:
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
变形 Transformations
状态的保存和恢复
save()
restore()
你可以调用任意多次 save方法。每一次调用 restore 方法,上一个保存的状态就从栈中弹出,所有设定都恢复。
移动 Translating
translate(x, y)
旋转 Rotating
rotate(angle)
旋转的中心点始终是 canvas 的原点,如果要改变它,我们需要用到 translate 方法。
缩放 Scaing
我们用它来增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大。
scale(x, y)
变形 Transforms
transform(a, b, c, d, e, f)
a (m11)
b(m12)
c(m21)
d(m22)
e(dx)
f(dy)
setTransform(a, b, c, d, e, f)
resetTransform()
合成与裁切
组合 Compositing
globalCompositeOperation = type
'source-over'
'source-in'
'source-out'
'source-atop'
'destination-over'
'destination-in'
'destination-out'
'destination-atop'
'lighter'
'copy'
'xor'
'multiply'
'screen'
'overlay'
'darken'
'lighten'
'color-dodge'
'color-burn'
'hard-light'
'soft-light'
'difference'
'exclusion'
'hue'
'saturation'
'color'
'luminosity'
裁切路径
裁切路径和普通的 canvas 图形差不多,不同的是它的作用是遮罩,用来隐藏不需要的部分。
clip()
ctx.fillRect(100, 50, 200, 200);
ctx.arc(200, 150, 75, 0, Math.PI * 2, true);
ctx.clip();
ctx.fillStyle = 'green';
ctx.fillRect(100, 50, 200, 200);
基本动画
动画的基本步骤
1. 清空 canvas
除非接下来要画的内容会完全充满 canvas (例如背景图),否则你需要清空所有。最简单的做法就是用 clearRect 方法。
2. 保存 canvas 状态
如果你要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下。
3. 绘制动画图形(animated shapes)
这一步才是重绘动画帧。
4. 恢复 canvas 状态
如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。
更新画布
setInterval(function, delay) (en-US)
setTimeout(function, delay) (en-US)
requestAnimationFrame(callback)