了解CanvasRenderingContext2D
CanvasRenderingContext2D顾名思义就是“Canvas 2D渲染上下文”,可以理解为下面代码中的context。
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
context暴露了大量的API属性和方法,可以用来绘制文本,图形以及像素处理等,可以说是2D Canvas应用的核心所在。
绘制矩形
.clearRect()
清除指定矩形区域内部所有的像素信息为初始色(通常为透明)。
- 语法
context.clearRect(x, y, width, height);
- x 矩形左上角x坐标。
- y 矩形左上角y坐标。
- width 被清除的矩形区域的高度。
- height 被清除的矩形区域的宽度度。
- 案例
先把一张图片绘制在Canvas画布上,然后再把中间一块矩形区域的像素信息清除,JavaScript代码如下:
// 先绘制图片
var img = new Image();
img.onload = function () {
context.drawImage(img, 0, 0, 250, 167);
// 中间开个方形的洞
context.clearRect(50, 50, 100, 66);
};
img.src = './1.jpg';
一个圆球来回运动效果,不断clearRect清除,不断再绘制实现,本案例把时间放慢为每次刷新1000毫秒,便于我们肉眼也能感受Canvas中动画实现的基本原理。
context.font = '16px STHeiti, SimHei';
// 圆球的水平位置和半径
var x = 30, radius = 30;
// 清除方法
var clear = function () {
// 清除画布
context.clearRect(0, 0, 250, 167);
context.fillText('清除', 20, 30);
setTimeout(draw, 500);
};
// 绘制球方法
var draw = function () {
x += 10;
if (x > 220) {
x = 30;
}
// 清除提示文字
context.clearRect(0, 0, 250, 167);
// 新的文字提示,以及圆绘制
context.fillText('绘制', 20, 30);
context.beginPath();
context.arc(x, 84, radius, 0, Math.PI * 2);
context.fillStyle = '#039';
context.fill();
// 清除
setTimeout(clear, 500);
};
// 初始触发
draw();
.fillRect()
矩形填充,可以填充颜色,渐变,图案等。
- 语法
context.fillRect(x, y, width, height);
- x 填充矩形的起点横坐标。
- y 填充矩形的起点纵坐标。
- width 填充矩形的宽度。
- height 填充矩形的高度。
- 案例
填充两个矩形,绘制一个十字图形,直接上代码:
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
// 中心点坐标
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
// 矩形填充
context.fillRect(centerX - 30, centerY - 4, 60, 8);
context.fillRect(centerX - 4, centerY - 30, 8, 60);
.strokeRect()
矩形描边。
- 语法
context.strokeRect(x, y, width, height);
- x 描边矩形的起点横坐标。
- y 描边矩形的起点纵坐标。
- width 描边矩形的宽度。
- height 描边矩形的高度。
- 案例
// 2像素宽矩形描边
context.lineWidth = 2;
context.strokeRect(75, 25, 150, 100);
绘制文本
.fillText()
文字填充,可以填充纯色,渐变或者图案。
- 语法
context.fillText(text, x, y, maxWidth);
- text 用来填充的文本信息。
- x 填充文本的起点横坐标。
- y 填充文本的起点纵坐标。
- maxWidth (可选)填充文本占据的最大宽度,当文本占据宽度超过此最大宽度时候,通过压缩每个文本宽度进行渲染,而非换行。
- 案例
基本绘制
context.font = '24px STheiti, SimHei';
context.fillText('Canvas API中文网', 24, 66);
maxWidth与文本缩放
context.font = '24px STheiti, SimHei';
context.fillText('Canvas API中文网,不只是文档', 20, 66, 200);
Canvas中文本若想要自动换行,需要手动计算换行点,然后追行绘制。本案例借助这篇文章中扩展的wrapText()方法实现自动换行效果,
context.font = '24px STheiti, SimHei';
context.wrapText('Canvas API中文网,不只是文档', 24, 56, 200);
.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);
.measureText()
文字测量。返回TextMetrics对象,该对象的width属性值就是字符占据的宽度。
- 语法
context.measureText(text)
- text 被测量的文本。
- 案例
示意输出一个中文字符宽度以及输出一个英文单词的宽度,宽度直接在Canvas文字右上方标记
// 设置字体字号
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);
线条样式
.lineWidth
线条宽度,主使用场景是描边,默认宽度是1.0,支持小数。
- 语法
context.lineWidth = value;
- value 表示线的宽度。数值类型,默认值是1.0。如果是负数,0,NaN,或者Infinity都会忽略。
- 案例
绘制一个1像素宽的随机三角形。
<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();
.lineCap
线条端点的样式。支持如下属性值:butt(默认值,断头,无端帽),round(圆形端帽),square(方形端帽)。
- 语法
context.lineCap = 'butt';
context.lineCap = 'round';
context.lineCap = 'square';
- butt 默认值,线的端点就像是个断头台,例如一条横线,终点x坐标是100,则这条线的最右侧边缘就是100这个位置,没有超出。
- round 线的端点多出一个圆弧。
- square 线的端点多出一个方框,框框的宽度和线一样宽,高度是线厚度的一半
- 案例
绘制一个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();
}
.lineJoin
线条转角的样式。支持如下属性值:miter(默认值,尖角),round(圆角),bevel(平角)。
- 语法
context.lineJoin = 'miter';
context.lineJoin = 'round';
context.lineJoin = 'bevel';
- miter 默认值,转角是尖头。如果折线角度比较小,则尖头会非常长,因此需要miterLimit进行限制。
- round 转角是圆头。
- 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');
.miterLimit
尖角限制比率。线条的尖角如果没有限制,在线条粗角度小的情况下会很长很长,因此,需要一个限制比率。默认是10。
- 语法
context.miterLimit = value;
- value 表示多大范围内转角表现为miter的宽度。数值类型,默认值是10.0。如果是负数,0,NaN,或者Infinity都会忽略。
- 案例
以最大斜接长度 5 绘制线条
<canvas id="myCanvas"></canvas>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.lineWidth=10;
ctx.lineJoin="miter";
ctx.miterLimit=5;
ctx.moveTo(20,20);
ctx.lineTo(50,27);
ctx.lineTo(20,34);
ctx.stroke();
.getLineDash()
返回当前虚线数值。返回值是一个偶数个数的数组。
- 语法
context.getLineDash();
- 返回值 返回值是一个数组,数组里面的值都是数字,称为数字列表。所谓虚线,就是一段实线一段空隙交替出现的条线,而这里的数字列表中的值表示的就是交替的实线和间隙的长度值。如果设置虚线时候的数字个数是奇数,则数组里面的数字会被复制和链接,这样数量就变成偶数,例如,虚线设置为[5, 10, 15]将返回[5, 10, 15, 5, 10, 15]。
- 案例
演示设置奇数个数的虚线,然后获取,看看值是多少。
context.beginPath();
context.moveTo(10, 60);
context.lineTo(230, 60);
context.setLineDash([5]);
context.stroke();
// 将getLineDash()结果绘制在画布上
var dash = context.getLineDash();
// 绘制文本
context.font = '16px arial';
context.fillText(dash, 10, 48);
// 控制台输出
console.log(dash);
.setLineDash()
设置线条为虚线。
- 语法
ctx.setLineDash(segments);
- segments 数值列表数组。例如[5, 5],表示虚线的实线和透明部分长度是5像素和5像素。如果此参数值适合空数组[],则表示实线,常用来重置虚线设置。
- 案例
绘制一条虚线,只是虚线多尺寸并存
context.beginPath();
context.setLineDash([5, 10, 15, 30]);
context.moveTo(20, 70);
context.lineTo(280, 70);
context.stroke();
.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();
文本样式
.font
设置字体相关样式,包括字号,字体信息。默认值是10px sans-serif。(在Chrome浏览器中sans-serif不是默认字体,并且未能解析字体集关键字,从兼容性角度来说,在Canvas中sans-serif和serif尽量避免使用)
- 语法
context.font = value;
- value 字号字体值,其规则和CSS的font很类似,除了一些很微小的细节差异,其他几乎没什么区别。
- 案例
一个简单的24px的宋体效果
<canvas width="240" height="80"></canvas>
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
// 设置字体样式
context.font = '24px SimSun, Songti SC';
context.fillText('24px的宋体呈现', 20, 50);
.textAlign
设置文本水平对齐方式。支持属性值有:start(默认值),end,left,right以及center。
- 语法
context.textAlign = value;
value支持以下值:
- left 文本左对齐。也就是最终绘制的文本内容最左侧位置就是设定的x坐标值。
- right 文本右对齐。也就是最终绘制的文本内容最右侧位置就是设定的x坐标值。
- center 文本居中对齐。也就是最终绘制的文本内容的水平中心位置就是设定的x坐标值。
- start 文本起始方位对齐。如果文本是从左往右排列的,则表示左对齐;如果文本是从右往左排列的(例如设置context.direction为rtl),则表示右对齐。
- end 文本结束方位对齐。如果文本是从左往右排列的,则表示右对齐;如果文本是从右往左排列的(例如设置context.direction为rtl),则表示左对齐。
- 案例
置水平x坐标是画布的水平中心位置,然后选择不同的textAlign属性值,
<canvas width="240" height="120"></canvas>
let align
// 设置字体和字号
context.font = '48px STHeiti, SimHei';
// 设置对齐方式
context.textAlign = align || 'left';
// 填充文字
context.fillText('对齐', 120, 80);
.textBaseline
设置文本基线对齐方式。支持属性值有:top,hanging,middle,alphabetic(默认值),ideographic,bottom。
- 语法
context.textBaseline = value;
value支持以下值:
- top 设定的垂直y坐标作为文本em区域(em区域可以看成中文字符占据的区域)的顶部。
- hanging hanging主要在藏文和其他印度文字中使用,我们了解即可。
- middle 设定的垂直y坐标作为文本em区域的垂直中心位置。
- alphabetic 默认值。表示的是正常文本的基线,可以看成是字母x的下边缘。也就是设定的垂直y坐标就是字母x的下边缘。
- ideographic ideographic主要在汉语、日语和韩语中使用。字面直译是表意基线。含义为:如果字符的主体突出在字母基线之下,则这是字符主体的底部。例如汉字“中”比字母x位置更低,因此,底部是汉字主体的底部。
- bottom 设定的垂直y坐标作为文本em区域的底部。
- 案例
展示不同的textBaseline属性值和文本对应的垂直位置关系
var context = canvas.getContext('2d');
var baselines = ['top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom'];
context.font = '30px STHeiti, SimHei';
context.strokeStyle = 'red';
baselines.forEach(function (baseline, index) {
context.textBaseline = baseline;
var y = 75 + index * 75;
// 绘制参考线
context.beginPath();
context.moveTo(0, y + 0.5);
context.lineTo(550, y + 0.5);
context.stroke();
// 填充文字
context.fillText('AaXx一中-' + baseline, 0, y);
});
.direction
设置文本显示方向。支持属性值有:inherit(默认值),ltr和rtl。
- 语法
context.direction = value;
value支持以下值:
- inherit (默认值)继承父级属性配置
- ltr 从左侧开始显示渲染
- rtl 从右侧开始显示渲染
- 案例
let align
// 设置字体和字号
context.font = '48px STHeiti, SimHei';
// 设置对齐方式
context.direction = align || 'inherit';
// 填充文字
context.fillText('测试文字内容', 120, 80);
填充和描边
.fillStyle
填充样式。默认值是#000000纯黑色。
- 语法
context.fillStyle = color;
context.fillStyle = gradient;
context.fillStyle = pattern;
- color 使用纯色填充,支持RGB,HSL,RGBA,HSLA以及HEX色值。
- gradient 使用渐变填充,可以是线性渐变或者径向渐变。
- pattern 使用纹理填充。由于图片也能作为纹理,因此fillStyle也能填充普通的位图,可参见下面的案例。
- 案例
- 色值填充
<canvas id="canvasColor"></canvas>
var canvasColor = document.getElementById('canvasColor');
var contextColor = canvasColor.getContext('2d');
contextColor.fillStyle = 'RGB(255, 0, 0)';
// RGB(255, 0, 0) // RGBA(255, 0, 0, .5) // HSL(360, 100%, 50%) // HSLA(360, 100%, 50%, .5) // #FF0000
contextColor.fillRect(10, 10, 100, 100);
- 渐变填充-线性
<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');
gradientLinear.addColorStop(1, 'green');
// 填充线性渐变
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';
.fill()
填充。
- 语法
context.fill();
context.fill(fillRule);
context.fill(path, fillRule);
- fillRule 填充规则。用来确定一个点实在路径内还是路径外。可选值包括: nonzero:非零规则,此乃默认规则、evenodd:奇偶规则。
- path 指Path2D对象。
- 案例
“非零规则”和“奇偶规则”的不同表现
<canvas width="300" height="300" data-rule="nonzero"></canvas>
<canvas width="300" height="300" data-rule="evenodd"></canvas>
// 2个三角的点坐标
var arrPoints = [[60,60], [240,100], [160,280], [60,60], [150,20], [260,260]];
var canvases = document.querySelectorAll('canvas');
// 遍历
[].slice.call(canvases).forEach(function (canvas, index) {
var rule = canvas.getAttribute('data-rule');
var context = canvas.getContext('2d');
// 开始绘制
context.beginPath();
context.moveTo(arrPoints[0][0], arrPoints[0][1]);
context.lineTo(arrPoints[1][0], arrPoints[1][1]);
context.lineTo(arrPoints[2][0], arrPoints[2][1]);
context.lineTo(arrPoints[3][0], arrPoints[3][1]);
context.lineTo(arrPoints[4][0], arrPoints[4][1]);
context.lineTo(arrPoints[5][0], arrPoints[5][1]);
context.closePath();
context.fillStyle = '#0e65c5';
context.fill(rule);
});
.strokeStyle
描边样式。默认值是#000000纯黑色。
- 语法
context.strokeStyle = color;
context.strokeStyle = gradient;
context.strokeStyle = pattern;
- color 描边设置为颜色。
- gradient 描边设置为渐变。
- pattern 描边设置为图案。
- 案例
- 描边设置为颜色
// 红色描边
context.strokeStyle = 'red';
context.lineWidth = 10;
context.strokeRect(40, 20, 160, 80);
- 描边设置为渐变色
// 红绿渐变
var gradient = context.createLinearGradient(0, 0, 0, 120);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'green');
// 渐变描边
context.strokeStyle = gradient;
context.lineWidth = 10;
context.strokeRect(40, 20, 160, 80);
- 描边设置为图案
// 照片作为描边图案
var image = new Image();
image.onload = function () {
var pattern = context.createPattern(this, 'repeat');
context.strokeStyle = pattern;
context.lineWidth = 10;
context.strokeRect(40, 20, 160, 80);
};
image.src = '1.jpg';
.stroke()
描边。
- 语法
context.stroke();
context.stroke(path);
- path 指Path2D对象。IE浏览器不支持。
- 案例
描边一条直线
context.moveTo(50, 50);
context.lineTo(250, 100);
// 描边
context.stroke();
渐变相关
.createLinearGradient()
创建线性渐变。
- 语法
context.createLinearGradient(x0, y0, x1, y1);
- 返回值 返回值是CanvasGradient对象。
- x0 渐变起始点横坐标。
- y0 渐变起始点纵坐标。
- x1 渐变结束点横坐标。
- y1 渐变结束点纵坐标。
线性渐变效果比较好脑补,就是从坐标点[x0, y0]到坐标点[x1, y1]的位置画一条线,然后整个渐变色带与与这条线垂直。
- 案例
头尾二色渐变,以及渐变坐标的全局特性
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);
.createRadialGradient()
创建径向渐变。
- 语法
context.createRadialGradient(x0, y0, r0, x1, y1, r1);
- 返回值 返回值是CanvasPattern对象。
- x0 起始圆的横坐标。
- y0 起始圆的纵坐标。
- r0 起始圆的半径。
- x1 结束圆的横坐标。
- y1 结束圆的纵坐标。
- r1 结束圆的半径。
- 案例
- 标准两色径向渐变
<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);
图案相关
.createPattern()
创建图案。图案内容可以是图片,可以是canvas元素,也可以是渐变。此方法返回CanvasPattern对象。
- 语法
context.createPattern(image, repetition);
- 返回值 返回值是CanvasPattern对象。
- image 用来平铺的CanvasImageSource图像。可以是下面的类型
- HTMLImageElement,也就是
元素。
- HTMLImageElement,也就是
- HTMLVideoElement,也就是
- HTMLCanvasElement
- CanvasRenderingContext2D
- ImageBitmap
- ImageData
- Blob
- repetition 图案的平铺方式,可以是下面的值:
- ‘repeat’,水平和垂直平铺。当repetition属性值为空字符串’’或者null,也会按照’repeat’进行渲染。
- ‘repeat-x’,仅水平平铺。
- ‘repeat-y’,仅垂直平铺。
- ‘no-repeat’,不平铺。
- 案例
图片缩小,并作为纹理显示。我们直接把img元素作为纹理图案是无法控制其尺寸的,我们可以将img元素绘制在大小可控的Canvas元素上,然后把这个Canvas元素作为图案进行平铺即可。
<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';
阴影相关
.shadowBlur
阴影模糊大小。默认值是0。
- 语法
context.shadowBlur = value;
- value 表示阴影的模糊程度。数值类型,可以是小数。默认值是0.会忽略负数、NaN等
- 案例
- 矩形块的阴影模糊
// 设置阴影红色,同时模糊大小为10
context.shadowColor = 'red';
context.shadowBlur = 10;
// 填充个淡淡的颜色,以示尊敬
context.fillStyle = '#f0f3f9';
context.fillRect(40, 40, 160, 40);
- 文字的阴影模糊
// 设置阴影红色,同时模糊大小为10
context.shadowColor = 'red';
context.shadowBlur = 10;
// 文字80像素,黑体
context.font = '80px STheiti, simHei';
context.fillText('模糊', 40, 90);
.shadowColor
阴影颜色。默认值是全透明黑色。
- 语法
context.shadowColor = color;
- color 表示阴影的颜色。各种颜色值都支持,默认是透明黑: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);
.shadowOffsetX
阴影水平偏移大小。默认值是0。
- 语法
context.shadowOffsetX = offset;
- offset 表示偏移的大小,数值,默认值是0。忽略Infinity或者NaN值。
- 案例
借助足够大的水平偏移,克隆一个相同的文字。
// 设置阴影深黑色,同时右偏移1个字号大小
context.shadowColor = 'rgb(50, 50, 50)';
context.shadowOffsetX = 80;
// 文字80像素,黑体,红色
context.fillStyle = 'red';
context.font = '80px STheiti, simHei';
context.fillText('变', 40, 88);
.shadowOffsetY
阴影垂直偏移大小。默认值是0。
- 语法
context.shadowOffsetY = offset;
- offset 表示偏移的大小,数值,默认值是0。忽略Infinity或者NaN值。
- 案例
借助足够大的水平偏移,克隆一个相同的文字。
// 上阴影
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);
绘制路径
.beginPath()
开始一个新路径。
- 语法
context.beginPath();
- 案例
两个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();
// 开始路径
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();
只声明了一次beginPath(),结果两条折线全部的颜色都是绿色;而两次beginPath()则是一条蓝色,一条绿色。
因此,只要是非连续路径绘制,都要记得都要执行一句context.beginPath()。
.closePath()
闭合一个路径。
- 语法
context.closePath();
- 案例
两个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();
.moveTo()
路径绘制起始点。
- 语法
context.moveTo(x, y);
- x 落点的横坐标。
- y 落点的纵坐标。
- 案例
绘制直线一条
context.beginPath();
context.moveTo(50, 20);
context.lineTo(200, 100);
context.stroke();
.lineTo()
绘制直线到指定坐标点。
- 语法
context.lineTo(x, y);
- x 绘制的直线的落点的横坐标。
- y 绘制的直线的落点的纵坐标。
- 案例
一个贝塞尔曲线然后执行lineTo()方法
context.beginPath();
context.moveTo(50, 20);
context.bezierCurveTo(100, 100, 200, 40, 250, 120);
context.lineTo(50, 120);
context.stroke();
.bezierCurveTo()
绘制贝赛尔曲线到指定坐标点。
- 语法
context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
- cp1x 第1个控制点的横坐标。
- cp1y 第1个控制点的纵坐标。
- cp2x 第2个控制点的横坐标。
- cp2y 第2个控制点的纵坐标。
- x 结束点的横坐标。
- y 结束点的纵坐标。
- 案例
// 开始绘制
context.beginPath();
context.moveTo(50, 50);
context.bezierCurveTo(100, 100, 221, 24, 250, 100);
context.stroke();
.quadraticCurveTo()
绘制二次贝赛尔曲线到指定坐标点。
- 语法
context.quadraticCurveTo(cpx, cpy, x, y);
- cpx 控制点的横坐标。
- cpy 控制点的纵坐标。
- x 结束点的横坐标。
- y 结束点的纵坐标。
- 案例
// 开始绘制
context.beginPath();
context.moveTo(50, 50);
context.quadraticCurveTo(72, 99, 243, 57);
context.stroke();
.arc()
绘制圆弧(包括圆)。
- 语法
context.arc(x, y, radius, startAngle, endAngl, anticlockwise);
- x 圆弧对应的圆心横坐标。
- y 圆弧对应的圆心纵坐标。
- radius 圆弧的半径大小。
- startAngle 圆弧开始的角度,单位是弧度。
- endAngl 圆弧结束的角度,单位是弧度。
- anticlockwise (可选)弧度的开始到结束的绘制是按照顺时针来算,还是按时逆时针来算。如果设置为true,则表示按照逆时针方向从startAngle绘制到endAngle。
- 案例
- 绘制1/4弧度范围的圆弧
// 顺时针绘制0到1/4弧度圆弧
context.beginPath();
context.arc(150, 75, 50, 0, Math.PI / 2);
context.stroke();
// 逆时针绘制0到1/4弧度圆弧
context.beginPath();
context.arc(150, 75, 50, 0, Math.PI / 2, true);
context.stroke();
- 绘制一个圆
// 绘制完整圆
context.beginPath();
context.arc(150, 75, 50, 0, Math.PI * 2);
context.stroke();
.arcTo()
绘制圆弧,和之前的点以直线相连。
- 语法
context.arcTo(x1, y1, x2, y2, radius);
- x1 第1个控制点的横坐标。
- y1 第1个控制点的纵坐标。
- x2 第2个控制点的横坐标。
- y2 第2个控制点的纵坐标。
- radius 圆弧的半径大小。
- 案例
context.beginPath();
context.moveTo(50, 50);
context.arcTo(150, 100, 200, 40, 40);
context.lineTo(200, 40);
context.stroke();
.rect()
绘制矩形路径。
- 语法
context.rect(x, y, width, height);
- x 矩形路径的起点横坐标。
- y 矩形路径的起点纵坐标。
- width 矩形的宽度。
- height 矩形的高度。
- 案例
中心位置绘制一个100*100大小的矩形,然后描边
// 矩形
context.rect(100, 25, 100, 100);
context.stroke();
.ellipse()
绘制椭圆路径。
- 语法
context.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
- x 椭圆弧对应的圆心横坐标。
- y 椭圆弧对应的圆心纵坐标。
- radiusX 椭圆弧的长轴半径大小。
- radiusY 椭圆弧的短轴半径大小。
- rotation 椭圆弧的旋转角度,单位是弧度。
- startAngle 圆弧开始的角度,角度从横轴开始算,单位是弧度。
- endAngle 圆弧结束的角度,单位是弧度。
- anticlockwise (可选)弧度的开始到结束的绘制是按照顺时针来算,还是按时逆时针来算。如何设置为true,则表示按照逆时针方向从startAngle绘制到endAngle。
- 案例
绘制一个长轴短轴比2:1,同时旋转45°的椭圆
// 绘制椭圆
context.ellipse(150, 75, 80, 40, Math.PI / 4, 0, 2 * Math.PI);
context.stroke();
.clip()
创建剪裁路径,之后绘制的路径只有在里面的才会显示。
- 语法
context.clip();
context.clip(fillRule);
context.clip(path, fillRule);
- fillRule 填充规则。用来确定一个点实在路径内还是路径外。可选值包括:
- nonzero:非零规则。此乃默认规则。
- evenodd:奇偶规则。
- path 指Path2D对象。
- 案例
利用剪裁实现一个图案填充效果。实现图案填充,标准用法是创建一个Pattern对象,然后作为fillStyle进行路径填充。这里,我们还可以使用clip()剪裁实现,这样就不用new一个Pattern对象了。例如,实现一个三角形,里面是人物照片图案。
var context = canvas.getContext('2d');
// 需要图片先加载完毕
var img = new Image();
img.onload = function () {
// 剪裁路径是三角形
context.beginPath();
context.moveTo(20, 20);
context.lineTo(200, 80);
context.lineTo(110, 150);
// 剪裁
context.clip();
// 填充图片
context.drawImage(img, 0, 0, 250, 167);
};
img.src = './1.jpg';
位置检测
.isPointInPath()
当前点是否在指定路径内。
- 语法
context.isPointInPath(x, y);
context.isPointInPath(x, y, fillRule);
// 下面语法IE不支持
context.isPointInPath(path, x, y);
context.isPointInPath(path, x, y, fillRule);
- 返回值 返回Boolean值。
- x 用来检测的点的横坐标。
- y 用来检测的点的纵坐标。
- fillRule 填充规则。用来确定一个点实在路径内还是路径外。可选值包括:
- nonzero:非零规则,此乃默认规则。
- evenodd:奇偶规则。
- path 指Path2D对象。
- 案例
在Canvas画布上画两个圈圈,然后看看圈内圈外圈上这几个点的返回值是什么
// 画一个圆
context.arc(120, 120, 80, 0, Math.PI * 2);
context.stroke();
// 用来测试的点坐标们
var arrPoints = [{
x: 50,
y: 50
}, point2 = {
x: 150,
y: 150
}, point3 = {
x: 120,
y: 40
}];
arrPoints.forEach(function (point) {
// 检测点是否在路径内
point.isPointInPath = context.isPointInPath(point.x, point.y);
});
arrPoints.forEach(function (point) {
// 标记这几个点
context.fillStyle = 'red';
context.beginPath();
context.arc(point.x, point.y, 3, 0, Math.PI * 2);
context.fill();
// 检测结果以文本方式绘制
context.font = '14px arial';
context.fillText(point.isPointInPath, point.x + 5, point.y);
});
.isPointInStroke()
当前点是否在指定路径描边上。
- 语法
context.isPointInStroke(x, y);
context.isPointInStroke(path, x, y);
- 返回值 返回Boolean值。
- x 用来检测的点的横坐标。
- y 用来检测的点的纵坐标。
- path 指Path2D对象。
- 案例
在Canvas画布上画两个圈圈,然后看看圈内圈外圈上这几个点的返回值是什么
// 画一个圆
context.arc(120, 120, 80, 0, Math.PI * 2);
context.lineWidth = 5;
context.stroke();
// 用来测试的点坐标们
var arrPoints = [{
x: 40,
y: 40
}, point2 = {
x: 120,
y: 180
}, point3 = {
x: 120,
y: 38
}];
arrPoints.forEach(function (point) {
// 检测点是否在路径内
point.isPointInStroke = context.isPointInStroke(point.x, point.y);
});
arrPoints.forEach(function (point) {
// 标记这几个点
context.fillStyle = 'red';
context.beginPath();
context.arc(point.x, point.y, 3, 0, Math.PI * 2);
context.fill();
// 检测结果以文本方式绘制
context.font = '14px arial';
context.fillText(point.isPointInStroke, point.x + 5, point.y);
});
变换
.rotate()
旋转。
- 语法
context.rotate(angle);
- angle Canvas画布坐标系旋转的角度,单位是弧度。注意,此旋转和CSS3的旋转变换不一样,旋转的是坐标系,而非元素。因此,实际开发的时候,旋转完毕,需要将坐标系再还原。
- 案例
- 基本旋转
// 旋转45度
context.rotate(45 * Math.PI / 180);
// 字体填充
context.font = '20px STHeiti, SimHei';
context.fillText('旋转,跳跃,我闭着眼', 60, -40, 188);
// 重置当前的变换矩阵为初始态
context.setTransform(1, 0, 0, 1, 0, 0);
- 画布和图片同步旋转
<canvas id="canvas2" width="200" height="300"></canvas>
var context = canvas2.getContext('2d');
var width = canvas2.width;
var height = canvas2.height;
// 加载图片素材
var img = new Image();
img.onload = function () {
// 先位移坐标到中心
context.translate(width / 2, height / 2);
// 旋转90度
context.rotate(90 * Math.PI / 180);
// 此时按照旋转后的尺寸
// 把定位中心移动到左上角
context.translate(-1 * height / 2, -1 * width / 2);
// 绘制图片
context.drawImage(this, 0, 0, height, width);
// 坐标系还原到初始
context.setTransform(1, 0, 0, 1, 0, 0);
};
img.src = './1.jpg';
.scale()
缩放。
- 语法
context.scale(x, y);
- x Canvas坐标系水平缩放的比例。支持小数,如果值是-1,表示水平翻转。
- y Canvas坐标系垂直缩放的比例。支持小数,如果值是-1,表示垂直翻转。
- 案例
- 基本使用
// 显示绘制个正方形用来对比
context.fillRect(10, 10, 10, 10);
// 缩放
context.scale(10, 3);
// 再次绘制
context.fillRect(10, 10, 10, 10);
// 恢复坐标系
context.setTransform(1, 0, 0, 1, 0, 0);
- 垂直翻转
// 记住Canvas状态
context.save();
// 来来来,垂直翻转下
context.scale(1, -1);
// 填充文字
context.font = '32px STHeiti, SimHei';
context.fillText('换个角度看世界', 36, -64);
// 恢复状态,不要影响接下来的绘制
context.restore();
.translate()
位移。
- 语法
context.translate(x, y);
- x 坐标系水平位移的距离。
- y 坐标系垂直位移的距离。
- 案例
借助translate()方法,实现Canvas元素内容以Canvas画布的中心点为变换点进行旋转。
<canvas id="canvas" width="300" height="200"></canvas>
var img = new Image();
img.onload = function () {
var context = canvas.getContext('2d');
// 坐标位移
context.translate(150, 100);
// 旋转45度
context.rotate(45 * Math.PI / 180);
// 再位移回来
context.translate(-150, -100);
// 此时绘制图片就是中心旋转了
context.drawImage(this, 0, 0, 300, 200);
// 坐标系还原
context.setTransform(1, 0, 0, 1, 0, 0);
};
img.src = './1.jpg';
.transform()
当前矩阵变换基础上再次矩阵变换。
- 语法
context.transform(a, b, c, d, e, f);
- a 水平缩放。
- b 水平斜切。
- c 垂直斜切。
- d 垂直缩放。
- e 水平位移。
- f 垂直位移。
- 案例
借助transform()方法绘制一个平行四边形
context.transform(1, 0, 1, 1, 0, 0);
context.fillRect(10, 20, 100, 100);
.setTransform()
直接重置为当前设置的矩阵变换。(此方法和transform()方法的区别在于,后者不会完全重置已有的变换,而是累加。)
- 语法
context.setTransform(a, b, c, d, e, f);
- a 水平缩放。
- b 水平斜切。
- c 垂直斜切。
- d 垂直缩放。
- e 水平位移。
- f 垂直位移。
- 案例
借助setTransform()方法绘制一个平行四边形
context.setTransform(1, 0, 1, 1, 0, 0);
context.fillRect(10, 20, 100, 100);
透明度和层级
.globalAlpha
全局透明度。
对于纯色图形的绘制,如果需要用到半透明或者不透明,建议使用RGBA或者HSLA色值进行控制,比使用globalAlpha方便,因为按照我个人的使用经验,使用globalAlpha设置透明度之后都是需要再进行还原设置的,而直接使用RGBA或者HSLA色值则没有这样的重置操作。
但是,如果是绘制图片,借助globalAlpha属性是更好的选择
- 语法
context.globalAlpha = value;
- value value就是设置的全局透明度,范围是0到1,范围以外的值会被忽略。
- 案例
先绘制半透明图片,再绘制不透明文字
// 先存储当前的上下文状态
context.save();
// 设置图片半透明
context.globalAlpha = 0.5;
// 绘制图片
context.drawImage(img, 0, 0);
// 透明度还原
context.restore();
// 然后绘制文字
context.font = '48px serif';
context.fillText('标题', 96, 90);
这里globalAlpha的重置用的是Canvas独有的save()和restore()组合,这两个组合方法可以还原所有Canvas上下文状态,不只是透明度。在本例中,就只有透明度,因此,我们使用context.globalAlpha = 1重置也是可以的。
.globalCompositeOperation
设置图形叠加时候的混合方式,可以用来改变绘制元素上下叠加关系,也就是层级。
- 语法
context.globalCompositeOperation = type;
type有以下参数值:
- source-over 绘制图形的默认混合方式,直接在现有图形的上方绘制,纯视觉覆盖。
- source-in 仅在和原Canvas图形重叠的位置绘制新图形,否则处理为透明。如果重叠位置是半透明颜色,则也处理为半透明。此效果类似遮罩,新内容为显示层,原内容是遮罩层,遮罩层无论张什么样子,都不显示。
- source-out 和source-in相反,重叠的位置是透明的,不重叠的或者半透明的重叠区域反而显示新图形。同样,原内容无论性质如何,最终效果都不会出现。
- source-atop 尽在新内容与原内容重叠的位置进行类似遮罩的绘制,如果们没有重叠的位置,则原封不动,这个和source-in区别在于source-in就算与原内容不重叠,原内容也永远不会显示,但source-atop会保留。
- destination-over destination- * 系列和source- * 系列的区别就是动作的主体是新内容还是原内容。source- * 系列是新内容,而destination- * 系列动作主体是元内容。例如这里的destination-over表示原内容在上方,也就是新内容在原内容的下方绘制。
- destination-in 显示原内容和新内容重叠的部分。
- destination-out 隐藏原内容和新内容重叠的部分。
- destination-atop 原内容只显示和新内容重叠的部分,同时新内容在下方显示。
- lighter 无论是哪种语言,哪种工具的混合模式,其实概念都类似的。如果这里的lighter等同于Adobe Photoshop中lighter color的话,则这个属性值可以理解为自然光混合效果。红绿蓝混合会成为白色。其色值混合原理如下,比较新内容和原内容颜色的所有通道值的总和,并显示更高值的颜色。例如,红色RGB(255,0,0)和蓝色RGB(0,0,255)进行混合,则最终颜色值是RGB(255,0,255),也就是紫色。实际取色发现还是和PS还是有些出入的,并不是纯紫色。因此,这里的理解并不一定完全准确,仅供参考。
- copy 只显示新内容。
- xor 互相重叠的区域是透明的。
- multiply 正片叠底。顶层的像素与底层的对应像素相乘。结果是一幅更黑暗的图画。
- screen 滤色。像素反转,相乘,然后再反转。最终得到更淡的图形(和multiply相反)。
- overlay 叠加。multiply和screen组合效果。基础图层上暗的部分更暗,亮的部分更亮。
- darken 变暗。保留原内容和新内容中最暗的像素。
- lighten 变亮。保留原内容和新内容中最亮的像素。
- color-dodge 颜色减淡。底部图层色值除以顶部图层的反相色值。
- color-burn 颜色加深。底部图层的色值除以顶部图层色值,得到的结果再反相。
- hard-light 强光。类似overlay,是multiply和screen组合效果。只不过底层和顶层位置交换下。
- soft-light 柔光。hard-light的柔和版本。纯黑色或白色不会生成为纯黑色或白色。
- difference 差异。顶层色值减去底层色值的绝对值。如果都是白色,则最后是黑色,因为值为0;什么时候是白色呢,例如RGB(255,0,0)和RGB(0,255,255),色值相减后绝对值是RGB(255,255,255)。
- exclusion 排除。类似difference,不过对比度较低。
- hue 色调。最终的颜色保留底层的亮度和色度,同时采用顶层的色调。
- saturation 饱和度。最终的颜色保留底层的亮度和色调,同时采用顶层的色度。
- color 色值。最终的颜色保留底层的亮度,同时采用顶层的色调和色度。
- luminosity 亮度。最终的颜色保留底层的色调和色度,同时采用顶层的亮度。
- 案例
- 文字镂空效果
// 绘制图片
context.drawImage(img, 0, 0, 300, 200);
// 改变混合方式
context.globalCompositeOperation = 'destination-out';
// 绘制文本
context.font = 'bold 120px SimHei, STHeiti';
context.fillText('镂空', 25, 140);
- 给图片增加装饰效果
// 绘制底图
context.drawImage(imgBase, 0, 0, 300, 200);
// 设置混合模式为滤色
context.globalCompositeOperation = 'screen';
// 绘制装饰图
context.drawImage(imgScreen, 0, 0, 300, 200);
图片与像素
.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 绘制在Canvas上的元素,可以是各类Canvas图片资源,如img图片,SVG图像,Canvas元素本身等。
- dx 在Canvas画布上规划一片区域用来放置图片,dx就是这片区域的左上角横坐标。
- dy 在Canvas画布上规划一片区域用来放置图片,dy就是这片区域的左上角纵坐标。
- dWidth 在Canvas画布上规划一片区域用来放置图片,dWidth就是这片区域的宽度。
- dHeight 在Canvas画布上规划一片区域用来放置图片,dHeight就是这片区域的高度。
- sx 表示图片元素绘制在Canvas画布上起始横坐标。
- sy 表示图片元素绘制在Canvas画布上起始纵坐标。
- sWidth 表示图片元素从坐标点开始算,多大的宽度内容绘制Canvas画布上。
- sHeight 表示图片元素从坐标点开始算,多大的高度内容绘制Canvas画布上。
- 案例
- 保持原始图片尺寸和比例
context.drawImage(image, 0, 0);
- 拉伸图片到指定大小和位置
context.drawImage(image, 0, 0, 300, 150);
- 拉伸图片同时保持图片比例
context.drawImage(image, 0, 42, 500, 250, 0, 0, 300, 150);
.createImageData()
创建一个新的空白的ImageData对象。
- 语法
context.createImageData(width, height);
context.createImageData(imagedata);
- width ImageData对象包含的width值。如果ImageData对象转换成图像,则此width也是最终图像呈现的宽度。
- height ImageData对象包含的height值。如果ImageData对象转换成图像,则此height也是最终图像呈现的高度。
- imagedata 一个存在的ImageData对象,只会使用该ImageData对象中的width和height值,包含的像素信息会全部转换为透明黑。
- 案例
使用createImageData()方法创建一个图像,例如,所有位置是5的倍数的地方我们塞入一个绿色颜色值,这样可以得到一个点阵图效果
<canvas id="canvas"></canvas>
// 绘制在Canvas上
var context = canvas.getContext('2d');
var imagedata = context.createImageData(300, 150);
// 给对应坐标位置的数据设置色值为绿色
for (var x = 1; x <= 300; x+=5) {
for (var y = 1; y <= 150; y+= 5) {
var index = 4 * ((y - 1) * 300 + (x - 1));
// 变为绿色,色值依次是0, 128, 0, 256
imagedata.data[index] = 0;
imagedata.data[index + 1] = 128;
imagedata.data[index + 2] = 0;
imagedata.data[index + 3] = 256;
}
}
// 再重绘
context.putImageData(imagedata, 0, 0);
.getImageData()
获取Canvas画布的设定区域的ImageData对象。
- 语法
context.getImageData(sx, sy, sWidth, sHeight);
- sx 需要返回的图像数据区域的起始横坐标。
- sy 需要返回的图像数据区域的起始纵坐标。
- sWidth 需要返回的图像数据区域的宽度。
- sHeight 需要返回的图像数据区域的高度。
- 案例
绘制一张图片到Canvas画布上,然后把中间100*100区域变成灰色。
<canvas id="canvas" width="250" height="167"></canvas>
var img = new Image();
img.onload = function () {
var context = canvas.getContext('2d');
// 图片绘制
context.drawImage(this, 0, 0, 250, 167);
// 然后获取中间100*100区域数据
var imageData = context.getImageData(75, 34, 100, 100);
var length = imageData.data.length;
for (var index = 0; index < length; index += 4) {
var r = imageData.data[index];
var g = imageData.data[index + 1];
var b = imageData.data[index + 2];
// 计算灰度
var gray = r * 0.299 + g * 0.587 + b * 0.114;
imageData.data[index] = gray;
imageData.data[index + 1] = gray;
imageData.data[index + 2] = gray;
}
// 更新新数据
context.putImageData(imageData, 75, 34);
};
img.src = './1.jpg';
.putImageData()
给定的ImageData对象应用在Canvas画布上。
- 语法
context.putImageData(imagedata, dx, dy);
context.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
- imagedata 包含图像像素信息的ImageData对象。
- dx 目标Canvas中被图像数据替换的起点横坐标。
- dy 目标Canvas中被图像数据替换的起点纵坐标。
- dirtyX (可选)图像数据渲染区域的左上角横坐标。默认值是0。
- dirtyY (可选)图像数据渲染区域的左上角纵坐标。默认值是0。
- dirtyWidth (可选)图像数据渲染区域的宽度。默认值是imagedata图像的宽度。
- dirtyHeight (可选)图像数据渲染区域的高度。默认值是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状态
.save()
存储当前Canvas的状态。
- 语法
context.save();
- 案例
先存储默认的Canvas状态,再还原,可以看到填充颜色变成了默认的黑色了。
// 保存初始Canvas状态
context.save();
// 设置红色填充
context.fillStyle = 'red';
// 矩形填充
context.fillRect(20, 20, 100, 60);
// 还原在绘制
context.restore();
// 矩形填充again
context.fillRect(180, 60, 100, 60);
.restore()
恢复Canvas到前一次存储的状态。
- 语法
context.restore();
- 案例
点击“保存”按钮,我们不断递增改变Canvas上下文的font属性值,然后执行save()方法存储当前Canvas状态,然后点击“恢复”按钮执行restore()进行恢复。
<input id="save" type="submit" value="保存">
<input id="restore" type="reset" value="恢复">
<canvas id="canvas"></canvas>
var context = canvas.getContext('2d');
// 初始字体大小
var fontSize = 16;
// 初始文字填充
context.font = fontSize + 'px arial';
context.fillText('观察字号大小', 10, 80);
// 按钮事件
save.addEventListener('click', function () {
context.clearRect(0, 0, 300, 150);
// 状态继续存储
context.save();
// 字号递增
fontSize++;
context.font = fontSize + 'px arial';
context.fillText('观察字号大小', 10, 80);
});
restore.addEventListener('click', function () {
context.clearRect(0, 0, 300, 150);
// 字号递减
fontSize--;
// 恢复上一次状态
context.restore();
// 看看现在字号大小
context.fillText('观察字号大小', 10, 80);
});
.canvas
反向识别当前上下文源自哪个HTMLCanvasElement。
- 语法
var canvas = context.canvas;
- 案例
扩展一个名为fullCircle()按照最短边画圆的Canvas上下文方法,此时需要知道Canvas的尺寸,此时canvas属性就派上用场了:
<canvas></canvas>
CanvasRenderingContext2D.prototype.fullCircle = function (style) {
// 反向获取当前上下文源Canavs元素
var canvas = this.canvas;
// 获取最短边半径
var minRadius = Math.min(canvas.width, canvas.height) / 2;
// 使用arc() API绘制圆
this.fillStyle = style || '#000';
// 绘制
this.beginPath();
this.arc(canvas.width / 2, canvas.height / 2, minRadius, 0, 2 * Math.PI);
this.fill();
};
// 调用
document.querySelector('canvas').getContext('2d').fullCircle('red');
其他方法
其他一些不常用的API方法。
.drawFocusIfNeeded()
如果给定元素被聚焦,则该方法在当前路径周围绘制焦点环。
- 语法
context.drawFocusIfNeeded(element);
context.drawFocusIfNeeded(path, element);
- element 用来检测当前是否处于focus状态的元素。此元素需要原本就是可聚焦的元素,例如按钮或者链接或者输入框等。然后,还需要放置在canvas标签中才有用。
- path 指Path2D对象。
- 案例
点击画布中的2个圈圈元素,此时会触发canvas包裹的对应的button按钮的focus态,然后Canvas圈圈呈现高亮效果,此高亮效果为系统自动,可以描绘出各种曲线。
<canvas id="canvas" width="240" height="120">
<button id="button1">按钮1</button>
<button id="button2">按钮2</button>
</canvas>
// 两个按钮元素
var button1 = document.getElementById('button1');
var button2 = document.getElementById('button2');
// canvas元素和上下文
var canvas = document.querySelector('#canvas');
var context = canvas.getContext('2d');
var draw = function () {
context.clearRect(0, 0, 240, 120);
// 设置字体样式
context.font = '16px STHeiti, SimHei';
context.textAlign = 'center';
context.textBaseline = 'middle';
// 绘制两个圆和文字
context.beginPath();
context.arc(60, 60, 50, 0, Math.PI * 2);
context.fillStyle = 'red';
context.fill();
context.fillStyle = 'white';
context.fillText('按钮1', 60, 60);
context.drawFocusIfNeeded(button1);
context.beginPath();
context.arc(180, 60, 50, 0, Math.PI * 2);
context.fillStyle = 'green';
context.fill();
context.fillStyle = 'white';
context.fillText('按钮2', 180, 60);
context.drawFocusIfNeeded(button2);
};
draw();
// 点击canvas元素
canvas.addEventListener('click', function (event) {
// 通过点击位置判断点击是哪个圈圈
var x = event.clientX - canvas.getBoundingClientRect().left;
var y = event.clientY - canvas.getBoundingClientRect().top;
// 距离两个圈圈圆心的距离
var distance1 = Math.sqrt((60 - x) * (60 - x) + (60 - y) * (60 - y));
var distance2 = Math.sqrt((180 - x) * (180 - x) + (60 - y) * (60 - y));
// 根据距离和半径大小判断是否在圈内
if (distance1 <= 50) {
button1.focus();
draw();
} else if (distance2 < 50) {
button2.focus();
draw();
}
});
.scrollPathIntoView()
将当前路径或给定路径滚动到视图中。
注意,这个功能在当前的浏览器标准中可能还未被广泛实现或支持。该方法旨在允许开发者将指定的路径(path)滚动到视口(viewport)内,使得该路径对用户可见。
- 语法
context.scrollPathIntoView();
context.scrollPathIntoView(path);
- path 指Path2D对象。
- 案例
<canvas id="canvas"></canvas>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.fillRect(10, 10, 30, 30);
ctx.scrollPathIntoView();