16 KiB
title | date | author | tags | categories | |||
---|---|---|---|---|---|---|---|
Canvas API 速食 | 2020-12-16 | ac |
|
|
Canvas API接口是使用
<canvas>
标签,通过JavaScript
来绘制图形,用于动画、数据可视化、图片编辑、游戏画面以及实时视频等功能的Web API接口。
<canvas>
标签是H5
中新添加的元素。可以将<canvas>
标签看作图形的容器(画布),在上面绘制路径、圆、盒子、字符等。
一、创建画布
<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()
方法中添加一个参数,来指定上下文格式。
var canvas = document.getElementById('cnv');
var ctx = canvas.getContext('2d');//返回一个CanvasRenderingContext2D实例
CanvasRenderingContext2D
接口是Canvas API的一部分,可为canvas
元素的绘图表面提供2D渲染上下文。 它用于绘制形状,文本,图像和其他对象。
三、Canvas坐标
canvas 是一个二维网格(grid)。
canvas 的左上角(原点)坐标为 (0,0),所有绘制的元素的位置都相对于原点坐标。
四、图形绘制
<canvas>
只支持两种形式的图形绘制:矩形和路径(由一系列点连接成的线段)。
其他复杂的图形都是通过一条或多条路径组合而成的。不过CanvasRenderingContext2D
已经提供了很多复杂图形路径生成的方法。
矩形
CanvasRenderingContext2D
中有三个绘制矩形的方法:
fillRect(x, y, width, height)
:绘制填充矩形的方法。当前渲染上下文中的fillStyle
属性决定了对这个矩形的填充样式。clearRect(x, y, width, height)
:通过把矩形区域内的像素设置为透明以达到擦除一个矩形的效果,如果该矩形绘制在空白的区域,则看不出效果。strokeRect(x, y, width, height)
:使用当前的绘画样式,描绘一个左上角在 (x, y) 、宽度为 width 、高度为 height 的矩形边框的方法。该方法直接绘制到画布而不修改当前路径,因此任何后续fill()
或stroke()
调用对它没有影响。
<!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>
路径
图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。
注意:一个路径,甚至一个子路径(线段),都是闭合的。
使用路径绘制图形需要的步骤:
- 首先创建路径起始点。
- 然后使用画图命令去画出路径。
- 之后把路径封闭。
- 一旦路径生成,就能通过描边(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
等)构成的图形。使用上面的方法重新解释使用路径绘制图形的步骤:
beginPath
清空路径列表,准备绘制图形- 使用线
line
、弧形arc
、矩形rect
、椭圆ellipse
等方法添加路径,可以使用clip
进行裁剪需要显示的内容。 - 闭合路径
closePath()
,这不是必需的。 - 最后通过
stroke()
方法使用线性样式将路径在画布中绘制出来,或使用fill()
方法使用填充样式将路径填充渲染在画布上。
当您调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用
closePath()
函数。但是调用stroke()
时不会自动闭合。
<!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
对象已可以在较新版本的浏览器中使用,用来缓存或记录绘画命令(路径集合)。
new Path2D(); // 空的Path对象
new Path2D(path); // 克隆Path对象
new Path2D(d); // 从SVG建立Path对象
Path2D
实例可以使用所有路径绘制方法,如moveTo
、rect
、lineTo
、arc
等。同时也可以使用Path2D.addPath(path[,transform])
方法叠加路径,其中可以添加一个转换矩阵。
<!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
对象包含的信息(例如它的宽度)。
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
颜色值标准的有效字符串。如:
ctx.fillStyle = "blue";
ctx.fillStyle = "#457b9d";
ctx.fillStyle = "rgb(69,123,157)";
ctx.fillStyle = "rgba(69,123,157,1)";
样式属性
CanvasRenderingContext2D
中与图形样式相关的属性可以分为:
- 填充和描边样式
- 线型样式
- 文本样式
- 渐变和图案
- 阴影
填充和描边样式
fillStyle
:图形内部的颜色和样式,默认 #000
(黑色)。
strokeStyle
:图形轮廓边线的颜色和样式,默认#000
(黑色)。
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]。
[绘制,不绘制,绘制,不绘制,...]。如果数组为空,则是实线模式。
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
)定义的断点到渐变中。
示例:
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);
//据参数确定两个圆的坐标,绘制放射性渐变的方法。
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);
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
:奇偶规则
根据两个规则填充曲线(顶部):奇偶规则(左)和非零缠绕规则(右)。
参考文章
[1] Canvas教程 https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial
[2] CanvasRenderingContext2Dhttps://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D