1. HTML CanvasElement(IE9+)
HTML CanvasElement 可以看成是所有canvas元素的代称,是一个在浏览器可视窗口可以全局访问的接口。例如运行window.HTMLCanvasElement
ƒ HTMLCanvasElement() { [native code] }
这些接口都是DOM Level 2中出现的,因此,IE9+浏览器才支持,IE8浏览器并不支持。考虑到<canvas>元素也是IE9+开始支持的,因此,如果你有需要使用HTMLCanvasElement的场景,就无需担心兼容性问题。
类似的还有 HTMLDivElement(对应<div>元素),HTMLImageElement(对应<img>元素), HTMLParagraphElement(对应<p>元素)等一些列接口,几乎所有的标准HTML标签都有一个对应的接口名称,然后全部都继承于HTMLElement,完整继承关系见下图。
通常这些接口会内置一些属性和方法,方便用户使用。我们也可以根据自己需求,自定义一些方法,方便调用。例如,下面这个检测canvas画布上是否有透明或半透明像素点的方法:
// 检测canvas画布上是否有透明或半透明像素点
HTMLCanvasElement.prototype.isSomeAlphaPixel = function () {
var context = this.getContext('2d');
// 获取图片像素信息
var imageData = context.getImageData(0, 0, this.width, this.height).data;
// 检测有没有透明数据
var isAlphaBackground = false;
for (var index = 3; index < imageData.length; index += 4) {
if (imageData[index] != 255) {
isAlphaBackground = true;
break;
}
}
return isAlphaBackground;
};
此时,页面上任意<canvas>元素DOM对象都可以调用isSomeAlphaPixel()方法。例如:
var canvas = document.querySelector('canvas');
// 输出true或false
console.log(canvas.isSomeAlphaPixel());
1.1. height 和 width属性
- height 获取、设置画布高度。
- width获取、设置画布宽度。
// 获取高度
var pixel = canvas.height;
// 设置高度
canvas.height = pixel;
// 获取宽度
var pixel = canvas.width;
// 设置宽度
canvas.width = pixel;
虽然height标准属性值只能是整数,但是实际上:
-
如果设置小数,浏览器不会认为是不合法的。例如:
<canvas height="88.888" style="background:green;"></canvas> -
经测试,各个主流浏览器下都会渲染为
88像素高度。width="188em"
-
如果宽度值后面是个完全不合法的字符,则按照前面的数值进行宽度解析,例如
width="188abc"
- 宽度高度负数会展示为150像素,IE浏览器会展示为0
1.2. getContext()返回上下文
var context = canvas.getContext(contextType);
var context = canvas.getContext(contextType, contextAttributes);
- contextType:String
- "2d"
会创建并返回一个
CanvasRenderingContext2D对象,主要用来进行2d绘制,也就是二维绘制,平面绘制。 "webgl"或"experimental-webgl"
此参数可以返回一个WebGLRenderingContext(WebGL渲染上下文)对象,WebGL(全写Web Graphics Library)是一种3D绘图协议,可以为HTML5 Canvas提供硬件3D加速渲染,这样Web开发人员就可以借助系统显卡来在浏览器里更流畅地展示3D场景和模型,无需安装任何其他插件。此参数对应的WebGL版本1(OpenGL ES 2.0
"webgl2"此参数可以返回一个
WebGL2RenderingContext对象,可以用来绘制三维3D效果。此参数对应的WebGL版本2(OpenGL ES 3.0)。不过目前这个还处于试验阶段,我们实际Kaufman都是使用'webgl'这个参数。"bitmaprenderer"创建一个
ImageBitmapRenderingContext(位图渲染上下文),可以借助给定的ImageBitmap替换Canavs的内容。
2. contextAttribute(可选):Object
contextAttributes为一个纯对象参数,下面是一个使用示意:
var gl = canvas.getContext('webgl', {
antialias: false,
depth: false
});
该参数对象支持的属性值具体如下:
如果contextType参数值是'2d',则contextAttributes支持的标准属性值为:
- alphaBoolean
表示Canavs是否包含alpha透明通道,如果设置为false,则表示Canvas不支持全透明或者半透明,在绘制带有透明效果的图形或者图像时候速度会更快一些。
如果contextType参数值是'webgl',则contextAttributes支持的标准属性值为:
- alphaBoolean
表示Canavs是否包含透明缓冲区。
- antialiasBoolean
表示是否需要抗边缘锯齿。如果设置为true,图像呈现质量会好一些,但是速度会拖慢。
- depthBoolean
表示绘制缓冲区的缓冲深度至少16位。
- failIfMajorPerformanceCaveatBoolean
表示如果用户的系统性能比较差,是否继续常见绘制上下文。
- powerPreferenceString
高速用户使用的客户端(如浏览器)我们现在这个WebGL上下文最合适的GPU配置是什么。支持下面关键字值:
- 'default'
让用户的客户端设备自己觉得那个GPU配置是最合适的。这个是此参数的默认值。
- 'high-performance'
渲染性能优先,通常更耗掉(如手机,平板等移动设备)。
- 'low-power'
省电优先,渲染性能权重可以低一些。
- premultipliedAlphaBoolean
表示页面合成器将假定绘图缓冲区包含具有alpha预乘(pre-multiplied alpha)颜色。
- preserveDrawingBufferBoolean
如果值为true,则不会清除缓冲区并保留其值,直到作者清除或覆盖。
- stencilBoolean
表示绘图缓冲区具有至少8位的模板缓冲区。
返回值
无论getContext()方法中的参数是什么,其返回值都可以称之为RenderingContext,再细分可以包括下面这些:
- '2d'参数值对应的CanvasRenderingContext2D;
- 'webgl'或experimental-webgl参数值对应的WebGLRenderingContext;
- 'webgl2'参数值对应的WebGL2RenderingContext;
- 'bitmaprenderer'参数值对应的ImageBitmapRenderingContext。
基本上,只要我们使用canvas实现功能,getContext()方法调用100%需要使用。甚至可以这么说,只要你想用canvas实现任何平面效果,首先不管三七二十一,先把下面2行写上:
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
当然如果页面存在多个<canvas>元素,我们可以自己加个id或者class类名在配合选择器获取。也可以直接create创建,然后append到页面中,例如:
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var context = canvas.getContext('2d');
如果希望<canvas>元素放在<body>元素最前面,则可以:
var canvas = document.createElement('canvas');
document.body.insertBefore(canvas, document.body.firstElementChild);
var context = canvas.getContext('2d');
1.3. toBlob()将Canvas图像转为Blob对象(IE10+)
toBlob()方法可以Canvas图像对应的Blob对象(binary large object)。此方法可以把Canvas图像缓存在磁盘上,或者存储在内存中,这个往往由浏览器决定。
void canvas.toBlob(callback, mimeType, quality);
这里的void表示无返回值。
参数说明
- Callback:Function (回调函数)
toBlob()方法执行成功后的回调方法,支持一个参数,表示当前转换的Blob对象。
- mimeType:(可选)String (图片类型)
mimeType表示需要转换的图像的mimeType类型。默认值是image/png,还可以是image/jpeg,甚至image/webp(前提浏览器支持)等。
- quality:(可选)Number (图片质量(0-1))
quality表示转换的图片质量。范围是0到1。由于Canvas的toBlob()方法转PNG是无损的,因此,此参数默认是没有效的,除非,指定图片mimeType是image/jpeg或者image/webp,此时默认压缩值是0.92。
案例1:Blob图像上传
这个案例演示的是Canvas图像转换成Blob二进制对象并使用HTML5 FormData进行Ajax上传
<canvas></canvas>
假设上面<canvas>是已经绘制好的图形,我们需要Ajax提交到后台进行保存,则JavaScript代码可以这样:
var canvas = document.querySelector('canvas');
// canvas转为blob并上传
canvas.toBlob(function (blob) {
var data = new FormData();
// 装载图片数据
data.append('image', blob);
// 图片ajax上传,字段名是image
var xhr = new XMLHttpRequest();
// 文件上传成功
xhr.onload = function() {
// xhr.responseText就是返回的数据
};
// 开始上传
xhr.open('POST', 'upload.php', true);
xhr.send(data);
});
如果我们希望把<canvas>元素图像使用<img>元素显示,toBlob()和toDataURL()方法都是可以的,但个人推荐使用toBlob()方法(如果不用顾及兼容性)。
blob数据对象是无法直接作为<img>的src属性值呈现的,需要URL.createObjectURL()方法处理下。
具体使用可参见下面一个图片水印合成的例子,点击下面的按钮选择合适的图片,会得到一个Blob形式的合成图片,当我们打开开发者工具查看上图(假设你已经选择了图片),会发现其src是一个blob地址,如下截图示意:

Blob数据转URL地址关键JavaScript代码如下:
canvas.toBlob(function(blob) {
var url = URL.createObjectURL(blob);
p.innerHTML = '<img src="'+ url +'">';
}, 'image/jpeg');
另外,如果图片转换交互频繁,性能开销比较大,且图片仅展示无其它数据层面的交互,我们可以使用URL.revokeObjectURL(url)释放资源。不过实际开发页面通常都不复杂,不释放也没关系。
首先,toBlob()方法IE9浏览器不支持,因为Blob数据格式IE10+才支持。
然后,对于IE浏览器,toBlob()的兼容性有些奇怪,IE10浏览器支持ms私有前缀的toBlob()方法,完整方法名称是msToBlob()。而IE11+,toBlob()方法却不支持。
但是,我们可以基于toDataURL()方法进行polyfill,性能相对会差一些,JavaScript代码如下,参考自MDN:
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
value: function (callback, type, quality) {
var canvas = this;
setTimeout(function() {
var binStr = atob( canvas.toDataURL(type, quality).split(',')[1] );
var len = binStr.length;
var arr = new Uint8Array(len);
for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i);
}
callback(new Blob([arr], { type: type || 'image/png' }));
});
}
});
}
1.4. 头、DataURL()方法
Canvas本质上就是一个位图图像,因此,浏览器提供了若干API可以将Canvas图像转换成可以作为IMG呈现的数据,其中最老牌的方法就是HTMLCanvasElement.toDataURL(),此方法可以返回Canvas图像对应的data URI,也就是平常我们所说的base64地址。格式如下:
data:[<mime type>][;base64],<data>
我们可以指定转换的图片格式,转换后的图像的分辨率为96 dPI。
canvas.toDataURL(mimeType, quality);
参数说明
- mimeType:(可选)String
mimeType表示需要转换的图像的mimeType类型。默认值是image/png,还可以是image/jpeg,甚至image/webp(前提浏览器支持)等。
quality:(可选)Number
quality表示转换的图片质量。范围是0到1。此参数要想有效,图片的mimeType需要是image/jpeg或者image/webp,其他mimeType值无效。默认压缩质量是0.92。
根据自己的肉眼分辨,如果使用toDataURL()的quality参数对图片进行压缩,同样的压缩百分比呈现效果要比Adobe Photoshop差一些。
返回值:返回base64 data图片数据。
案例
案例1:最基础的使用
如下,一个10x10纯透明Canvas:
<canvas width="10" height="10"></canvas>
如下JavaScript代码可以返回此Canvas的data-URL:
var canvas = document.querySelector('canvas');
var dataURL = canvas.toDataURL();console.log(dataURL);
// 结果是:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAF0lEQVQoU2NkIBIwEqmOYVQh3pAiOngACmkAC5eMKzgAAAAASUVORK5CYII="
案例2:转换成JPG图片
如果Canvas中颜色信息非常丰富,如实物照片,则建议指定mime type为image/jpeg,data-URL数据量会大大降低,但质量也依然很OK。例如:
canvas.toDataURL('image/jpeg');
如果Canvas图像本身是2倍图(画布实际尺寸是CSS样式尺寸的2倍+),则图像理论质量0.5~0.6足矣。但考虑到Canvas本身压缩后的图像品质不算高,实际质量值建议0.6~0.7。如果是一倍图,则使用默认值即可。
canvas.toDataURL('image/jpeg', 0.6);
案例3:检测浏览器是否支持webp
var isSupportWebp = !![].map && document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0;
console.log(isSupportWebp); // true or false
2. CanvasRenderingContext2D
CanvasRenderingContext2D顾名思义就是“Canvas 2D渲染上下文”,可以理解为下面代码中的context。
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
context暴露了大量的API属性和方法,可以用来绘制文本,图形以及像素处理等,可以说是2D Canvas应用的核心所在。
按照功能,可以对这些暴露的标准API属性和方法进行如下的分组:
绘制矩形
(1) clearRect(): 清除Canvas元素的一块矩形的内容
context.clearRect(x, y, width, height);
x: number 矩形左上角的x坐标
y: number 矩形左上角的y坐标
width: number 要清除的矩形宽度
height: number 要清除的矩形高度
// 示例
// 先绘制图片
var img = new Image();
img.onload = function () {
context.drawImage(img, 0, 0, 250, 167);
// 中间开个方形的洞
context.clearRect(50, 50, 100, 66);
};
img.src = './1.jpg';

(2) fillStyle():填充canvas
context.fillStyle = color;//纯色填充
context.fillStyle = gradient;//渐变色填充
context.fillStyle = pattern;//纹理填充、图片填充
纯色
<canvas id="canvasColor"></canvas>
var canvasColor = document.getElementById('canvasColor');
var contextColor = canvasColor.getContext('2d');
contextColor.fillStyle = 'RGB(255, 0, 0)';
contextColor.fillRect(10, 10, 100, 100);

所以我们借助createLinearGradient() API实现一个从红色到绿色的线性渐变填充效果,以及createRadialGradient()实现径向渐变的效果:
线性渐变
<canvas id="canvasLinear"></canvas>
var canvasLinear = document.getElementById('canvasLinear');
var contextLinear = canvasLinear.getContext('2d');
// 创建线性渐变对象
var gradientLinear = contextLinear.createLinearGradient(0, 0, 0, 100);
gradientLinear.addColorStop(0, 'red');// 从位置0开始时红色
gradientLinear.addColorStop(1, 'green');//到结束位置1的绿色
// 填充线性渐变
contextLinear.fillStyle = gradientLinear;
contextLinear.fillRect(10, 10, 100, 100);
径向渐变
<canvas id="canvasRadial"></canvas>
var canvasRadial = document.getElementById('canvasRadial');
var contextRadial = canvasRadial.getContext('2d');
// 创建径向渐变对象,半径50
var gradientRadial = contextRadial.createRadialGradient(60, 60, 0, 60, 60, 50);
gradientRadial.addColorStop(0, 'red');
gradientRadial.addColorStop(1, 'green');
// 填充径向渐变
contextLinear.fillStyle = gradientRadial;
contextLinear.fillRect(10, 10, 100, 100);


图片填充
<canvas id="canvasPattern"></canvas>
var canvasPattern = document.getElementById('canvasPattern');
var contextPattern = canvasPattern.getContext('2d');
// 创建图案对象
var imagePattern = document.createElement('img');
imagePattern.onload = function () {
// 缩放原始图片到50*50大小
var canvas = document.createElement('canvas');
canvas.width = canvas.height = 50;
var context = canvas.getContext('2d');
// 通过drawImage()方法缩放
context.drawImage(this, 0, 0, 50, 50);
// 把这个创建的canvas图形作为图案使用
var pattern = contextPattern.createPattern(canvas, 'repeat');
// 填充图案
contextPattern.fillStyle = pattern;
contextPattern.fillRect(10, 10, 100, 100);
};
imagePattern.src = './pattern.jpg';

(3) strokeRect():矩形描边效果
context.strokeRect(x, y, width, height);
- x:描边矩形起点横坐标
- y:描边矩形起点纵坐标
- width:描边矩形宽度
- height:描边矩形高度
// 2像素宽矩形描边
context.lineWidth = 2;// 定义描边的线宽度为2
context.strokeRect(75, 25, 150, 100);// 定义一个矩形描边,起点为75,25.长度为150,宽度为100

绘制文本
(1)fillText() 文字填充(可以填充纯色,渐变,图片)
是绘制文本的主力方法。
context.fillText(text, x, y [, maxWidth]);
- text:文本信息
- x:填充文本的起点坐标
- y:填充文本的终点坐标
- maxWidth(可选):填充文本的最大宽度,文本 超出时,通过压缩文本宽度进行适配。
context.font = '24px STheiti, SimHei';
context.fillText('Canvas API中文网', 24, 66,200);
可以看到文本水平方向压缩了。
-
wrapText():文本自动换行
context.font = '24px STheiti, SimHei';
context.wrapText('Canvas API中文网,不只是文档', 24, 56, 200);

(2) strokeText():给文字描边
context.strokeText(text, x, y [, maxWidth]);
- text:文本内容
- x:起点横坐标
- y:起点纵坐标
- maxWidth【可选】:填充文本的最大宽度,超出换行
// 文字描边 context.font = '50px STHeiti, SimHei'; context.strokeText('文字描边', 50, 90);
实色文字描边
// 文字样式 context.font = '50px STHeiti, SimHei'; // 文字先描边 context.lineWidth = 3; context.strokeStyle = 'red'; context.strokeText('文字描边', 50, 90); // 再填充 context.fillText('文字描边', 50, 90);
(3) measureText(text):测量文本字符宽度等信息。
measureText()方法很实用,是文本自动换行效果实现的核心所在,此方法返回的字符宽度值非常精准。
context.measureText(text)
- text:被测量的文本。
// 设置字体字号 context.font = '24px STHeiTi, SimHei'; // 文本信息对象就有了 var textZh = context.measureText('帅'); var textEn = context.measureText('handsome'); // 文字绘制 context.fillText('帅', 60, 50); context.fillText('handsome', 60, 90); // 显示宽度 context.font = '12px Arial'; context.fillStyle = 'red'; context.fillText('宽' + textZh.width, 62 + textZh.width, 40); context.fillText('宽' + textEn.width, 62 + textEn.width, 80);
线条样式
(1)lineWidth:线条宽度,默认1,支持小数
context.lineWidth = value;value 表示线的宽度。数值类型,默认值是
1.0。如果是负数,0,NaN,或者Infinity都会忽略。<canvas width="240" height="120"></canvas> // 随机三角形 var canvas = document.querySelector('canvas'); var context = canvas.getContext('2d'); var width = canvas.width; var height = canvas.height; // 随机三个坐标点 var positionA = [width * Math.random(), height / 2 * Math.random()]; var positionB = [width / 2 * Math.random(), height / 2 + height / 2 * Math.random()]; var positionC = [width / 2 + width / 2 * Math.random(), height / 2 + height / 2 * Math.random()]; // 开始绘制 context.beginPath(); context.moveTo(positionA[0], positionA[1]); context.lineTo(positionB[0], positionB[1]); context.lineTo(positionC[0], positionC[1]); context.closePath(); // 绘制,由于默认宽度就是1像素,因此 // context.lineWidth设置缺省 context.stroke();
(2)lineCap:线条端点的样式。(线帽)
- butt(默认值,断头,无端帽)
round(圆形端帽)square(方形端帽)context.lineCap = 'butt'; context.lineCap = 'round'; context.lineCap = 'square';
绘制一个loading菊花效果,点击菊花可以旋转,如下:
loading图标实际上就是由一些线条组成,同时设置线断点为圆弧,这样看上去更舒适些。
相关代码如下:
<canvas width="40" height="40"></canvas> // 圆心坐标 var center = [20, 20]; // 线长度和距离圆心距离 var length = 8, offset = 8; // 开始绘制 context.lineWidth = 4; context.lineCap = 'round'; for (var angle = 0; angle < 360; angle += 45) { // 正余弦 var sin = Math.sin(angle / 180 * Math.PI); var cos = Math.cos(angle / 180 * Math.PI); // 开始绘制 context.beginPath(); context.moveTo(center[0] + offset * cos, center[1] + offset * sin); context.lineTo(center[0] + (offset + length) * cos, center[1] + (offset + length) * sin); context.strokeStyle = 'rgba(0,0,0,'+ (0.25 + 0.75 * angle / 360) +')'; context.stroke(); }
(2)lineJoin:线条转角的样式。
miter(默认值,尖角)round(圆角)bevel(平角,平头)context.lineJoin = 'miter'; context.lineJoin = 'round'; context.lineJoin = 'bevel';
案例:绘制一个圆润的三角箭头(单箭头,双箭头,双向单箭头)
<canvas id="arrow" width="210" height="100"></canvas> var canvas = document.getElementById('arrow'); var context = canvas.getContext('2d'); // 起止点坐标,这里是示意 var start = { x: 20, y: 20 }; var end = { x: 190, y: 80 }; // 计算两点距离,主要是为了计算斜率 var distanceX = end.x - start.x, distanceY = end.y - start.y; var distance = Math.sqrt(distanceY * distanceY + distanceX * distanceX); // 箭头的尺寸 var distanceArrow = 7, sharpeArrow = 3; // 先确定轴线与三角两个尖角点交汇坐标 var arrowMoveTo = { x: start.x + distanceArrow * distanceX / distance, y: start.y + distanceArrow * distanceY / distance }; var arrowLineTo = { x: end.x - distanceArrow * distanceX / distance, y: end.y - distanceArrow * distanceY / distance }; // 4个对称点坐标 var arrowTo1 = { x: arrowMoveTo.x - sharpeArrow * distanceY / distance, y: arrowMoveTo.y + sharpeArrow * distanceX / distance }; var arrowTo2 = { x: arrowMoveTo.x + sharpeArrow * distanceY / distance, y: arrowMoveTo.y - sharpeArrow * distanceX / distance }; var arrowTo3 = { x: arrowLineTo.x - sharpeArrow * distanceY / distance, y: arrowLineTo.y + sharpeArrow * distanceX / distance }; var arrowTo4 = { x: arrowLineTo.x + sharpeArrow * distanceY / distance, y: arrowLineTo.y - sharpeArrow * distanceX / distance }; // 设置线的粗细和断点,转角样式 context.lineWidth = 2; context.lineCap = 'round'; context.lineJoin = 'round'; // 绘制方法 var draw = function (arrow) { arrow = arrow || 'single'; // 清除画布 context.clearRect(0, 0, canvas.width, canvas.height); // 开始绘制 context.beginPath(); // 三种箭头类型 switch (arrow) { case 'single': { context.moveTo(start.x, start.y); context.lineTo(end.x, end.y); // 两个结束对称点 context.lineTo(arrowTo3.x, arrowTo3.y); context.lineTo(arrowTo4.x, arrowTo4.y); // 回到结束点 context.lineTo(end.x, end.y); break; } case 'both': { context.moveTo(start.x, start.y); // 两个起始对称点 context.lineTo(arrowTo1.x, arrowTo1.y); context.lineTo(arrowTo2.x, arrowTo2.y); // 回到起始点 context.lineTo(start.x, start.y); // 重复single的绘制 context.lineTo(end.x, end.y); context.lineTo(arrowTo3.x, arrowTo3.y); context.lineTo(arrowTo4.x, arrowTo4.y); context.lineTo(end.x, end.y); break; } case 'part-both': { // 先绘制起止线 context.moveTo(start.x, start.y); context.lineTo(end.x, end.y); // 结束点位置的半个箭头 context.lineTo(arrowTo4.x, arrowTo4.y); context.lineTo(arrowLineTo.x, arrowLineTo.y); context.closePath(); // 另一端的半箭头 context.moveTo(start.x, start.y); context.lineTo(arrowTo1.x, arrowTo1.y); context.lineTo(arrowMoveTo.x, arrowMoveTo.y); break; } } // 闭合,描边与填充 context.closePath(); context.stroke(); context.fill(); }; // 绘制单箭头 draw(); // 绘制双箭头 // draw('both'); // 绘制双向单侧箭头 // draw('part-both');
(3)miterLimit:尖角限制比率,默认10,如果是负数,0,NaN,或者Infinity都会忽略。
context.miterLimit = value;

0 和 10
(4)getLineDash() 返回当前虚线数值
(5)setLineDash() 设置线条为虚线
ctx.setLineDash(segments);
案例
绘制一条虚线,只是虚线多尺寸并存:
context.beginPath(); context.setLineDash([5, 10, 15, 30]); context.moveTo(20, 70); context.lineTo(280, 70); context.stroke();
(6)lineDashOffset() 设置虚线的起始偏移
context.lineDashOffset = value;
value 表示虚线起始绘制的偏移距离,为浮点型,默认值是0.0。

<canvas id="canvas" width="240" height="120"></canvas>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
// 偏移大小
var offset = 0;
// 绘制
var draw = function () {
context.clearRect(0,0, canvas.width, canvas.height);
context.setLineDash([8, 4]);
context.lineDashOffset = offset;
context.strokeRect(2, 2, 236, 116);
}
var run = function () {
offset += 0.5;
if (offset > 24) {
offset = 0;
}
draw();
// 继续绘制
requestAnimationFrame(run);
}
run();
文本样式
下面就是与Canvas中与文本样式相关的若干属性和方法。
设置字体相关样式,包括字号,字体信息。默认值是10px sans-serif。
CanvasRenderingContext2D.textAlign
设置文本水平对齐方式。支持属性值有:start(默认值),end,left,right以及center。
CanvasRenderingContext2D.textBaseline
设置文本基线对齐方式。支持属性值有:top,hanging,middle,alphabetic(默认值),ideographic,bottom。
CanvasRenderingContext2D.direction
设置文本显示方向。支持属性值有:inherit(默认值),ltr和rtl。
填充和描边
就是下面这两个属性和两个方法,是高频使用的API。
(1) CanvasRenderingContext2D.fillStyle
填充样式。默认值是#000000纯黑色。
(2) CanvasRenderingContext2D.fill()
填充。
(3) CanvasRenderingContext2D.strokeStyle
描边样式。默认值是#000000纯黑色。
(4) CanvasRenderingContext2D.stroke()
描边。
渐变相关
Canvas中与渐变相关的方法就是创建线性渐变和径向渐变这两个方法。
(1) createLinearGradient() 创建线性渐变
创建线性渐变。渐变是全局的,而不是相对于填充元素,是相对于Canvas
context.createLinearGradient(x0, y0, x1, y1); x0 : Number 渐变起始点横坐标。 y0 : Number 渐变起始点纵坐标。 x1 : Number 渐变结束点横坐标。 y1 : Number 渐变结束点纵坐标。线性渐变效果比较好脑补,就是从坐标点
[x0, y0]到坐标点[x1, y1]的位置画一条线,然后整个渐变色带与与这条线垂直。案例
演示下最简单的头尾二色渐变,以及渐变坐标的全局特性,如下JavaScript代码:
var context = canvas.getContext('2d'); // 创建渐变 var gradient = context.createLinearGradient(0, 0, 300, 0); gradient.addColorStop(0, 'red'); gradient.addColorStop(1, 'green'); // 设置填充样式为渐变 context.fillStyle = gradient; // 左上角和右下角分别填充2个矩形 context.fillRect(10, 10, 160, 60); context.fillRect(120, 80, 160, 60);
很直观展示了渐变坐标是全局的,而不是相对于填充元素(例如这里的矩形)。
(2) createRadialGradient() 创建径向渐变。
用来创建径向渐变。和CSS3的径向渐变有所不同的是,在Canvas中,径向渐变的起始点由两个圆环坐标构成,而非点坐标。
context.createRadialGradient(x0, y0, r0, x1, y1, r1); 返回值是CanvasPattern对象。 参数 各个参数含义和作用如下: x0 :Number 起始圆的横坐标。 y0:Number 起始圆的纵坐标。 r0:Number 起始圆的半径。 x1:Number 结束圆的横坐标。 y1:Number 结束圆的纵坐标。 r1:Number 结束圆的半径。标准两色镜像渐变
从Canvas径向渐变语法上讲,看似标准两色径向渐变的实现并不是理所当然,反而是需要一些技巧,技巧就是我们的起始圆半径设置为
0,化作一个点。例如实现一个红绿渐变,代码示意如下:<canvas width="240" height="120"></canvas> var context = canvas.getContext('2d'); // 创建一个起始圆半径为0的径向渐变对象 var gradient = context.createRadialGradient(120, 60, 0, 120, 60, 60); // 设置起止颜色 gradient.addColorStop(0, 'red'); gradient.addColorStop(1, 'green'); // 矩形填充 context.fillStyle = gradient; context.fillRect(0, 0, 240, 120);
色带分隔明显的色环
实现一个五彩色环效果,代码如下:
<canvas width="150" height="150"></canvas> var context = canvas.getContext('2d'); // 创建一个起始圆半径为0的径向渐变对象 var gradient = context.createRadialGradient(75, 75, 0, 75, 75, 75); // 设置起止颜色 gradient.addColorStop(0, 'red'); gradient.addColorStop(0.2, 'red'); gradient.addColorStop(0.2, 'orange'); gradient.addColorStop(0.4, 'orange'); gradient.addColorStop(0.4, 'yellow'); gradient.addColorStop(0.6, 'yellow'); gradient.addColorStop(0.6, 'green'); gradient.addColorStop(0.8, 'green'); gradient.addColorStop(0.8, 'purple'); gradient.addColorStop(1, 'purple'); gradient.addColorStop(1, 'transparent'); // 矩形填充 context.fillStyle = gradient; context.fillRect(0, 0, 150, 150);
对比截图,可以看到在渲染细节上,不同浏览器还是有差异的,Chrome浏览器下的锯齿比较明显。
实际上,如果起始渐变圆的半径如果不是
0,则在Firefox浏览器下,第5环会无法呈现,且也会出现明显锯齿,这个案例可参见CanvasGradient.addColorStop。
图案相关
Canvas中与图案相关的方法就是创建图案对象方法。
createPattern():创建纹理,可以是图片
创建图案。图案内容可以是图片,可以是<canvas>元素,也可以是渐变。此方法返回CanvasPattern对象。
context.createPattern(image, repetition);
image : Object
用来平铺的CanvasImageSource图像。可以是下面的类型:
HTMLImageElement,也就是<img>元素。HTMLVideoElement,也就是<video>元素,例如捕获摄像头视频产生的图像信息。HTMLCanvasElementCanvasRenderingContext2DImageBitmapImageDataBlobrepetition : String
图案的平铺方式,可以是下面的值:
'repeat',水平和垂直平铺。当repetition属性值为空字符串''或者null,也会按照'repeat'进行渲染。'repeat-x',仅水平平铺。'repeat-y',仅垂直平铺。'no-repeat',不平铺。<canvas id="canvas" width="250" height="167"></canvas> // 先绘制图片 var img = new Image(); img.onload = function () { // 我们创建一个Canvas元素 var canvasCreated = document.createElement('canvas'); canvasCreated.width = 50; canvasCreated.height = 34; canvasCreated.getContext('2d').drawImage(this, 0, 0, 50, 34); // 页面上需要呈现最终纹理的Canvas上下文 var context = canvas.getContext('2d'); // 创建纹理并填充,顺便测试null是否渲染为'repeat' var pattern = context.createPattern(canvasCreated, null); context.fillStyle = pattern; context.fillRect(0, 0, 250, 167); }; img.src = './1.jpg';
练习,完整展开图片
<canvas id="myCanvas">你的浏览器不支持 HTML5 canvas 标签。</canvas> <canvas id="cav2" width="400" height="400"></canvas> <script> let img = new Image(); img.onload = function(){ let c=document.getElementById('cav2'); let ct = c.getContext("2d"); //创建canvas画布 var canvasCreated = document.createElement('canvas'); canvasCreated.width=250 canvasCreated.height=400 canvasCreated.getContext("2d").drawImage(this,0,0,250,250); // 给创建纹理,填null会渲染为repeat let pattern = ct.createPattern(canvasCreated,"no-repeat"); //填充纹理 ct.fillStyle = pattern ct.fillRect(0, 0, 250, 250); } img.src = "https://img1.baidu.com/it/u=2956201572,2258203469&fm=253&fmt=auto&app=138&f=PNG?w=500&h=500";
阴影相关
Canvas中与阴影相关的属性就是下面这些。
(1) shadowBlur 模糊阴影
阴影模糊大小。默认值是0。
context.shadowBlur = value;
value : 表示阴影的模糊程度。数值类型,可以是小数。默认值是8。会忽略负数,NaN,或者 Infinity 。
1. 矩形块的阴影模糊
绘制一个矩形,然后给个投影,加点模糊,代码如下:
设置阴影红色,同时模糊大小为10 context.shadowColor = 'red'; context.shadowBlur = 10; 填充个淡淡的颜色,以示尊敬 context.fillStyle = '#f0f3f9'; context.fillRect(40, 40, 160, 40);
2. 文字的阴影模糊
写两个大大的文字,然后给个投影,加点模糊,代码如下:
设置阴影红色,同时模糊大小为10 context.shadowColor = 'red'; context.shadowBlur = 10; 文字80像素,黑体 context.font = '80px STheiti, simHei'; context.fillText('模糊', 40, 90);
(2)shadowColor 阴影颜色,默认透明黑
阴影颜色。默认值是全透明黑色。
context.shadowColor = color; document.createElement('canvas').getContext('2d').shadowColor; 当前浏览器输出结果是:rgba(0, 0, 0, 0)文字阴影效果,深黑色,无模糊,代码如下: // 设置阴影深黑色,同时右下角偏移3像素 context.shadowColor = 'rgb(50, 50, 50)'; context.shadowOffsetY = 3; context.shadowOffsetX = 3; // 文字80像素,黑体,红色 context.fillStyle = 'red'; context.font = '80px STheiti, simHei'; context.fillText('颜色', 40, 88);
(3)shadowOffsetX 阴影x偏移距离
阴影水平偏移大小。默认值是0。
借助足够大的水平偏移,克隆一个相同的文字。代码如下:
设置阴影深黑色,同时右偏移1个字号大小 context.shadowColor = 'rgb(50, 50, 50)'; context.shadowOffsetX = 80; 文字80像素,黑体,红色 context.fillStyle = 'red'; context.font = '80px STheiti, simHei'; context.fillText('变', 40, 88);
(4)shadowOffsetY 阴影垂直偏移大小。默认值是0。
借助足够大的水平偏移,克隆一个相同的文字。代码如下:
上阴影 context.shadowColor = 'rgb(50, 50, 50)'; context.shadowBlur = 5; context.shadowOffsetY = -5; 文字80像素,黑体,红色 context.fillStyle = 'red'; context.font = '70px STheiti, simHei'; context.fillText('上偏移', 10, 88);
绘制路径
下面的方法可以用来处理路径对象。
(1) beginPath() :开始一个新路径。没有参数没有返回值。
开始路径 context.beginPath(); context.strokeStyle = 'blue'; context.moveTo(60, 20); context.lineTo(220, 20); context.stroke(); 开始路径 again context.beginPath(); context.strokeStyle = 'green'; context.moveTo(60, 20); context.lineTo(160, 120); context.stroke();这个执行了2次
beginPath(),实时效果如下:
开始路径 context.beginPath(); context.strokeStyle = 'blue'; context.moveTo(60, 20); context.lineTo(220, 20); context.stroke(); context.strokeStyle = 'green'; context.moveTo(60, 20); context.lineTo(160, 120); context.stroke();
因此,只要是非连续路径绘制,都要记得都要执行一句
context.beginPath()。
(2) closePath() : 闭合开始点和结束点,行程闭合区域
闭合一个路径。没有参数,没有返回
context.closePath();

绘制三角
context.beginPath();
context.moveTo(10, 10);
context.lineTo(140, 70);
context.lineTo(70, 140);
// 不执行闭合,直接描边
context.stroke();
绘制另外一个三角
context.beginPath();
context.moveTo(160, 10);
context.lineTo(290, 70);
context.lineTo(220, 140);
执行闭合,然后描边
context.closePath();
context.stroke();
(3)moveTo() : 路径绘制的起点
路径绘制起始点。
context.moveTo(x, y);
x :起点的横坐标
y : 起点的纵坐标
context.beginPath();
context.moveTo(50, 20);
context.lineTo(200, 100);
context.stroke();

(4)lineTo() : 绘制路径点当前最后的子路点到lineTo()指定的终点。
绘制直线到指定坐标点。
context.lineTo(x, y);
示意一个贝塞尔曲线然后执行lineTo()方法的效果,代码如下:
context.beginPath();
context.moveTo(50, 20);
context.bezierCurveTo(100, 100, 200, 40, 250, 120);
context.lineTo(50, 120);
context.stroke();

上图中下面那个“平底鞋”的平底就是执行lineTo()绘制出来的。
(5)bezierCurveTo() 贝塞尔曲线
绘制贝赛尔曲线到指定坐标点。
(6).quadraticCurveTo() 二次贝塞尔曲线
绘制二次贝赛尔曲线到指定坐标点。
(7)arc() 绘制圆弧和圆
绘制圆弧(包括圆)。
context.arc(x, y, radius, startAngle, endAngle [, anticlockwise]);各个参数含义和作用如下:
- x 【Number】:圆弧对应的圆心横坐标。
- y Number【】:圆弧对应的圆心纵坐标。
- radius 【Number】:圆弧的半径大小。
- startAngle【Number】 : 圆弧开始的角度,单位是弧度。
- endAngle 【Number】: 圆弧结束的角度,单位是弧度。
- anticlockwise 【(可选)Boolean】: 弧度的开始到结束的绘制是按照顺时针来算,还是按时逆时针来算。如何设置为
true,则表示按照逆时针方向从startAngle绘制到endAngle。1. 绘制1/4弧度范围的圆弧
顺时针绘制一个角度从0到1/4弧度的圆弧,JavaScript代码如下:
顺时针绘制0到1/4弧度圆弧 context.beginPath(); context.arc(150, 75, 50, 0, Math.PI / 2); context.stroke();
逆时针绘制一个角度从0到1/4弧度的圆弧,JavaScript代码如下:
逆时针绘制0到1/4弧度圆弧 context.beginPath(); context.arc(150, 75, 50, 0, Math.PI / 2, true); context.stroke();
2. 绘制一个圆
直接设置起止弧度范围是2个π即可,如下代码:
绘制完整圆 context.beginPath(); context.arc(150, 75, 50, 0, Math.PI * 2); context.stroke();
CanvasRenderingContext2D.arcTo()
绘制圆弧,和之前的点以直线相连。
CanvasRenderingContext2D.rect()
绘制矩形路径。
CanvasRenderingContext2D.ellipse()
绘制椭圆路径。
CanvasRenderingContext2D.clip()
创建剪裁路径,之后绘制的路径只有在里面的才会显示。
位置检测
下面这两个方法可以判定某个坐标点是否路径或者描边中。
CanvasRenderingContext2D.isPointInPath()
当前点是否在指定路径内。
CanvasRenderingContext2D.isPointInStroke()
当前点是否在指定路径描边上。
变换
旋转缩放等变换方法。
CanvasRenderingContext2D.rotate()
旋转。
CanvasRenderingContext2D.scale()
缩放。
CanvasRenderingContext2D.translate()
位移。
CanvasRenderingContext2D.transform()
当前矩阵变换基础上再次矩阵变换。
CanvasRenderingContext2D.setTransform()
直接重置为当前设置的矩阵变换。
透明度和层级
一个是控制全局透明度,另外一个可以改变层级关系,设置混合模式,以及实现遮罩效果等。
(1)globalAlpha 全局透明度
全局透明度。
(2) globalCompositeOperation 图形混合模式,遮罩,剪裁,图形上下层叠关系。
设置图形叠加时候的混合方式,可以用来改变绘制元素上下叠加关系,也就是层级。
context.globalCompositeOperation = type;
其中type就是混合类型
- source-over:(默认)纯视觉覆盖,图形被第二层图形遮盖。
- source-in:绘制重叠部分
仅在和原Canvas图形重叠的位置绘制新图形,否则处理为透明。如果重叠位置是半透明颜色,则也处理为半透明。此效果类似遮罩,新内容为显示层,原内容是遮罩层,遮罩层无论张什么样子,都不显示。
source-out :绘制不重叠部分, 重叠部分透明
和
source-in相反,重叠的位置是透明的,不重叠的或者半透明的重叠区域反而显示新图形。同样,原内容无论性质如何,最终效果都不会出现。
source-atop: 新内容在旧内容上形成遮罩,没有重叠就不显示
destination-over:旧内容在新内容上方
destination-*系列和source-*系列的区别就是动作的主体是新内容还是原内容。source-*系列是新内容,而destination-*系列动作主体是元内容。例如这里的destination-over表示原内容在上方,也就是新内容在原内容的下方绘制。
- destination-in:显示新内容和旧内容重叠部分。
- destination-out:隐藏新旧内容重叠部分
- lighter:自然光混合效果
无论是哪种语言,哪种工具的混合模式,其实概念都类似的。如果这里的lighter等同于Adobe Photoshop中lighter color的话,则这个属性值可以理解为自然光混合效果。红绿蓝混合会成为白色。其色值混合原理如下,比较新内容和原内容颜色的所有通道值的总和,并显示更高值的颜色。例如,红色
RGB(255,0,0)和蓝色RGB(0,0,255)进行混合,则最终颜色值是RGB(255,0,255),也就是紫色。实际取色发现还是和PS还是有些出入的,并不是纯紫色。因此,我这里的理解并不一定完全准确,仅供参考。
- screen:滤色。像素反转,相乘,然后再反转。最终得到更淡的图形(和
multiply相反)。
- lighten:变亮。保留原内容和新内容中最亮的像素。
- hard-light:强光。类似
overlay,是multiply和screen组合效果。只不过底层和顶层位置交换下。
案例1:文字镂空效果
一个图片,然后文字部分是透明的。借助destination-out实现镂空效果,JS代码如下:
绘制图片
context.drawImage(img, 0, 0, 300, 200);
改变混合方式
context.globalCompositeOperation = 'destination-out';
绘制文本
context.font = 'bold 120px SimHei, STHeiti';
context.fillText('镂空', 25, 140);
本效果使用xor也是可以的。
案例2:给图片增加装饰效果
假设底图和装饰图分别如下:

我们使用screen模式进行混合,结果实时效果如下:

相关JS代码如下,假设图片都已经完全加载,且imgBase表示底图DOM元素对象,imgScreen表示装饰图DOM元素对象:
绘制底图
context.drawImage(imgBase, 0, 0, 300, 200);
设置混合模式为滤色
context.globalCompositeOperation = 'screen';
绘制装饰图
context.drawImage(imgScreen, 0, 0, 300, 200);
图片与像素
绘制图片和图像像素信息处理方法。
(1)drawImage() : 图片绘制在画布上。
CanvasRenderingContext2D.drawImage()是Canvas与web结合使用最频繁的方法,没有之-因为Canvas的很多API效果其它web技术也能实现,但是,对于很多图像相关的处理,如图像压缩,水印合成,或者图像的像素操作等(见最后的相关资源),那就没得选择,非使用drawImage()不可。
保持原始图片尺寸和比例
context.drawImage(image, dx, dy);
拉伸图片到指定大小和位置
context.drawImage(image, dx, dy, dWidth, dHeight);
拉伸图片同时保持图片比例
context.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
- image:图片。
<img>图片,SVG图像,Canvas元素本身等。 - dx 和 dy:左上角的横坐标和纵坐标。
- dWidth 和 dHeight:要放置图片的宽度和高度
- sx 和 sy:图片元素绘制在canvas画布上的起始横坐标和起始纵坐标。
- sWidth 和 sHeight:表示从坐标点开始算,多大的宽度和高度内容绘制Canvas画布上
1. 保持原始图片尺寸和比例
使用context.drawImage(image, dx, dy)绘制图片,可以保持图片的原始尺寸和比例。
context.drawImage(image, 0, 0);

从实际开发经验来看,当图片的原始尺寸和我们的Canvas画布尺寸一模一样的时候,可以直接只使用image,dx,dy这3个参数。
2. 拉伸图片到指定大小和位置
我们还可以指定Canvas中呈现图片的区域,这个时候,如果我们没有指定图片呈现的位置和尺寸的话,图片会被自动拉伸到这个指定区域内,很可能最终呈现的长宽比和原始图片就不一样。
context.drawImage(image, 0, 0, 300, 150);
可以看到图片虽然看到了全貌,但是被压扁了,不是原始的图片比例,这并不是我们需要的效果,需要进一步调整。
3. 拉伸图片同时保持图片比例
如何填满Canvas画布,同时保持图片的原始比例呢?这个就需要用到sx,sy,sWidth和sHeight这几个参数,注意,这4个参数是要写在dx,dy,dWidth和dHeight前面的,和一般的API不一样。
context.drawImage(image, 0, 42, 500, 250, 0, 0, 300, 150);
(原图尺寸500 * 333像素)
此案例关键是理解0, 42, 500, 250这4个坐标是怎么来的。我们最终图片要显示在300*150大小的Canvas画布中,比例是2:1,因此,我们最终设置的用来绘制的图片尺寸比例也是2:1。原始图片宽度是500,要保持2:1,则高度我们就设置为250;而原始高度是333,我们希望显示图片中心区域,因此起始垂直坐标计算下,(333 - 250) / 2,四舍五入就是42,因此,就有了对原始图片的剪裁坐标值和尺寸值0, 42, 500, 250。
(2)createImageData() 创建一个新的空白的ImageData对象。
(3)getImageData() 获取Canvas画布的设定区域的ImageData对象。
(4)putImageData() 给定的ImageData对象应用在Canvas画布上。
将图片2绘制在图片1的指定位置
context.putImageData(imagedata, dx, dy);
context.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
- imagedata:包含图像像素信息的ImageData对象。(膏药)
- dx 和 dy:目标Canvas中被图像数据替换的起点横坐标和纵坐标。(图片膏药要贴在什么位置)
dirtyX 和 dirtyY(可选):图像数据渲染区域的左上角横坐标和纵坐标。默认值是
0。(图片膏药从膏药哪个地方开始贴)dirtyWidth 和 dirtyHeight(可选):图像数据渲染区域的宽度和高度。默认值是
imagedata图像的宽度。(图片膏药要贴多大的宽度和高度)
案例

下面这个图像是imagedata数据源:

接下来,使用getImageData()方法获取imagedata数据源,然后仅中心100*100区域替换原始Canvas。
最终效果如下:

<img id="image1" src="./1.jpg" alt="目标图片">
<img id="image2" src="./1.jpg" alt="数据源图片">
<canvas id="canvas" width="300" height="200"></canvas>
尺寸
var width = 300, height = 200;
目标Canvas上下文
var context = canvas.getContext('2d');
目标Canvas绘制
context.drawImage(image1, 0, 0, width, height);
获取覆盖图数据
var dirtyCanvas = document.createElement('canvas');
var dirtyContext = dirtyCanvas.getContext('2d');
设置屏幕外Canvas尺寸
dirtyCanvas.width = width;
dirtyCanvas.height = height;
绘制替换图
dirtyContext.drawImage(image2, 0, 0, width, height);
此时可以得到imagedata数据
var imagedata = dirtyContext.getImageData(0, 0, width, height);
然后中间100*100区域替换目标Canvas
context.putImageData(imagedata, 0, 0, 100, 50, 100, 100);
Canvas状态
Canvas状态管理几个方法。
CanvasRenderingContext2D.save()
存储当前Canvas的状态。
CanvasRenderingContext2D.restore()
恢复Canvas到前一次存储的状态。
CanvasRenderingContext2D.canvas
反向识别当前上下文源自哪个HTMLCanvasElement。
其他方法
其他一些不常用的API方法。
CanvasRenderingContext2D.drawFocusIfNeeded()
如果给定元素被聚焦,则该方法在当前路径周围绘制焦点环。
CanvasRenderingContext2D.scrollPathIntoView()
将当前路径或给定路径滚动到视图中。
原文件地址 :https://www.canvasapi.cn/
































2147

被折叠的 条评论
为什么被折叠?



