CanvasRenderingContext2D


了解CanvasRenderingContext2D

CanvasRenderingContext2D顾名思义就是“Canvas 2D渲染上下文”,可以理解为下面代码中的context。

var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');

context暴露了大量的API属性和方法,可以用来绘制文本,图形以及像素处理等,可以说是2D Canvas应用的核心所在。

绘制矩形

.clearRect()

清除指定矩形区域内部所有的像素信息为初始色(通常为透明)。

  1. 语法
context.clearRect(x, y, width, height);
  • x 矩形左上角x坐标。
  • y 矩形左上角y坐标。
  • width 被清除的矩形区域的高度。
  • height 被清除的矩形区域的宽度度。
  1. 案例
    先把一张图片绘制在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()

矩形填充,可以填充颜色,渐变,图案等。

  1. 语法
context.fillRect(x, y, width, height);
  • x 填充矩形的起点横坐标。
  • y 填充矩形的起点纵坐标。
  • width 填充矩形的宽度。
  • height 填充矩形的高度。
  1. 案例
    填充两个矩形,绘制一个十字图形,直接上代码:
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()

矩形描边。

  1. 语法
context.strokeRect(x, y, width, height);
  • x 描边矩形的起点横坐标。
  • y 描边矩形的起点纵坐标。
  • width 描边矩形的宽度。
  • height 描边矩形的高度。
  1. 案例
// 2像素宽矩形描边
context.lineWidth = 2;
context.strokeRect(75, 25, 150, 100);

绘制文本

.fillText()

文字填充,可以填充纯色,渐变或者图案。

  1. 语法
context.fillText(text, x, y, maxWidth);
  • text 用来填充的文本信息。
  • x 填充文本的起点横坐标。
  • y 填充文本的起点纵坐标。
  • maxWidth (可选)填充文本占据的最大宽度,当文本占据宽度超过此最大宽度时候,通过压缩每个文本宽度进行渲染,而非换行。
  1. 案例
    基本绘制
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()

文字描边。

  1. 语法
context.strokeText(text, x, y, maxWidth);
  • text 用来描边的文本信息。
  • x 描边文本的起点横坐标。
  • y 描边文本的起点纵坐标。
  • maxWidth (可选)填充文本占据的最大宽度,当文本占据宽度超过此最大宽度时候,通过压缩每个文本宽度进行渲染,而非换行。
  1. 案例
    基本绘制
// 文字描边
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属性值就是字符占据的宽度。

  1. 语法
context.measureText(text)
  • text 被测量的文本。
  1. 案例
    示意输出一个中文字符宽度以及输出一个英文单词的宽度,宽度直接在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,支持小数。

  1. 语法
context.lineWidth = value;
  • value 表示线的宽度。数值类型,默认值是1.0。如果是负数,0,NaN,或者Infinity都会忽略。
  1. 案例
    绘制一个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(方形端帽)。

  1. 语法
context.lineCap = 'butt';
context.lineCap = 'round';
context.lineCap = 'square';
  • butt 默认值,线的端点就像是个断头台,例如一条横线,终点x坐标是100,则这条线的最右侧边缘就是100这个位置,没有超出。
  • round 线的端点多出一个圆弧。
  • square 线的端点多出一个方框,框框的宽度和线一样宽,高度是线厚度的一半
  1. 案例
    绘制一个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(平角)。

  1. 语法
context.lineJoin = 'miter';
context.lineJoin = 'round';
context.lineJoin = 'bevel';
  • miter 默认值,转角是尖头。如果折线角度比较小,则尖头会非常长,因此需要miterLimit进行限制。
  • round 转角是圆头。
  • bevel 转角是平头。
  1. 案例
    绘制一个圆润的三角箭头。点击下面的单选选项,可以切换箭头类型是单箭头还是双箭头。
<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。

  1. 语法
context.miterLimit = value;
  • value 表示多大范围内转角表现为miter的宽度。数值类型,默认值是10.0。如果是负数,0,NaN,或者Infinity都会忽略。
  1. 案例
    以最大斜接长度 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()

返回当前虚线数值。返回值是一个偶数个数的数组。

  1. 语法
context.getLineDash();
  • 返回值 返回值是一个数组,数组里面的值都是数字,称为数字列表。所谓虚线,就是一段实线一段空隙交替出现的条线,而这里的数字列表中的值表示的就是交替的实线和间隙的长度值。如果设置虚线时候的数字个数是奇数,则数组里面的数字会被复制和链接,这样数量就变成偶数,例如,虚线设置为[5, 10, 15]将返回[5, 10, 15, 5, 10, 15]。
  1. 案例
    演示设置奇数个数的虚线,然后获取,看看值是多少。
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()

设置线条为虚线。

  1. 语法
ctx.setLineDash(segments);
  • segments 数值列表数组。例如[5, 5],表示虚线的实线和透明部分长度是5像素和5像素。如果此参数值适合空数组[],则表示实线,常用来重置虚线设置。
  1. 案例
    绘制一条虚线,只是虚线多尺寸并存
context.beginPath();
context.setLineDash([5, 10, 15, 30]);
context.moveTo(20, 70);
context.lineTo(280, 70);
context.stroke();

.lineDashOffset

设置虚线的起始偏移。

  1. 语法
context.lineDashOffset = value;
  • value 表示虚线起始绘制的偏移距离,为浮点型,默认值是0.0。
  1. 案例
    实现一个虚线边框不断旋转的效果
<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尽量避免使用)

  1. 语法
context.font = value;
  • value 字号字体值,其规则和CSS的font很类似,除了一些很微小的细节差异,其他几乎没什么区别。
  1. 案例
    一个简单的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。

  1. 语法
context.textAlign = value;

value支持以下值:

  • left 文本左对齐。也就是最终绘制的文本内容最左侧位置就是设定的x坐标值。
  • right 文本右对齐。也就是最终绘制的文本内容最右侧位置就是设定的x坐标值。
  • center 文本居中对齐。也就是最终绘制的文本内容的水平中心位置就是设定的x坐标值。
  • start 文本起始方位对齐。如果文本是从左往右排列的,则表示左对齐;如果文本是从右往左排列的(例如设置context.direction为rtl),则表示右对齐。
  • end 文本结束方位对齐。如果文本是从左往右排列的,则表示右对齐;如果文本是从右往左排列的(例如设置context.direction为rtl),则表示左对齐。
  1. 案例
    置水平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。

  1. 语法
context.textBaseline = value;

value支持以下值:

  • top 设定的垂直y坐标作为文本em区域(em区域可以看成中文字符占据的区域)的顶部。
  • hanging hanging主要在藏文和其他印度文字中使用,我们了解即可。
  • middle 设定的垂直y坐标作为文本em区域的垂直中心位置。
  • alphabetic 默认值。表示的是正常文本的基线,可以看成是字母x的下边缘。也就是设定的垂直y坐标就是字母x的下边缘。
  • ideographic ideographic主要在汉语、日语和韩语中使用。字面直译是表意基线。含义为:如果字符的主体突出在字母基线之下,则这是字符主体的底部。例如汉字“中”比字母x位置更低,因此,底部是汉字主体的底部。
  • bottom 设定的垂直y坐标作为文本em区域的底部。
  1. 案例
    展示不同的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。

  1. 语法
context.direction = value;

value支持以下值:

  • inherit (默认值)继承父级属性配置
  • ltr 从左侧开始显示渲染
  • rtl 从右侧开始显示渲染
  1. 案例
let align
// 设置字体和字号
context.font = '48px STHeiti, SimHei';
// 设置对齐方式
context.direction = align || 'inherit';
// 填充文字
context.fillText('测试文字内容', 120, 80);

填充和描边

.fillStyle

填充样式。默认值是#000000纯黑色。

  1. 语法
context.fillStyle = color;
context.fillStyle = gradient;
context.fillStyle = pattern;
  • color 使用纯色填充,支持RGB,HSL,RGBA,HSLA以及HEX色值。
  • gradient 使用渐变填充,可以是线性渐变或者径向渐变。
  • pattern 使用纹理填充。由于图片也能作为纹理,因此fillStyle也能填充普通的位图,可参见下面的案例。
  1. 案例
  • 色值填充
<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()

填充。

  1. 语法
context.fill();
context.fill(fillRule);
context.fill(path, fillRule);
  • fillRule 填充规则。用来确定一个点实在路径内还是路径外。可选值包括: nonzero:非零规则,此乃默认规则、evenodd:奇偶规则。
  • path 指Path2D对象。
  1. 案例
    “非零规则”和“奇偶规则”的不同表现
<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纯黑色。

  1. 语法
context.strokeStyle = color;
context.strokeStyle = gradient;
context.strokeStyle = pattern;
  • color 描边设置为颜色。
  • gradient 描边设置为渐变。
  • pattern 描边设置为图案。
  1. 案例
  • 描边设置为颜色
// 红色描边
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()

描边。

  1. 语法
context.stroke();
context.stroke(path);
  • path 指Path2D对象。IE浏览器不支持。
  1. 案例
    描边一条直线
context.moveTo(50, 50);
context.lineTo(250, 100);
// 描边
context.stroke();

渐变相关

.createLinearGradient()

创建线性渐变。

  1. 语法
context.createLinearGradient(x0, y0, x1, y1);
  • 返回值 返回值是CanvasGradient对象。
  • x0 渐变起始点横坐标。
  • y0 渐变起始点纵坐标。
  • x1 渐变结束点横坐标。
  • y1 渐变结束点纵坐标。
    线性渐变效果比较好脑补,就是从坐标点[x0, y0]到坐标点[x1, y1]的位置画一条线,然后整个渐变色带与与这条线垂直。
  1. 案例
    头尾二色渐变,以及渐变坐标的全局特性
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()

创建径向渐变。

  1. 语法
context.createRadialGradient(x0, y0, r0, x1, y1, r1);
  • 返回值 返回值是CanvasPattern对象。
  • x0 起始圆的横坐标。
  • y0 起始圆的纵坐标。
  • r0 起始圆的半径。
  • x1 结束圆的横坐标。
  • y1 结束圆的纵坐标。
  • r1 结束圆的半径。
  1. 案例
  • 标准两色径向渐变
<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对象。

  1. 语法
context.createPattern(image, repetition);
  • 返回值 返回值是CanvasPattern对象。
  • image 用来平铺的CanvasImageSource图像。可以是下面的类型
    • HTMLImageElement,也就是元素。
    • HTMLVideoElement,也就是
    • HTMLCanvasElement
    • CanvasRenderingContext2D
    • ImageBitmap
    • ImageData
    • Blob
  • repetition 图案的平铺方式,可以是下面的值:
    • ‘repeat’,水平和垂直平铺。当repetition属性值为空字符串’’或者null,也会按照’repeat’进行渲染。
    • ‘repeat-x’,仅水平平铺。
    • ‘repeat-y’,仅垂直平铺。
    • ‘no-repeat’,不平铺。
  1. 案例
    图片缩小,并作为纹理显示。我们直接把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。

  1. 语法
context.shadowBlur = value;
  • value 表示阴影的模糊程度。数值类型,可以是小数。默认值是0.会忽略负数、NaN等
  1. 案例
  • 矩形块的阴影模糊
// 设置阴影红色,同时模糊大小为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

阴影颜色。默认值是全透明黑色。

  1. 语法
context.shadowColor = color;
  • color 表示阴影的颜色。各种颜色值都支持,默认是透明黑:rgba(0, 0, 0, 0)
  1. 案例
    文字阴影效果,深黑色,无模糊
// 设置阴影深黑色,同时右下角偏移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。

  1. 语法
context.shadowOffsetX = offset;
  • offset 表示偏移的大小,数值,默认值是0。忽略Infinity或者NaN值。
  1. 案例
    借助足够大的水平偏移,克隆一个相同的文字。
// 设置阴影深黑色,同时右偏移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。

  1. 语法
context.shadowOffsetY = offset;
  • offset 表示偏移的大小,数值,默认值是0。忽略Infinity或者NaN值。
  1. 案例
    借助足够大的水平偏移,克隆一个相同的文字。
// 上阴影
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()

开始一个新路径。

  1. 语法
context.beginPath();
  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();
// 开始路径
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()

闭合一个路径。

  1. 语法
context.closePath();
  1. 案例
    两个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()

路径绘制起始点。

  1. 语法
context.moveTo(x, y);
  • x 落点的横坐标。
  • y 落点的纵坐标。
  1. 案例
    绘制直线一条
context.beginPath();
context.moveTo(50, 20);
context.lineTo(200, 100);
context.stroke();

.lineTo()

绘制直线到指定坐标点。

  1. 语法
context.lineTo(x, y);
  • x 绘制的直线的落点的横坐标。
  • y 绘制的直线的落点的纵坐标。
  1. 案例
    一个贝塞尔曲线然后执行lineTo()方法
context.beginPath();
context.moveTo(50, 20);
context.bezierCurveTo(100, 100, 200, 40, 250, 120);
context.lineTo(50, 120);
context.stroke();

.bezierCurveTo()

绘制贝赛尔曲线到指定坐标点。

  1. 语法
context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
  • cp1x 第1个控制点的横坐标。
  • cp1y 第1个控制点的纵坐标。
  • cp2x 第2个控制点的横坐标。
  • cp2y 第2个控制点的纵坐标。
  • x 结束点的横坐标。
  • y 结束点的纵坐标。
  1. 案例
// 开始绘制
context.beginPath();
context.moveTo(50, 50);
context.bezierCurveTo(100, 100, 221, 24, 250, 100);
context.stroke();

.quadraticCurveTo()

绘制二次贝赛尔曲线到指定坐标点。

  1. 语法
context.quadraticCurveTo(cpx, cpy, x, y);
  • cpx 控制点的横坐标。
  • cpy 控制点的纵坐标。
  • x 结束点的横坐标。
  • y 结束点的纵坐标。
  1. 案例
// 开始绘制
context.beginPath();
context.moveTo(50, 50);
context.quadraticCurveTo(72, 99, 243, 57);
context.stroke();

.arc()

绘制圆弧(包括圆)。

  1. 语法
context.arc(x, y, radius, startAngle, endAngl, anticlockwise);
  • x 圆弧对应的圆心横坐标。
  • y 圆弧对应的圆心纵坐标。
  • radius 圆弧的半径大小。
  • startAngle 圆弧开始的角度,单位是弧度。
  • endAngl 圆弧结束的角度,单位是弧度。
  • anticlockwise (可选)弧度的开始到结束的绘制是按照顺时针来算,还是按时逆时针来算。如果设置为true,则表示按照逆时针方向从startAngle绘制到endAngle。
  1. 案例
  • 绘制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()

绘制圆弧,和之前的点以直线相连。

  1. 语法
context.arcTo(x1, y1, x2, y2, radius);
  • x1 第1个控制点的横坐标。
  • y1 第1个控制点的纵坐标。
  • x2 第2个控制点的横坐标。
  • y2 第2个控制点的纵坐标。
  • radius 圆弧的半径大小。
  1. 案例
context.beginPath();
context.moveTo(50, 50);
context.arcTo(150, 100, 200, 40, 40);
context.lineTo(200, 40);
context.stroke();

.rect()

绘制矩形路径。

  1. 语法
context.rect(x, y, width, height);
  • x 矩形路径的起点横坐标。
  • y 矩形路径的起点纵坐标。
  • width 矩形的宽度。
  • height 矩形的高度。
  1. 案例
    中心位置绘制一个100*100大小的矩形,然后描边
// 矩形
context.rect(100, 25, 100, 100);
context.stroke();

.ellipse()

绘制椭圆路径。

  1. 语法
context.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
  • x 椭圆弧对应的圆心横坐标。
  • y 椭圆弧对应的圆心纵坐标。
  • radiusX 椭圆弧的长轴半径大小。
  • radiusY 椭圆弧的短轴半径大小。
  • rotation 椭圆弧的旋转角度,单位是弧度。
  • startAngle 圆弧开始的角度,角度从横轴开始算,单位是弧度。
  • endAngle 圆弧结束的角度,单位是弧度。
  • anticlockwise (可选)弧度的开始到结束的绘制是按照顺时针来算,还是按时逆时针来算。如何设置为true,则表示按照逆时针方向从startAngle绘制到endAngle。
  1. 案例
    绘制一个长轴短轴比2:1,同时旋转45°的椭圆
// 绘制椭圆
context.ellipse(150, 75, 80, 40, Math.PI / 4, 0, 2 * Math.PI);
context.stroke();

.clip()

创建剪裁路径,之后绘制的路径只有在里面的才会显示。

  1. 语法
context.clip();
context.clip(fillRule);
context.clip(path, fillRule);
  • fillRule 填充规则。用来确定一个点实在路径内还是路径外。可选值包括:
    • nonzero:非零规则。此乃默认规则。
    • evenodd:奇偶规则。
  • path 指Path2D对象。
  1. 案例
    利用剪裁实现一个图案填充效果。实现图案填充,标准用法是创建一个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()

当前点是否在指定路径内。

  1. 语法
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对象。
  1. 案例
    在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()

当前点是否在指定路径描边上。

  1. 语法
context.isPointInStroke(x, y);
context.isPointInStroke(path, x, y);
  • 返回值 返回Boolean值。
  • x 用来检测的点的横坐标。
  • y 用来检测的点的纵坐标。
  • path 指Path2D对象。
  1. 案例
    在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()

旋转。

  1. 语法
context.rotate(angle);
  • angle Canvas画布坐标系旋转的角度,单位是弧度。注意,此旋转和CSS3的旋转变换不一样,旋转的是坐标系,而非元素。因此,实际开发的时候,旋转完毕,需要将坐标系再还原。
  1. 案例
  • 基本旋转
// 旋转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()

缩放。

  1. 语法
context.scale(x, y);
  • x Canvas坐标系水平缩放的比例。支持小数,如果值是-1,表示水平翻转。
  • y Canvas坐标系垂直缩放的比例。支持小数,如果值是-1,表示垂直翻转。
  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()

位移。

  1. 语法
context.translate(x, y);
  • x 坐标系水平位移的距离。
  • y 坐标系垂直位移的距离。
  1. 案例
    借助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()

当前矩阵变换基础上再次矩阵变换。

  1. 语法
context.transform(a, b, c, d, e, f);
  • a 水平缩放。
  • b 水平斜切。
  • c 垂直斜切。
  • d 垂直缩放。
  • e 水平位移。
  • f 垂直位移。
  1. 案例
    借助transform()方法绘制一个平行四边形
context.transform(1, 0, 1, 1, 0, 0);
context.fillRect(10, 20, 100, 100);

.setTransform()

直接重置为当前设置的矩阵变换。(此方法和transform()方法的区别在于,后者不会完全重置已有的变换,而是累加。)

  1. 语法
context.setTransform(a, b, c, d, e, f);
  • a 水平缩放。
  • b 水平斜切。
  • c 垂直斜切。
  • d 垂直缩放。
  • e 水平位移。
  • f 垂直位移。
  1. 案例
    借助setTransform()方法绘制一个平行四边形
context.setTransform(1, 0, 1, 1, 0, 0);
context.fillRect(10, 20, 100, 100);

透明度和层级

.globalAlpha

全局透明度。

对于纯色图形的绘制,如果需要用到半透明或者不透明,建议使用RGBA或者HSLA色值进行控制,比使用globalAlpha方便,因为按照我个人的使用经验,使用globalAlpha设置透明度之后都是需要再进行还原设置的,而直接使用RGBA或者HSLA色值则没有这样的重置操作。
但是,如果是绘制图片,借助globalAlpha属性是更好的选择

  1. 语法
context.globalAlpha = value;
  • value value就是设置的全局透明度,范围是0到1,范围以外的值会被忽略。
  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

设置图形叠加时候的混合方式,可以用来改变绘制元素上下叠加关系,也就是层级。

  1. 语法
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 亮度。最终的颜色保留底层的色调和色度,同时采用顶层的亮度。
  1. 案例
  • 文字镂空效果
// 绘制图片
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()

图片绘制在画布上。

  1. 语法
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画布上。
  1. 案例
  • 保持原始图片尺寸和比例
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对象。

  1. 语法
context.createImageData(width, height); 
context.createImageData(imagedata);
  • width ImageData对象包含的width值。如果ImageData对象转换成图像,则此width也是最终图像呈现的宽度。
  • height ImageData对象包含的height值。如果ImageData对象转换成图像,则此height也是最终图像呈现的高度。
  • imagedata 一个存在的ImageData对象,只会使用该ImageData对象中的width和height值,包含的像素信息会全部转换为透明黑。
  1. 案例
    使用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对象。

  1. 语法
context.getImageData(sx, sy, sWidth, sHeight);
  • sx 需要返回的图像数据区域的起始横坐标。
  • sy 需要返回的图像数据区域的起始纵坐标。
  • sWidth 需要返回的图像数据区域的宽度。
  • sHeight 需要返回的图像数据区域的高度。
  1. 案例
    绘制一张图片到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画布上。

  1. 语法
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图像的高度。
  1. 案例
    使用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的状态。

  1. 语法
context.save();
  1. 案例
    先存储默认的Canvas状态,再还原,可以看到填充颜色变成了默认的黑色了。
// 保存初始Canvas状态
context.save();
// 设置红色填充
context.fillStyle = 'red';
// 矩形填充
context.fillRect(20, 20, 100, 60);
// 还原在绘制
context.restore();
// 矩形填充again
context.fillRect(180, 60, 100, 60);

.restore()

恢复Canvas到前一次存储的状态。

  1. 语法
context.restore();
  1. 案例
    点击“保存”按钮,我们不断递增改变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。

  1. 语法
var canvas = context.canvas;
  1. 案例
    扩展一个名为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()

如果给定元素被聚焦,则该方法在当前路径周围绘制焦点环。

  1. 语法
context.drawFocusIfNeeded(element);
context.drawFocusIfNeeded(path, element);
  • element 用来检测当前是否处于focus状态的元素。此元素需要原本就是可聚焦的元素,例如按钮或者链接或者输入框等。然后,还需要放置在canvas标签中才有用。
  • path 指Path2D对象。
  1. 案例
    点击画布中的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)内,使得该路径对用户可见。

  1. 语法
context.scrollPathIntoView();
context.scrollPathIntoView(path);
  • path 指Path2D对象。
  1. 案例
<canvas id="canvas"></canvas>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

ctx.beginPath();
ctx.fillRect(10, 10, 30, 30);
ctx.scrollPathIntoView();

文章作者: Ron.
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ron. !
  目录