--- title: Canvas API 速食 date: 2020-12-16 author: ac tags: - Canvas - JavaScript categories: - 可视化 --- > Canvas API接口是使用``标签,通过`JavaScript`来绘制图形,用于动画、数据可视化、图片编辑、游戏画面以及实时视频等功能的Web API接口。 ``标签是`H5`中新添加的元素。可以将``标签看作图形的容器(画布),在上面绘制路径、圆、盒子、字符等。 ### 一、创建画布 ```html ``` ``标签实际上只有两个属性`width`和`height`(`id`属性是每个HTML元素都有的默认属性),但这是可选的,如果不指定,则`width`默认为300像素,`heigth`默认为150像素。除了在标签内的属性中定义大小外,也可以使用`CSS`样式定义。 > 如果`CSS`样式是以响应式或百分比的形式定义的大小,当``元素的尺寸与原始尺寸不等比例伸缩时,画布内已绘制的图像会出现变形。 被创建的``画布,在没有设置样式前,默认是完全透明的。 ### 二、渲染上下文 ``元素创建来一个固定大小的画布,它公开了一个或多个**渲染上下文**,用来绘制和处理要展示的内容。 **渲染上下文**的种类有很多,但Canvas API主要聚焦于2D图形,所以这里将注意力放在2D渲染上下文中。另外像同样是使用``元素的WebGL API 则是使用基于`OpenGL ES`的3D上下文。 渲染上下文可以通过Canvas元素的`getContext()`方法获得,该方法是用来获得渲染上下和它的绘画功能。对于2D图像,可以在`getContext()`方法中添加一个参数,来指定上下文格式。 ```javascript var canvas = document.getElementById('cnv'); var ctx = canvas.getContext('2d');//返回一个CanvasRenderingContext2D实例 ``` > `CanvasRenderingContext2D`接口是Canvas API的一部分,可为[`canvas`](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/canvas)元素的绘图表面提供2D渲染上下文。 它用于绘制形状,文本,图像和其他对象。 ### 三、Canvas坐标 ![image-20201216110957358](./images/image-20201216110957358.png) canvas 是一个二维网格(grid)。 canvas 的左上角(原点)坐标为 (0,0),所有绘制的元素的位置都相对于原点坐标。 ### 四、图形绘制 ``只支持两种形式的图形绘制:矩形和路径(由一系列点连接成的线段)。 其他复杂的图形都是通过一条或多条路径组合而成的。不过`CanvasRenderingContext2D`已经提供了很多复杂图形路径生成的方法。 #### 矩形 `CanvasRenderingContext2D`中有三个绘制矩形的方法: 1. `fillRect(x, y, width, height)`:绘制填充矩形的方法。当前渲染上下文中的`fillStyle` 属性决定了对这个矩形的填充样式。 2. `clearRect(x, y, width, height)`:通过把矩形区域内的像素设置为透明以达到擦除一个矩形的效果,如果该矩形绘制在空白的区域,则看不出效果。 3. `strokeRect(x, y, width, height)`:使用当前的绘画样式,描绘一个左上角在 *(x, y)* 、宽度为 *width* 、高度为 *height* 的矩形边框的方法。该方法直接绘制到画布而不修改当前路径,因此任何后续[`fill()`](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/fill) 或[`stroke()`](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/stroke)调用对它没有影响。 ![image-20201216142917015](./images/image-20201216142917015.png) ```html 基本图形的绘制 ``` #### 路径 图形的基本元素是路径。路径是通过不同**颜色**和**宽度**的线段或曲线相连形成的不同形状的点的集合。 > 注意:一个路径,甚至一个子路径(线段),都是**闭合**的。 使用路径绘制图形需要的步骤: 1. 首先创建路径起始点。 2. 然后使用[画图命令](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D#Paths)去画出路径。 3. 之后把路径封闭。 4. 一旦路径生成,就能通过描边(stroke)或填充(fill)路径区域来渲染图形。 `CanvasRenderingContext2D`中绘制路径相关的方法: - `beginPath()`:清空子路径列表,开始一个新的路径。 - `closePath()`:使画笔返回当前子路径的起始点(尝试从当前点到起始点绘制一条直线)。 - `moveTo(x,y)`:将一个新的子路径的起始点移动到(x,y)坐标。常用于绘制不连续的路径。 - `lineTo(x,y)`:从子路径的最后一个点向(x,y)坐标绘制一条直线。 - `arc(x, y, radius, startAngle, endAngle[,anticlockwise])`:绘制圆弧路径,圆心(x,y),半径radius,根据*anticlockwise*(默认为false顺时针)指定的方向从 *startAngle* 开始绘制,到 *endAngle* 结束。 - `arcTo(x1, y1, x2, y2, radius)`:根据当前描点与给定的控制点1(x1,y1)连接的直线,和控制点1与控制点2(x2,y2)连接的直线,作为使用指定半径的圆的**切线**,画出两条切线之间的弧线路径。 - `ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)`:添加椭圆的路径。 - `rect()`:创建一个矩形路径,矩形的起点位置是 *(x, y)* ,尺寸为 *width* 和 *height*。 - `fill()`:使用当前的样式填充子路径。 - `stroke()`:根据当前的画线样式,绘制当前或已经存在的路径的方法。即将路径列表中的路径绘制在画布上 - `clip()`:从当前路径创建一个剪切路径。后续绘制的内容只会显示裁剪路径内的图形。 - `isPointInPath(x, y)`:判断在当前路径中是否包含检测点,图形的内部或边上。 - `isPointInStroke()`:判断检测点是否在路径的描边线上。 本质上,路径是由很多子路径(线段)构成的,这些子路径都在一个列表中,包含所有的子路径(线`line`、弧形`arc`、矩形`rect`、椭圆`ellipse`等)构成的图形。使用上面的方法重新解释使用路径绘制图形的步骤: 1. `beginPath`清空路径列表,准备绘制图形 2. 使用线`line`、弧形`arc`、矩形`rect`、椭圆`ellipse`等方法添加路径,可以使用`clip`进行裁剪需要显示的内容。 3. 闭合路径`closePath()`,这不是必需的。 4. 最后通过`stroke()`方法使用线性样式将路径在画布中绘制出来,或使用`fill()`方法使用填充样式将路径填充渲染在画布上。 > **当您调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用`closePath()`函数。但是调用`stroke()`时不会自动闭合**。 ![image-20201216172336777](./images/image-20201216172336777.png) ```html 基本图形的绘制
x:
y:
``` #### Path2D对象 [`Path2D`](https://developer.mozilla.org/zh-CN/docs/Web/API/Path2D)对象已可以在较新版本的浏览器中使用,用来缓存或记录绘画命令(路径集合)。 ```javascript new Path2D(); // 空的Path对象 new Path2D(path); // 克隆Path对象 new Path2D(d); // 从SVG建立Path对象 ``` `Path2D`实例可以使用所有路径绘制方法,如`moveTo`、`rect`、`lineTo`、`arc`等。同时也可以使用`Path2D.addPath(path[,transform])`方法叠加路径,其中可以添加一个转换矩阵。 ```html Path2D路径缓存 ``` #### 绘制文本 `CanvasRenderingContext2D`中提供三个关于绘制和测量文本的方法: - `fillText(text,x,y[,maxWidth])`:在 *(x, y)*位置绘制填充文本text。 - `strokeText(text, x, y [, maxWidth])`:在 *(x, y)*位置对文本text进行描边。 - `measureText(text)`:返回一个关于被测量文本[`TextMetrics`](https://developer.mozilla.org/zh-CN/docs/Web/API/TextMetrics) 对象包含的信息(例如它的宽度)。 ![image-20201227174130269](./images/image-20201227174130269.png) ```javascript var cnv = document.getElementById("cnv"); var ctx = cnv.getContext("2d"); ctx.font = "48px serif"; ctx.strokeText("Hello world", 30, 50,200); ctx.moveTo(30,50); ctx.lineTo(230,50); ctx.stroke(); ctx.fillText("Learning Canvas",30,120,200); ctx.moveTo(30,120); ctx.lineTo(230,120); ctx.stroke(); ``` ### 五、样式与颜色 在绘制图形的时候,可以指定图形的样式,像填充样式`fillStyle`和边界轮廓样式 `strokeStyle`设置图形的颜色,或者使用`lineWidth`指定轮廓线的宽度等。 #### 颜色Color canvas中使用的Color必须是符合[`CSS3`颜色值标准](https://www.w3.org/TR/css-color-3/)的有效字符串。如: ```javascript ctx.fillStyle = "blue"; ctx.fillStyle = "#457b9d"; ctx.fillStyle = "rgb(69,123,157)"; ctx.fillStyle = "rgba(69,123,157,1)"; ``` #### 样式属性 `CanvasRenderingContext2D`中与图形样式相关的属性可以分为: 1. 填充和描边样式 2. 线型样式 3. 文本样式 4. 渐变和图案 5. 阴影 ##### 填充和描边样式 `fillStyle`:图形内部的颜色和样式,默认 `#000` (黑色)。 `strokeStyle`:图形轮廓边线的颜色和样式,默认`#000` (黑色)。 ![image-20210108151817844](./images/image-20210108151817844.png) ```javascript var cnv = document.getElementById("cnv"); var ctx = cnv.getContext("2d"); ctx.fillStyle = "#457b9d"; ctx.lineWidth = 20; ctx.fillRect(50,50,100,50); ctx.fill(); ctx.strokeRect(200,50,50,50); ctx.stroke(); ``` 可以看到`fillStyle`只对填充的图形才有效,对封闭的路径的图形是没有效果的。`strokeStyle`只对路径有效。 ##### 线型样式 `lineWidth`属性:线的宽度。***以路径为中心,两边各绘制一半线宽。***奇数值的线并不能精确呈现。 `lineJoin`属性:线末端的类型,可选值:`round`、`bevel`、`miter`。 ![image-20210108165146464](./images/image-20210108165146464.png) > `round`折线的边角是圆角,圆的半径是线宽。当是`miter`时,线段会在连接处向外延伸直至交予一点,延伸效果会受到`miterLimit`属性约束(斜接限定值)。`miterLimit` 属性是用来设定外延交点与连接点的最大距离,如果交点距离大于此值,连接效果会变成了` bevel`。 `lineCap`属性:两条线相交的拐点的样式类型。`butt`,`round` 和 `square`。默认是 `butt`。 ![image-20210108171614811](./images/image-20210108171614811.png) > `butt`的端点出没有处理,是平直的效果;`round` 的端点加上了一个以线宽一半为半径的半圆;`square`的端点是加上一半线宽的方块。 `lineDashOffset`属性和`setLineDash()`方法:绘制虚线。 `lineDashOffset`属性用于设置起始的偏移量,`setLineDash()`方法接收一个用来描述交替绘制线段和间距(坐标空间单位)长度的数字值型数组。如果数组元素的数量是奇数, 数组的元素会被复制并重复。例如, `[5, 15, 25]` 会变成 `[5, 15, 25, 5, 15, 25]。`[绘制,不绘制,绘制,不绘制,...]。如果数组为空,则是实线模式。 ```javascript let y = 15; function drawDashedLine(pattern) { ctx.beginPath(); ctx.setLineDash(pattern); ctx.moveTo(0, y); ctx.lineTo(300, y); ctx.stroke(); y += 20; } drawDashedLine([]); //实线 drawDashedLine([1, 1]); drawDashedLine([10, 10]); drawDashedLine([20, 5]); drawDashedLine([15, 3, 3, 3]); drawDashedLine([20, 3, 3, 3, 3, 3, 3, 3]); drawDashedLine([12, 3, 3]); // 等于 [12, 3, 3, 12, 3, 3][绘制,不绘制,绘制,不绘制,...] ``` ![image-20210108175447524](./images/image-20210108175447524.png) ##### 文本样式 #### 渐变 Gradients `CanvasGradient` 接口表示描述渐变的不透明对象。通过以下两个方法得到: - `CanvasRenderingContext2D.createLinearGradient(x0, y0, x1, y1)` - `CanvasRenderingContext2D.createRadialGradient(x0, y0, r0, x1, y1, r1)` 接口方法: - addColorStop(offset, color):添加一个由范围在0到1的`偏移(offset)`和颜色(`color`)定义的断点到渐变中。 示例: ![image-20210430103423811](./images/image-20210103423811.png) ```javascript var gradient = ctx.createLinearGradient(0,0,200,0); gradient.addColorStop(0,"#457b9d"); gradient.addColorStop(1,"white"); ctx.fillStyle = gradient; ctx.fillRect(10,10,200,100); ``` ![image-20210430104236427](./images/image-20210430104236427.png) ```javascript //据参数确定两个圆的坐标,绘制放射性渐变的方法。 var gradient = ctx.createRadialGradient(100,100,100,100,100,0); gradient.addColorStop(0,"white"); gradient.addColorStop(1,"green"); ctx.fillStyle = gradient; ctx.fillRect(0,0,200,200); ``` ![image-20210430112057665](./images/image-20210430112057665.png) ```javascript var x1=45,y1=45,r1=10,x2=52,y2=50,r2=30; // 创建渐变 var radgrad = ctx.createRadialGradient(x1,y1,r1,x2,y2,r2); radgrad.addColorStop(0, '#A7D30C'); radgrad.addColorStop(0.9, '#019F62'); radgrad.addColorStop(1, 'rgba(1,159,98,0)'); ctx.fillStyle = radgrad; ctx.fillRect(0,0,150,150); var circle1 = new Path2D(); circle1.moveTo(x1+r1,y1); circle1.arc(x1,y1,r1,0,2*Math.PI,false); ctx.stroke(circle1); var circle2 = new Path2D(); circle2.moveTo(x2+r2,y2); circle2.arc(x2,y2,r2,0,2*Math.PI,false); ctx.stroke(circle2); ``` #### 填充规则 在使用`fill`、`clip`、`isPointinPath`方法时,可以选择填充规则,确定某处在路径的外面还是里面来进行填充: - `nonzero`:非零缠绕规则(默认值) - `evenodd`:奇偶规则 ![image-20210430113137386](./images/image-20210430113137386.png) 根据两个规则填充曲线(顶部):[奇偶规则](https://en.wikipedia.org/wiki/Even-odd_rule)(左)和**非零缠绕规则**(右)。 ### 参考文章 [1] [Canvas教程](https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial) `https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial` [2] [CanvasRenderingContext2D](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D)`https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D`