[译] HTML Canvas 2D Context

全篇共 14130 字。按500字/分钟阅读,预计用时 28.3 分钟。总访问 529 次,日访问 2 次。

W3C编写的这个Canvas规范是给浏览器厂商参考的,它告诉厂商们应该如何实现canvas的绘图上下文。而对于普通前端开发者来说,能知道其原理固然好,相比于这些细枝末节,能在工作中将canvas活学活用才更重要。所以这篇规范并不是前端必读,可以作为想更深入canvas的参考资料来看待。

点击 HTML Canvas 2D Context,在新页面打开该规范的原文。

Canvas开源类库

对于普通前端开发者,用原生的Canvas绘图上下文API实现你的创意未免繁琐,所以你可以借助现成的Canvas开源类库实现你的创意。

  • Paper.js
    The Swiss Army Knife of Vector Graphics Scripting.
  • Fabric.js
    KineticJS is an HTML5 Canvas JavaScript framework that extends the 2d context by enabling canvas interactivity for desktop and mobile applications.
  • Easel.js
    A JavaScript library that makes working with the HTML5 Canvas element easy. Useful for creating games, generative art, and other highly graphical experiences.
  • Kinetic.js
    fast, robust, HTML5 Canvas Library that is no longer maintained! (注意这个库不再维护)

厂商遵守规范一致性

这个规范是一个HTML规范。所有标准的要求,标准的类、定义、依赖、术语,和排版惯例描述都可参照HTML5规范。HTML接口定义。这个规范定义2d上下文是如何实现的CanvasRenderingContext2D这个接口。当用户使用canvas元素的getContext()方法时,这个方法会返回一个包含绘图上下文的新的对象。用户代理(即浏览器)必须返回一个新的CanvasRenderingContext2D对象,任何额附加的参数都会被忽略。

这个2D绘图上下文代表一个笛卡尔坐标系的平面,即(0,0)原点在画布的左上角,并且x坐标会向右越来越大,y坐标向下原来越大。

接口描述语言

typedef (HTMLImageElement or HTMLVideoElement or HTMLCanvasElement) CanvasImageSource;

// CanvasRenderingContext2D 接口
interface CanvasRenderingContext2D {
  // 对canvas元素的引用
  readonly attribute HTMLCanvasElement canvas;
  
  // 上下文状态管理
  void save();
  void restore();
	
  // 坐标系变换
  void scale(unrestricted double x, unrestricted double y);
  void rotate(unrestricted double angle);
  void translate(unrestricted double x, unrestricted double y);
  void transform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f);
  void setTransform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f);

  // 合成
  attribute unrestricted double globalAlpha;
  attribute DOMString globalCompositeOperation;

  // 颜色和样式
  attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle;
  attribute (DOMString or CanvasGradient or CanvasPattern) fillStyle;
  CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
  CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
  CanvasPattern createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetition);

  // 阴影
  attribute unrestricted double shadowOffsetX;
  attribute unrestricted double shadowOffsetY;
  attribute unrestricted double shadowBlur;
  attribute DOMString shadowColor;

  // 矩形
  void clearRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
  void fillRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
  void strokeRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);

  // 路径
  void beginPath();
  void fill();
  void stroke();
  void drawFocusIfNeeded(Element element);
  void clip();
  boolean isPointInPath(unrestricted double x, unrestricted double y);

  // 文本
  void fillText(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth);
  void strokeText(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth);
  TextMetrics measureText(DOMString text);

  // 绘制图像
  void drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy);
  void drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh);
  void drawImage(CanvasImageSource image, unrestricted double sx, unrestricted double sy, unrestricted double sw, unrestricted double sh, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh);

  // 热点
  void addHitRegion(HitRegionOptions options);
  void removeHitRegion(DOMString id);
  void clearHitRegions();

  // 像素操作
  ImageData createImageData(unrestricted double sw, unrestricted double sh);
  ImageData createImageData(ImageData imagedata);
  ImageData getImageData(double sx, double sy, double sw, double sh);
  void putImageData(ImageData imagedata, double dx, double dy);
  void putImageData(ImageData imagedata, double dx, double dy, double dirtyX, double dirtyY, double dirtyWidth, double dirtyHeight);
};

CanvasRenderingContext2D implements CanvasDrawingStyles;
CanvasRenderingContext2D implements CanvasPathMethods;

[NoInterfaceObject]
interface CanvasDrawingStyles {
  // 线
  attribute unrestricted double lineWidth;
  attribute DOMString lineCap;
  attribute DOMString lineJoin;
  attribute unrestricted double miterLimit;

  // 虚线
  void setLineDash(sequence<unrestricted double> segments); // (default: empty)
  sequence<unrestricted double> getLineDash();
  attribute unrestricted double lineDashOffset;


  // 文本
  attribute DOMString font;
  attribute DOMString textAlign;
  attribute DOMString textBaseline;
};

[NoInterfaceObject]
interface CanvasPathMethods {
  void closePath();
  void moveTo(unrestricted double x, unrestricted double y);
  void lineTo(unrestricted double x, unrestricted double y);
  void quadraticCurveTo(unrestricted double cpx, unrestricted double cpy, unrestricted double x, unrestricted double y);
  void bezierCurveTo(unrestricted double cp1x, unrestricted double cp1y, unrestricted double cp2x, unrestricted double cp2y, unrestricted double x, unrestricted double y);
  void arcTo(unrestricted double x1, unrestricted double y1, unrestricted double x2, unrestricted double y2, unrestricted double radius);
  void rect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
  void arc(unrestricted double x, unrestricted double y, unrestricted double radius, unrestricted double startAngle, unrestricted double endAngle, optional boolean counterclockwise = false);
  };

interface CanvasGradient {
  void addColorStop(double offset, DOMString color);
};

interface CanvasPattern {

};

interface TextMetrics {
  readonly attribute double width;
};

dictionary HitRegionOptions {
  DOMString id = "";
  Element? control = null;
};

interface ImageData {
  readonly attribute unsigned long width;
  readonly attribute unsigned long height;
  readonly attribute Uint8ClampedArray data;
};

画布的状态

每个上下文包含了一个保存绘制状态的栈,绘制状态由以下内容组成:

  • 当前变换矩阵
  • 当前裁剪区域
  • 当前的这些属性值:
    • strokeStyle
    • fillStyle
    • globalAlpha
    • lineWidth
    • lineCap
    • lineJoin
    • miterLimit
    • shadowOffsetX
    • shadowOffsetY
    • shadowBlur
    • shadowColor
    • globalCompositeOperation
    • font
    • textAlign
    • textBaseline

当前路径和当前bitmap并不是绘制状态的一部分,当前路径是固定的,并且只能使用 beginPath() 方法重置。当前bitmap是canvas的一个属性,并不是上下文。

//将当前绘制状态推入栈。
context.save()
//将当前绘制状态出栈,恢复上下文到最近一个状态。
context.restore()

线的样式

// 返回或设置当前线的宽度。
// 忽略无穷数和零。
context.lineWidth [ = value ]

// 返回线的端点样式。
// 可以被设置,改变线的端点样式。
// 可能的线端点样式有 "butt","round","square",其他值则忽略。
context.lineCap [ = value ]

// 返回当前线的拐点样式。
// 可以被设置,改变线的拐点样式。
// 可能的线的拐点样式有 "bevel", "round", "miter"。 其他的值则忽略。
context.lineJoin [ = value ]

// 返回当前斜接限制比率。
// 可以被设置,改变斜接限制比率,非有限值和零值则直接忽略。
context.miterLimit [ = value ]

// 设置当前线的破折模式(仅被用于描边),参数是一个一维数组,分别为有线可无线的长度。
context.setLineDash(segments)

// 返回一个当前线的破折模式的拷贝。
segments = context.getLineDash()

// 返回破折的偏移量
// 可以被设置,改变偏移量,非有限值和零值则直接忽略。
context.lineDashOffset[ = value ]

实现了CanvasDrawingStyle接口的对象拥有属性和方法,用来控制对象是如何操纵线的。

lineWidth属性给了线一个宽度,在单位空间的坐标系中,如果需要取得,就必须返回当前的值,如果需要设置,零值、无穷值、负值和非数值的值可直接忽略,其他可用的值必须改变当前值到一个新的值。

当实现了CanvasDrawingStyle接口的对象被创建,lineWidth属性必须设置初始值为1.0。

lineCap属性定义了线的端点样式类型,这三个可用的值分别是"bott", “round”, “square”,需要取得该值,它必须返回当前的值。需要设置该值,如果新值是"butt",“round”,"square"其中之一,则必须将旧值改变为新的值,其他值直接忽略,原值不做变动。

CanvasDrawingStyle接口被对象实现后,lineCap的初始值为"butt"。

lineJoin属性定义了两条线在拐角相遇时的样式,这三种可用的值分别为"bevel",“round”,“miter”。

需要获取值,必须返回当前值。需要设置值时,如果新的值时"bevel",“round”,"miter"其中之一,那么当前的值必须改变为新的值;其他值直接忽略,原值不变动。

CanvasDrawingStyle接口被对象实现后,lineJoin必须初始化为"miter"。

lineJoin属性拥有值"miter",描边使用斜接限制比率来决定如何渲染两条线的连接效果。斜接限制比率可以被明确的使用miterLimit属性来指定。需要获取该值,必须返回当前的值,需要设置该值,零值,负值,无限值和非数值得值则必须忽略,并保持原值不变动。其他值可用的值则必须改变当前值为新的值。

当对象实现了CanvasDrawingStyle 接口时,miterLimit属性必须初始化为10.0。

每一个CanvasDrawingStyle对象都有一组dash列,初始化时dash list必须为空。

文本的样式

// 返回当前字体设置。
// 可以被设置,改变当前字体,语法和CSS中设置字体属性的语法相同,属性值如若不能被解析成CSS字体值则直接忽略。
context.font[ = value ]

// 返回当前文本的水平对齐样式。
// 可以被设置,改变水平对齐方式。可以的值有"start", "end", "left", "right"和"center",其他的值则直接忽略。
context.textAlign[ = value ]

// 返回当前垂直对齐样式。
// 可能的属性值有"top","hanging","middle","alphabetic","ideographic"或者"bottom"
// 可以被设置,改变垂直对齐样式,可被设置的值在下方,其他值则直接忽略,默认的值为“alphabetic”。
context.textBaseline[ = value ]

使用@font-face声明的字体,需要等待字体被加载后才能使用。只有矢量字体可以被用户代理识别;如果用户代理使用位图字体则渲染后的效果会非常的丑陋。

context.font = 'italic 400 12px/2 Unkonw Font, sans-serif';

![/public/article/1503974960681/baselines.png]

创建路径

每一个实现了CanvasPathMethod接口的对象都拥有path,一个路径拥有一系列的零个或更多个子路径。每一个子路径都包含一系列的点,由直线或者曲线连接,由一个标志标明是否闭合。一个闭合的子路径是指最后一个点和该路径的第一个点由一条直线相连接。子路径少于两个点则直接忽略。

当一个对象实现了CanvasPathMethod接口,路径必须初始化为拥有零个子路径。

context.movetTo(x, y);
// 使用给的点创建一条新的子路径。

context.closePath();
// 标记当前子路径为闭合的,并且使用一个和刚刚闭合的路径一模一样的一条新的组路径。

context.lineTo(x, y);
// 添加给的点到当前的子路径,通过一条直线相连接。

context.quadraticCurveTo(cpx, cpy, x, y);
// 添加给的点到当前的子路径,使用给的点创建一个二次贝塞尔曲线并连接到上一个点。

context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
// 添加给的点到当前路径,使用给的点创建一个三次贝塞尔曲线并连接到上一个点。

context.arcTo(x1, y1, x2, y2, radius);
// 使用给定的控制点和半径添加一个圆弧,使用一条直线连接上一个点。
// 如果给的半径为负数则抛出一个**IndexSizeError**错误。

canvas arc

context.arc(x, y, radius, startAngle, endAngle[, counterclockwise]);
// 使用给定的参数创建一个圆。

context.rect(x, y, width, height);
// 使用给定的参数创建一个矩形。

如下的方法允许开发者操作路径对象,即实现了CanvasPathMethods接口的对象。

对于CanvasRenderingContext2D对象,点和线添加到当前默认的路径中时,必须被通过变换后添加到路径中。

使用moveTo()方法将重新开始绘制一段路径。

如果对象的路径没有子路径,则**closePath()**方法必须不作为。否则,它必须标记为最后一条子路径为闭合的。

变换

每一个CanvasRenderingContext2D对象拥有一个当前的变换矩阵,并拥有一系列的方法操作这些矩阵,当一个CanvasRenderingContext2D对象被创建后,变换矩阵必须被初始化为有限的变换。

当在CanvasRenderingContext2D上创建当前路径,绘制文本,形状,路径时,变换矩阵被应用到坐标系中。

context.scale(x, y);
// 叠加改变变换矩阵通过给定的参数应用缩放变换。

context.rotate(angle);
// 叠加改变变换矩阵通过给定的参数应用旋转变换,参数为弧度制。

context.translate(x, y);
// 叠加改变变换矩阵通过给定的参数应用平移变换。

context.transform(a, b, c, d, e, f);
// 叠加改变变换矩阵通过应用给定的参数。

context.setTransform(a, b, c, d, e, f);
// 非叠加重置变换矩阵通过给定的参数。

填充和描边样式

context.fillStyle[ = value ]
// 返回当前填充形状使用的样式
// 可以被设置,改变填充样式
// 这个样式可以是字符串,CSS颜色,或者**CanvasGradient**或者**CanvasPattern**对象。
// 不可用的值将直接被忽略

context.strokeStyle[ = value ]
// 返回当前描边形状使用的样式
// 可以被设置,改变描边样式。
// 样式可以是字符串,包含CSS颜色,或者**CanvasGradient**或者**CanvasPattern**对象
// 不可用的值将被直接忽略。

创建线性渐变和径向渐变:

gradient.addColorStop(offset, color);
// 通过给定的颜色值和偏移位置,添加一个颜色控制点,0表示渐变开始的位置,1表示渐变结束的位置。
// 当给定的偏移位置溢出了0-1的范围,则抛出**IndexSizeError**错误。如果颜色值不能被解析则抛出**SyntaxError**错误。

gradient = context.createLinearGradient(x0, y0, x1, y1);
// 返回**CanvasGradient**对象,代表一个线性渐变,绘制沿着给定的坐标。

gradient = context.createRadinalGradient(x0, y0, r0, x1, y1, r1);
// 返回**CanvasGradient**对象,代表径向渐变,绘制沿着给定的两个圆形表示参数。
// 如果给定的圆形半径为负值,则抛出**IndexSizeError**错误。

创建填充模式:

patter = context.createPattern(image, repetition);
// 返回**CanvasPattern**对象,使用给定的图像源和重复规则。
// 允许的重复规则有"repeat","repeat-x","repeat-y"和"no-repeat",如果重复参数为空,则默认重复规则为"repeat"。
// 如果图像源没有图像数据,抛出**InvalidStateError**异常,如果第二个参数不是被允许的值,抛出**Syntax**异常。如果图像源没有被完全解码,则该方法返回**null**。

绘制矩形

context.clearRect(x, y, w, h);
// 在给定的参数的范围内,清空所有画布上的像素

context.fillRect(x, y, w, h);
// 使用给定的参数绘制矩形并使用当前上下文的颜色填充。

context.strokeRect(x, y, w, h);
// 使用给定的参数绘制矩形并使用当前上下文描边。

绘制文本

context.fillText(text, x, y[, maxWidth]);

context.strokeText(text, x, y[, maxWidth]);
// 使用给定的文本在给定的位置填充或描边,如果最大宽度在参数中被提供了,文本将被缩放以适应宽度(如果需要的话)。

metrics = context.measureText(text);
// 通过传入一段文本,返回应用当前字体上下文计算后**TextMetrics**对象,对象包括文本的宽度。

metrics.width
// 返回文本的宽度。

绘制路径

context.beginPath();
// 重置当前路径。

context.fill();
// 使用当前上下文的填充样式,填充当前路径的子路径。

context.stroke();
// 使用当前上下文的描边样式,描边当前路径的子路径。

context.drawFocusIfNeeded(element);
// 将当前画布的定位信息返回给用户,基于当前路径,如果给定的元素已经获取焦点,绘制一个获取焦点的样式,默认样式根据用户代理而定。

context.clip();
// 下方的绘制内容将限制在上方的路径内。

context.isPointInPath(x, y);
// 返回给定的点坐标是否在当前路径内。如果在则返回**True**,否则返回**false**。

图像源

以下元素能作为图像源。

  • HTMLImageElement(img元素)
  • HTMLVideoElement(video元素)
  • HTMLCanvasElement(canvas元素)

这些可使用的图像源均需要正确解码并加载完成后方可被渲染。请求的图像源必须要与当前源同源。

绘制图像源

drawImage方法被用来在画布上绘制图像。这个方法可以被以下三种不同的传参方式调用:

  • drawImage(image, x, y);
  • drawImage(image, dx, dy, dw, dh);
  • drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);

每一种方法的图像源都可以是HTMLImageElement、HTMLCanvasElement、HTMLVideoElement。

context.drawImage(image, dx, dy);
context.drawImage(image, dx, dx, dw, dh);
context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);

通过给定的参数绘制图像源到画布上。

如果给定的参数不是img、canvas或者video元素,就抛出TypeMismatchError异常,如果图像没有图像数据,抛出InvalidStateError异常,如果图像源的二维大小为0,抛出一个IndexSizeError异常,如果图像源没有被完全解码,则不做绘制。

响应区域

每一个canvas元素的私有上下文均为CanvasRenderingContext2D对象,便必须有响应区域列表。

context.addHitRegion(option);
// 添加一个响应区域到画布基于当前默认的路径,参数是一个包含如下属性的对象:
// id(默认为空的字符串)
// ID用于本次区域,用于在画布上的鼠标事件。

// control (默认为空)
// 一个元素(**canvas**的后代),事件的在这里被转发。

// 移除一个响应区域。
context.removeHitRegion(id);

// 从位图画布上移除所有的响应区域。
context.clearHitRegions();

像素操作

imagedata = context.createImageData(sw, sh);
// 返回一个**imageData**对象借助给定的维度范围。

imagedata = context.createImageData(imagedata);
// 返回**imageData**对象使用相同的提供的维度范围。

imagedata = context.getImageData(sx, sy. sw, sh);
// 返回**imageData**对象,包含图像数据为给定的矩形范围。

imagedata.width
imagedata.height
// 返回实际的维度数据。

imagedata.data
// 返回以为数组包含RGBA数据。

context.putImageData(imagedata, dx, dy[, dirtyX, dirtyY, dirtyWidth, dirtyHeight]);
// 从imageData绘制数据到画布上。

合成

context.globalAlpha[ = value ]
// 返回或设置当前不透明度,提供的值的范围为0-1;

context.globalCompositeOperation[ = value ]
// 返回或设置合成模式。
  • source-atop
    A atop B. Display the source image wherever both images are opaque. Display the destination image wherever the destination image is opaque but the source image is transparent. Display transparency elsewhere.
  • source-in
    A in B. Display the source image wherever both the source image and destination image are opaque. Display transparency elsewhere.
  • source-out
    A out B. Display the source image wherever the source image is opaque and the destination image is transparent. Display transparency elsewhere.
  • source-over (default)
    A over B. Display the source image wherever the source image is opaque. Display the destination image elsewhere.
  • destination-atop
    B atop A. Same as source-atop but using the destination image instead of the source image and vice versa.
  • destination-in
    B in A. Same as source-in but using the destination image instead of the source image and vice versa.
  • destination-out
    B out A. Same as source-out but using the destination image instead of the source image and vice versa.
  • destination-over
    B over A. Same as source-over but using the destination image instead of the source image and vice versa.
  • lighter
    A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit.
  • copy
    A (B is ignored). Display the source image instead of the destination image.
  • xor
    A xor B. Exclusive OR of the source image and destination image.
  • vendorName-operationName
    Vendor-specific extensions to the list of composition operators should use this syntax.

阴影

context.shadowColor[ = value ]
// 返回当前阴影颜色
// 设置阴影颜色

context.shadowOffsetX[ = value ]
context.shadowOffsetY[ = value ]
// 返回或设置阴影的偏移量

context.shadowBlur[ = value ]
// 返回或设置阴影的模糊值。
译者 » 陈帅华
发布日期 » 2017年8月29日 周二
更新日期 » 2020年3月11日 周三
上一篇 » MySQL实用命令汇总
下一篇 » 开启微信公众号开发者模式
:)记录此刻想法
请选择登录方式,开始记录你的想法。
授权微博登录
授权Github登录