445 lines
16 KiB
Markdown
445 lines
16 KiB
Markdown
|
---
|
|||
|
title: Canvas API 速食
|
|||
|
date: 2020-12-16
|
|||
|
author: ac
|
|||
|
tags:
|
|||
|
- Canvas
|
|||
|
- JavaScript
|
|||
|
categories:
|
|||
|
- 可视化
|
|||
|
---
|
|||
|
|
|||
|
> Canvas API接口是使用`<canvas>`标签,通过`JavaScript`来绘制图形,用于动画、数据可视化、图片编辑、游戏画面以及实时视频等功能的Web API接口。
|
|||
|
|
|||
|
<!-- more -->
|
|||
|
|
|||
|
`<canvas>`标签是`H5`中新添加的元素。可以将`<canvas>`标签看作图形的容器(画布),在上面绘制路径、圆、盒子、字符等。
|
|||
|
|
|||
|
### 一、创建画布
|
|||
|
|
|||
|
```html
|
|||
|
<canvas id='cnv' width='500' height='300'></canvas>
|
|||
|
```
|
|||
|
|
|||
|
`<canvas>`标签实际上只有两个属性`width`和`height`(`id`属性是每个HTML元素都有的默认属性),但这是可选的,如果不指定,则`width`默认为300像素,`heigth`默认为150像素。除了在标签内的属性中定义大小外,也可以使用`CSS`样式定义。
|
|||
|
|
|||
|
> 如果`CSS`样式是以响应式或百分比的形式定义的大小,当`<canvas>`元素的尺寸与原始尺寸不等比例伸缩时,画布内已绘制的图像会出现变形。
|
|||
|
|
|||
|
被创建的`<canvas>`画布,在没有设置样式前,默认是完全透明的。
|
|||
|
|
|||
|
|
|||
|
|
|||
|
### 二、渲染上下文
|
|||
|
|
|||
|
`<canvas>`元素创建来一个固定大小的画布,它公开了一个或多个**渲染上下文**,用来绘制和处理要展示的内容。
|
|||
|
|
|||
|
**渲染上下文**的种类有很多,但Canvas API主要聚焦于2D图形,所以这里将注意力放在2D渲染上下文中。另外像同样是使用`<canvas>`元素的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坐标
|
|||
|
|
|||
|

|
|||
|
|
|||
|
canvas 是一个二维网格(grid)。
|
|||
|
|
|||
|
canvas 的左上角(原点)坐标为 (0,0),所有绘制的元素的位置都相对于原点坐标。
|
|||
|
|
|||
|
|
|||
|
|
|||
|
### 四、图形绘制
|
|||
|
|
|||
|
`<canvas>`只支持两种形式的图形绘制:矩形和路径(由一系列点连接成的线段)。
|
|||
|
|
|||
|
其他复杂的图形都是通过一条或多条路径组合而成的。不过`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)调用对它没有影响。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
```html
|
|||
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="utf-8"/>
|
|||
|
<title>基本图形的绘制</title>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<canvas id="cnv"></canvas>
|
|||
|
<script>
|
|||
|
var cnv = document.getElementById("cnv");
|
|||
|
var ctx = cnv.getContext("2d");
|
|||
|
ctx.fillStyle = '#457b9d';//默认#000000
|
|||
|
ctx.fillRect(5,5,100,100);
|
|||
|
ctx.clearRect(30,30,50,50);
|
|||
|
ctx.strokeRect(120,5,50,50);
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</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()`时不会自动闭合**。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
```html
|
|||
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="utf-8"/>
|
|||
|
<title>基本图形的绘制</title>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<canvas id="cnv"></canvas>
|
|||
|
<div>x:<span id="x"></span></div>
|
|||
|
<div>y:<span id="y"></span></div>
|
|||
|
<script>
|
|||
|
var cnv = document.getElementById("cnv");
|
|||
|
var ctx = cnv.getContext("2d");
|
|||
|
ctx.fillStyle = '#457b9d';//默认#000000
|
|||
|
|
|||
|
ctx.beginPath();
|
|||
|
//---------------三角形---------------
|
|||
|
//当前路径为空,即调用beginPath()之后,或者canvas刚建的时候,
|
|||
|
//第一条路径构造命令通常被视为是moveTo(),无论实际上是什么。
|
|||
|
//ctx.lineTo(75, 50);
|
|||
|
//ctx.lineTo(100, 75);
|
|||
|
//ctx.lineTo(100, 25);
|
|||
|
//ctx.fill();
|
|||
|
|
|||
|
//---------------笑脸---------------
|
|||
|
ctx.arc(150,75,50,0,Math.PI*2,true);
|
|||
|
ctx.moveTo(190,75);
|
|||
|
ctx.arc(150,75,40,0,Math.PI,false);
|
|||
|
ctx.moveTo(140,60);
|
|||
|
ctx.arc(135,60,5,0,Math.PI*2,false);
|
|||
|
ctx.moveTo(175,60);
|
|||
|
ctx.arc(170,60,5,0,Math.PI*2,true);
|
|||
|
|
|||
|
ctx.stroke();
|
|||
|
|
|||
|
document.onmousemove = function(e){
|
|||
|
document.getElementById('x').innerText = e.x;
|
|||
|
document.getElementById('y').innerText = e.y;
|
|||
|
//console.log(e);
|
|||
|
}
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#### 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
|
|||
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="utf-8"/>
|
|||
|
<title>Path2D路径缓存</title>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<canvas id="cnv"></canvas>
|
|||
|
<script>
|
|||
|
var cnv = document.getElementById("cnv");
|
|||
|
var ctx = cnv.getContext("2d");
|
|||
|
ctx.fillStyle = '#457b9d';//默认#000000
|
|||
|
ctx.beginPath();
|
|||
|
//---------------Path---------------
|
|||
|
var rectangle = new Path2D();
|
|||
|
rectangle.rect(10,10,50,50);
|
|||
|
|
|||
|
var circle = new Path2D();
|
|||
|
circle.moveTo(125,35);
|
|||
|
circle.arc(100,35,25,0,Math.PI*2);
|
|||
|
|
|||
|
ctx.stroke(circle);
|
|||
|
ctx.fill(rectangle);
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#### 绘制文本
|
|||
|
|
|||
|
`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) 对象包含的信息(例如它的宽度)。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
```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` (黑色)。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
```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`。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
> `round`折线的边角是圆角,圆的半径是线宽。当是`miter`时,线段会在连接处向外延伸直至交予一点,延伸效果会受到`miterLimit`属性约束(斜接限定值)。`miterLimit` 属性是用来设定外延交点与连接点的最大距离,如果交点距离大于此值,连接效果会变成了` bevel`。
|
|||
|
|
|||
|
`lineCap`属性:两条线相交的拐点的样式类型。`butt`,`round` 和 `square`。默认是 `butt`。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
> `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][绘制,不绘制,绘制,不绘制,...]
|
|||
|
```
|
|||
|
|
|||
|

|
|||
|
|
|||
|
##### 文本样式
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#### 渐变 Gradients
|
|||
|
|
|||
|
`CanvasGradient` 接口表示描述渐变的不透明对象。通过以下两个方法得到:
|
|||
|
|
|||
|
- `CanvasRenderingContext2D.createLinearGradient(x0, y0, x1, y1)`
|
|||
|
- `CanvasRenderingContext2D.createRadialGradient(x0, y0, r0, x1, y1, r1)`
|
|||
|
|
|||
|
接口方法:
|
|||
|
|
|||
|
- addColorStop(offset, color):添加一个由范围在0到1的`偏移(offset)`和颜色(`color`)定义的断点到渐变中。
|
|||
|
|
|||
|
示例:
|
|||
|
|
|||
|

|
|||
|
|
|||
|
```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);
|
|||
|
```
|
|||
|
|
|||
|

|
|||
|
|
|||
|
```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);
|
|||
|
```
|
|||
|
|
|||
|

|
|||
|
|
|||
|
```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`:奇偶规则
|
|||
|
|
|||
|

|
|||
|
|
|||
|
根据两个规则填充曲线(顶部):[奇偶规则](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`
|