1.基本介绍
Canvas是一个内置的 HTML5 API,用于构建形状并将其有效地绘制到屏幕上。以下定义来自MDN 网络文档:Canvas 允许通过 Javascript 和 HTML元素 绘制图形。 我们可以将其用于动画、游戏图形、数据可视化、照片处理和实时视频处理。
2.基本用法
2.1 canvas画线段
移动画笔(moveTo())
之前我们获得了画笔 context
,所以以此为例,给出该方法的使用实例——context.moveTo(100,0)
。这句代码的意思是 移动画笔至(100,100)这个点(单位是px) 。记住,这里是以 canvas
画布的左上角为笛卡尔坐标系的原点,且y轴的正方向向下,x轴的正方向向右。
笔画停点(lineTo())
同理,context.lineTo(600,600)
。这句的意思是从 上一笔的停止点 绘制到(500,500)这里。不过要清楚,这里的moveTo()
lineTo()
都只是状态而已,是规划,是我准备要画,还没有开始画,只是一个计划而已!
选择画笔
这里我们暂且只设置一下画笔的颜色和粗细。
context.lineWidth = 5
,这句话的意思是设置画笔(线条)的粗细为 5 px。
context.strokeStyle = "red"
,这句话的意思是设置画笔(线条)的颜色为红色。
确定绘制
确定绘制只有两种方法,fill()
和stroke()
,有点绘画基础的应该知道,前者是指填充,后者是指描边。因为我们只是绘制线条,所以只要描边就可以了。调用代码 context.stroke()
即可。
//1.构建画布
const canvas = document.getElementById("canvas");
canvas.width = 800;
canvas.height = 600;
const context = canvas.getContext("2d");
//2.移动画笔,使画笔移动至绘画的开始处
context.moveTo(100, 0);
//3.确定第一笔的停止点
context.lineTo(500, 500);
//4.确定好画笔的颜色和线条粗细
context.lineWidth = 5;
context.strokeStyle = "red";
//5.确定绘制
context.stroke();
线条属性
1、lineCap属性
lineCap 定义上下文中线的端点,可以有以下 3 个值。
- butt:默认值,端点是垂直于线段边缘的平直边缘。
- round:端点是在线段边缘处以线宽为直径的半圆。
- square:端点是在选段边缘处以线宽为长、以一半线宽为宽的矩形。
const canvas = document.getElementById("canvas");
canvas.width = 800;
canvas.height = 600;
const context = canvas.getContext("2d");
context.beginPath();
context.moveTo(100, 100);
context.lineTo(700, 100);
context.lineWidth = 50;
context.strokeStyle = "gold";
context.lineCap = "butt";
context.stroke();
context.beginPath();
context.moveTo(100, 300);
context.lineTo(700, 300);
context.lineWidth = 50;
context.strokeStyle = "red";
context.lineCap = "round";
context.stroke();
context.beginPath();
context.moveTo(100, 500);
context.lineTo(700, 500);
context.lineWidth = 50;
context.strokeStyle = "green";
context.lineCap = "square";
context.stroke();
2、lineJoin属性
lineJoin 定义两条线相交产生的拐角,可将其称为连接。在连接处创建一个填充三角形,可以使用 lineJoin 设置它的基本属性。
- miter:默认值,在连接处边缘延长相接。miterLimit 是角长和线宽所允许的最大比例(默认是 10)。
- bevel:连接处是一个对角线斜角。
- round:连接处是一个圆。
注意:miterLimit
规定了一个自动填充连接点的极限值。如果超过了这个值,会导致lineJoin
属性失效,会从 miter 变成 bevel。可以看出来,这个值和线宽以及夹角有关
const canvas = document.getElementById("canvas1");
canvas.width = 800;
canvas.height = 600;
const context = canvas.getContext("2d");
context.beginPath();
context.moveTo(100, 100);
context.lineTo(300, 300);
context.lineTo(100, 500);
context.lineWidth = 20;
context.strokeStyle = "gold";
//方角连接
context.lineJoin = "miter";
context.stroke();
context.beginPath();
context.moveTo(300, 100);
context.lineTo(500, 300);
context.lineTo(300, 500);
context.lineWidth = 20;
context.strokeStyle = "red";
//断角连接
context.lineJoin = "bevel";
context.stroke();
context.beginPath();
context.moveTo(500, 100);
context.lineTo(700, 300);
context.lineTo(500, 500);
//圆角连接
context.lineJoin = "round";
context.lineWidth = 20;
context.strokeStyle = "green";
context.lineCap = "square";
context.stroke();
3、线宽
lineWidth 定义线的宽度(默认值为 1.0)。
2.2 画不同类型的线段
如何描绘?
如果需要画三个不同的线段,很多人想是不是就是将代码复制,其实不然,实际上Canvas是基于状态的绘制,每次使用stroke()
时,它都会把之前设置的状态再绘制一遍。因此在绘制之前应该加上context.beginPath()
。
const canvas = document.getElementById("canvas");
canvas.width = 800;
canvas.height = 600;
const context = canvas.getContext("2d");
//1.重新绘制
context.beginPath();
//2.移动画笔,使画笔移动至绘画的开始处
context.moveTo(100, 0);
//3.确定第一笔的停止点
context.lineTo(500, 500);
//4.确定好画笔的颜色和线条粗细
context.lineWidth = 5;
context.strokeStyle = "red";
//5.确定绘制
context.stroke();
//1.重新绘制
context.beginPath();
//2.移动画笔,使画笔移动至绘画的开始处
context.moveTo(20, 0);
//3.确定第一笔的停止点
context.lineTo(300, 300);
//4.确定好画笔的颜色和线条粗细
context.lineWidth = 5;
context.strokeStyle = "blue";
//5.确定绘制
context.stroke();
2.3 画矩形
如果使用上面画线段的方式,只要确定四个点就可以画矩形了,但事实上如果这样画出的矩形会没有闭合,因此在绘制之前应该加上context.closePath()
用来补全图形,这里还可以使用油漆桶给我们所画的图形上色,使用 context.fillStyle = "green"
给油漆桶设置一个颜色,context.fill();
进行填充。
const canvas = document.getElementById("canvas");
canvas.width = 800;
canvas.height = 600;
const context = canvas.getContext("2d");
//1.开始绘制
context.beginPath();
//2.从坐标100,100开始绘制
context.moveTo(100, 100);
context.lineTo(300, 100);
context.lineTo(300, 300);
context.lineTo(100, 300);
context.lineTo(100, 100);
//3.补全图形
context.closePath()
//4.选择油漆桶颜色
context.fillStyle = "green"
//5.确定图形线条宽度以及颜色
context.lineWidth=3;
context.strokeStyle='blue';
//6.确定填充
context.fill();
//7.开始绘制
context.stroke()
2.3使用rect绘制多个矩形
由于绘制矩形比较常见,因此在 canvas中已经封装了rect这个api用来绘制矩形context.rect(x,y,width,height)
const canvas = document.getElementById("canvas");
canvas.width = 800;
canvas.height = 600;
const context = canvas.getContext("2d");
let rectArr = [
{
x: 150,
y: 50,
width: 50,
height: 50,
fillColor: "blue",
borderColor: "gray",
borderWidth: 2,
},
{
x: 250,
y: 50,
width: 50,
height: 50,
fillColor: "yellow",
borderColor: "gray",
borderWidth: 2,
},
{
x: 350,
y: 50,
width: 50,
height: 50,
fillColor: "red",
borderColor: "gray",
borderWidth: 2,
},
{
x: 450,
y: 50,
width: 50,
height: 50,
fillColor: "pink",
borderColor: "gray",
borderWidth: 2,
},
{
x: 150,
y: 150,
width: 50,
height: 50,
fillColor: "orange",
borderColor: "gray",
borderWidth: 2,
},
{
x: 250,
y: 150,
width: 50,
height: 50,
fillColor: "green",
borderColor: "gray",
borderWidth: 2,
},
];
rectArr.forEach((item) => {
drawRect(
context,
item.x,
item.y,
item.width,
item.height,
item.fillColor,
item.borderWidth,
item.borderColor
);
});
function drawRect(
context,
x,
y,
width,
height,
fillColor,
borderWidth,
borderColor
) {
//1.开始绘制
context.beginPath();
//2.从坐标100,100开始绘制
// context.moveTo(x, y);
// context.lineTo(x + width, y);
// context.lineTo(x + width, y + height);
// context.lineTo(x, y + height);
// context.lineTo(x, y);
// //3.补全图形
// context.closePath();
//上面注释2,3与下面的代码同样的效果
context.rect(x, y, width, height);
//4.选择油漆桶颜色
context.fillStyle = fillColor;
//5.确定图形线条宽度以及颜色
context.lineWidth = borderWidth;
context.strokeStyle = borderColor;
//6.确定填充
context.fill();
//7.开始绘制
context.stroke();
}
绘制案例
<div id="canvas-warp">
<canvas
id="canvas"
style="border: 1px solid #aaaaaa; display: block; margin: 50px auto"
>
你的浏览器居然不支持Canvas?!赶快换一个吧!!
</canvas>
</div>
<script>
const canvas = document.getElementById("canvas");
canvas.width = 800;
canvas.height = 600;
const context = canvas.getContext("2d");
context.beginPath();
context.rect(0, 0, 800, 600);
context.fillStyle = "yellow";
context.fill();
context.beginPath();
for (var i = 0; i < 20; i++) {
drawWhiteRect(
context,
200 + 10 * i,
100 + 10 * i,
400 - 20 * i,
400 - 20 * i
);
drawBlackRect(
context,
205 + 10 * i,
105 + 10 * i,
390 - 20 * i,
390 - 20 * i
);
}
context.beginPath();
context.rect(395, 295, 5, 5);
context.fillStyle = "orange";
context.lineWidth = 5;
context.fill();
context.stroke();
function drawBlackRect(cxt, x, y, width, height) {
cxt.beginPath();
cxt.rect(x, y, width, height);
cxt.lineWidth = 5;
cxt.strokeStyle = "gold";
cxt.stroke();
}
function drawWhiteRect(cxt, x, y, width, height) {
cxt.beginPath();
cxt.rect(x, y, width, height);
cxt.lineWidth = 5;
cxt.strokeStyle = "white";
cxt.stroke();
}
绘制图形
2.4 绘制三角形
const canvas4 = document.getElementById('canvas4');
const ctx4 = canvas4.getContext('2d');
ctx4.beginPath();
ctx4.fillStyle = '#ff6';
ctx4.fillRect(0, 0, canvas.width, canvas.height);
ctx4.beginPath()
//起始绘制点
ctx4.moveTo(100, 50)
//第二个点
ctx4.lineTo(150, 50);
//第三个点
ctx4.lineTo(100, 100)
//填充颜色
ctx4.fillStyle = 'red';
//绘图
ctx4.fill()
3.深入用法
3.1 填充渐变颜色
渐变颜色分为线性渐变和径向渐变,线性渐变是基于两个端点定义的,但是径向渐变是基于两个圆定义的。
线性渐变
-
添加渐变线:
const grd = context.createLinearGradient(xstart,ystart,xend,yend);
-
为渐变线添加关键色(类似于颜色断点):
grd.addColorStop(stop,color);
-
应用渐变
const canvas = document.getElementById("canvasBox");
canvas.width = 800;
canvas.height = 600;
const context = canvas.getContext("2d");
context.rect(200,100,400,400);
//添加渐变线
//context.createLinearGradient(xstart,ystart,xend,yend);
var grd = context.createLinearGradient(200,300,600,300);
//添加颜色断点
grd.addColorStop(0,"pink");
grd.addColorStop(0.5,"red");
grd.addColorStop(1,"pink");
//应用渐变
context.fillStyle = grd;
context.fill();
3.2 填充图案
纹理其实就是图案的重复,填充图案通过createPattern()
函数进行初始化。它需要传进两个参数createPattern(img,repeat-style)
,第一个是Image对象实例,第二个参数是String类型,表示在形状中如何显示repeat图案。可以使用这个函数加载图像或者整个画布作为形状的填充图案。
有以下4种图像填充类型:
- 平面上重复:repeat;
- x轴上重复:repeat-x;
- y轴上重复:repeat-y;
- 不使用重复:no-repeat;
const canvas = document.getElementById("canvas");
canvas.width = 500;
canvas.height = 500;
const context = canvas.getContext("2d");
const img = new Image(10, 10);
img.src = "./img/flower.png";
img.onload = function () {
//重置一个画布用来改变图片大小
let tempCanvas = document.createElement("canvas");
tempCanvas.width = 10;
tempCanvas.height = 10;
let tempCtx = tempCanvas.getContext("2d");
tempCtx.drawImage(img, 0, 0, this.width, this.height);
let pattern = context.createPattern(tempCanvas, "repeat");
// 创建一个绘制背景图的模式
context.fillStyle = pattern;
context.fillRect(0, 0, 500, 500);
};
3.3圆弧
canvas画圆弧主要有以下几种方式:
- 标准圆弧:
arc()
- 复杂圆弧:
arcTo()
- 二次贝塞尔曲线:
quadraticCurveTo()
- 三次贝塞尔曲线:
bezierCurveTo()
标准圆
var canvas = document.getElementById("canvas");
canvas.width = "500";
canvas.height = "600";
var context = canvas.getContext("2d");
//圆
context.beginPath();
context.fillStyle = "gold";
context.strokeStyle = "gold";
context.arc(100, 100, 50, 0, 2 * Math.PI);
context.stroke();
context.fill();
//半圆
context.beginPath();
context.fillStyle = "pink";
context.strokeStyle = "pink";
context.arc(100, 250, 50, Math.PI, 2 * Math.PI);
context.stroke();
context.fill();
圆角矩形
画圆角矩形主要由圆弧和线段组成
var canvas = document.getElementById("canvas");
canvas.width = 800;
canvas.height = 600;
var context = canvas.getContext("2d");
context.fillRect(0, 0, 800, 600);
drawRoundRect(context, 100, 100, 400, 400, 50);
context.strokeStyle = "green";
context.fillStyle = "pink";
context.stroke();
context.fill();
//画圆角矩形
function drawRoundRect(cxt, x, y, width, height, radius){
cxt.beginPath();
//画弧线
cxt.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2);
//画线段
cxt.lineTo(width - radius + x, y);
cxt.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2);
cxt.lineTo(width + x, height + y - radius);
cxt.arc(width - radius + x, height - radius + y, radius, 0, Math.PI * 1 / 2);
cxt.lineTo(radius + x, height +y);
cxt.arc(radius + x, height - radius + y, radius, Math.PI * 1 / 2, Math.PI);
cxt.closePath();
}
使用切点画弧
使用arcTo(x1,y1,x2,y2,radius)
var canvas = document.getElementById("canvasBox");
canvas.width = 600;
canvas.height = 600;
var context = canvas.getContext("2d");
context.fillStyle = "#FFF";
context.fillRect(0, 0, 600, 600);
drawArcTo(context, 500, 200, 600, 200, 600, 400, 100);
function drawArcTo(context, x0, y0, x1, y1, x2, y2, r) {
context.beginPath();
context.moveTo(x0, y0);
context.arcTo(x1, y1, x2, y2, r);
context.lineWidth = 6;
context.strokeStyle = "red";
context.stroke();
context.beginPath();
context.moveTo(x0, y0);
context.lineTo(x1, y1);
context.lineTo(x2, y2);
context.lineWidth = 1;
context.strokeStyle = "#0088AA";
context.stroke();
}
3.4 微信聊天框
聊天框可由一个三角加上一个圆角矩形组成
聊天框
//绘制聊天框
function drawChatRect(cxt, type, x, y, width, height, radius, strokeStyle, fillStyle, num) {
cxt.beginPath();
cxt.strokeStyle = strokeStyle;
cxt.fillStyle = fillStyle;
//画弧线
cxt.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2);
//画线段
cxt.lineTo(width - radius + x, y);
cxt.arc(
width - radius + x,
radius + y,
radius,
(Math.PI * 3) / 2,
Math.PI * 2
);
if (type === "forward") {
cxt.lineTo(width + x, (height + y - radius) / 2 + y / 2);
cxt.lineTo(width + x + num, ((height + y - radius) / 2 + y / 2) + num);
cxt.lineTo(width + x, (height + y - radius) / 2 + y / 2 + num * 2);
cxt.lineTo(width + x, height + y - radius);
cxt.arc(
width - radius + x,
height - radius + y,
radius,
0,
(Math.PI * 1) / 2
);
cxt.lineTo(radius + x, height + y);
cxt.arc(
radius + x,
height - radius + y,
radius,
(Math.PI * 1) / 2,
Math.PI
);
} else {
cxt.lineTo(width + x, height + y - radius);
cxt.arc(
width - radius + x,
height - radius + y,
radius,
0,
(Math.PI * 1) / 2
);
cxt.lineTo(radius + x, height + y);
cxt.arc(
radius + x,
height - radius + y,
radius,
(Math.PI * 1) / 2,
Math.PI
);
cxt.lineTo(radius + x - num, height - radius + y - num * 2);
cxt.lineTo(radius + x - num * 2, height - radius + y - num * 3);
cxt.lineTo(radius + x - num, height - radius + y - num * 4);
}
cxt.closePath();
context.fill();
context.stroke();
}
文字显示与图片绘制
//绘制图片
function drawImg(img, x, y, width, height, url) {
context.beginPath();
img.onload = function () {
context.drawImage(img, x, y, width, height); //绘制图片
};
img.src = url; // 设置图片源地址
context.closePath();
}
//绘制文字
function drawText(fontSize, color, content, x, y) {
context.font = fontSize;
context.fillStyle = color;
context.fillText(content, x, y);
context.stroke();
}
右上角头像显示
这里其实是一个圆形加上一个闭合的半圆
function drawCircle(){
//绘制头部
context.beginPath();
//2.从坐标100,100开始绘制
context.moveTo(0, 0);
context.lineTo(300, 0);
context.lineTo(300, 40);
context.lineTo(0, 40);
//3.补全图形
context.closePath();
//4.选择油漆桶颜色
context.fillStyle = "rgb(44,44,46)";
//5.确定图形线条宽度以及颜色
context.lineWidth = 1;
context.strokeStyle = "rgb(44,44,46)";
//6.确定填充
context.fill();
//7.开始绘制
context.stroke();
}
//绘制封闭半圆
function drawSemicircle(){
context.beginPath();
context.moveTo(20, 10);
context.lineTo(10, 20);
context.lineTo(20, 30);
context.lineJoin = "miter";
context.miterLimit = 10;
context.lineWidth = 2;
context.strokeStyle = "#fff";
context.stroke();
}
3.5二次贝塞尔曲线
Bézier curve(贝塞尔曲线)是应用于二维图形应用程序的数学曲线。 曲线定义:起始点、终止点、控制点。通过调整控制点,贝塞尔曲线的形状会发生变化。 1962年,法国数学家Pierre Bézier第一个研究了这种矢量绘制曲线的方法,并给出了详细的计算公式,因此按照这样的公式绘制出来的曲线就用他的姓氏来命名,称为贝塞尔曲线。
贝塞尔曲线是一条由起始点、终止点和控制点所确定的曲线就行了。而n阶贝塞尔曲线就有n-1个控制点。用过Photoshop等绘图软件的同学应该比较熟悉,因为其中的钢笔工具设置锚点绘制路径的时候,用到的就是贝塞尔曲线。下面给一个绘制网址:blogs.sitepointstatic.com/examples/te…
使用canvas进行绘制时使用它的一个api:context.quadraticCurveTo(cpx,cpy,x,y);
const canvas = document.getElementById("canvasBox");
ctx = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 500;
ctx.lineWidth = 2;
ctx.strokeStyle = "gold";
ctx.beginPath();
ctx.moveTo(100, 250);
ctx.quadraticCurveTo(53, 13, 400, 250);
ctx.closePath();
ctx.fillStyle = "gold";
ctx.fill()
ctx.stroke();
3.6 三次贝塞尔曲线
如果我们想画波浪线那么三次贝塞尔曲线就可以充分的满足需求,他使用的是canvas中的api: context.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y);
传入的6个参数分别为控制点cp1 (cp1x, cp1y),控制点cp2 (cp2x, cp2y),与终止点 (x, y)。
下面一个绘制网页:blogs.sitepointstatic.com/examples/te…
const canvas = document.getElementById("canvasBox");
ctx = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 500;
ctx.lineWidth = 2;
ctx.strokeStyle = "green";
ctx.beginPath();
ctx.moveTo(100, 250);
ctx.bezierCurveTo(230, 51, 275, 438, 400, 250);
ctx.closePath();
ctx.fillStyle = "green";
ctx.fill()
ctx.stroke();
3.8 动画
平移
平移是从一个点到一个点,比如将位于(100,100)的矩形平移至(200,200)点。那么我只要在绘制矩形之前加上context.translate(100,100)
就可以实现了。canvas的平移是以左上角的点为初始点进行位移。
var canvas = document.getElementById("canvasBox");
canvas.width = 600;
canvas.height = 600;
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.fillStyle = "red";
ctx.fillRect(0, 100, 100, 100);
var i = 0;
let r=Math.floor(Math.random()*10)
let g=Math.floor(Math.random()*100)
let b=Math.floor(Math.random()*100)
var myInterVal = setInterval(() => {
i++;
if (i >= 11) {
clearInterval(myInterVal);
myInterVal = null;
}
// ctx.restore(); // 将canvas恢复到未旋转前的状态
ctx.clearRect(0, 100, 100, 100);
ctx.translate(10 * i, 0);
ctx.fillStyle = `rgb(${r*2+g+100*i},${3*g+10*i},${b*2+3*i+10*i})`;
ctx.fillRect(0, 100, 100, 100);
}, 300);
ctx.closePath();
未完待定