- 介绍
canvas是html5的一个标签,用于图形的绘制。
用<canvas></canvas>定义图形,用js脚本绘制图形。
我们想要canvas绘制,首先要在html的页面上创建一个canvas画布,创建的方法是使用canvas标签。
<canvas id="canvas" width="200" height="100" style="xxxx"></canvas>
const canvas=document.getElementById("canvas"); const context=canvas.getContext("2d"); // 2d绘图, 3d和2d的是截然不同的 //使用context绘制(canvas的所有接口都是使用context这个绘图上下文环境所提供的)
- 坐标
canvas 是一个二维网格。
canvas 的左上角坐标为 (0,0)
fillRect 方法拥有参数 (0,0,150,75)。
意思是:在画布上绘制 150x75 的矩形,从左上角开始 (0,0)。
- 路径
在Canvas上画线,我们将使用以下两种方法:
- moveTo(*x,y*) 定义线条开始坐标
- lineTo(*x,y*) 定义线条结束坐标
const canvas=document.getElementById("canvas");
const context=canvas.getContext("2d");
context.moveTo(0,0);// 可以想象成一个虚拟的笔尖,落在(0,0)这个位置,之后调用lineTo,笔尖划到(200,100)这个位置
context.lineTo(200,100);// 状态设置,canvas的绘图是基于状态的绘制,这里是设置了状态,告诉canvas是要将要绘制(0,0)到(200,100)的直线,但是没有真正的绘制
// 设置其他状态
context.lineWidth = 10;
context.strokeStyle = "#DDD";
context.stroke(); // 绘制线条
3.1 箭头
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
// 状态设置
context.moveTo(100, 350) // 笔尖移动
context.lineTo(500, 350)
context.lineTo(500, 200)
context.lineTo(700, 400)
context.lineTo(500, 600)
context.lineTo(500, 450)
context.lineTo(100, 450)
context.lineWidth = 10;
context.strokeStyle = "#058";
context.stroke() // 开始绘制
3.2 多条折线
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
// 状态设置
context.moveTo(100, 200) // 笔尖移动
context.lineTo(300, 400)
context.lineTo(100, 600)
context.moveTo(300, 200)
context.lineTo(500, 400)
context.lineTo(300, 600)
context.moveTo(500, 200)
context.lineTo(700, 400)
context.lineTo(500, 600)
context.lineWidth = 10;
context.strokeStyle = "#058";
context.stroke() // 开始绘制 绘制当前状态的所有内容
3.3 多种颜色线段
使用beginPath() 相当于创建了一个新的路径和新的状态来进行一次绘制。
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
context.lineWidth = 10;
context.beginPath();
context.lineTo(100, 200) // beginPath()和lineTo()放一起相当于context.moveTo(100, 200)
context.lineTo(300, 400)
context.lineTo(100, 600)
context.strokeStyle = "#ddd";
context.stroke()
context.beginPath();
context.lineTo(300, 200) // context.moveTo(300, 200)
context.lineTo(500, 400)
context.lineTo(300, 600)
context.strokeStyle = "red";
context.stroke()
context.beginPath();
context.lineTo(500, 200) // context.moveTo(500, 200)
context.lineTo(700, 400)
context.lineTo(500, 600)
context.strokeStyle = "#058";
context.stroke()
该方法的含义是开辟一条新的路径,canvas是基于状态绘制图形的,每次绘制(stroke和fill),canvas都会检查整个程序定义的所有的状态,当状态值发生改变的时候,根据是否使用了beginPath(),会出现两种不同的处理方式:
- 如果使用了beginPath()方法开始一条新的路径,则不同路径使用不同的值
- 如果没有使用beginPath()方法,则后面的值会覆盖前面的
当每条直线都是独立路径的时候,独立路径之间的设置是不互相影响的
3.4 绘制封闭图形
beginPath()和closePath() 成对出现
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
// 状态设置
context.beginPath();
context.moveTo(100, 350) // 笔尖移动
context.lineTo(500, 350)
context.lineTo(500, 200)
context.lineTo(700, 400)
context.lineTo(500, 600)
context.lineTo(500, 450)
context.lineTo(100, 450)
// context.lineTo(100, 350)
context.closePath();
context.lineWidth = 10
// 填充颜色
context.fillStyle = "yellow"
context.fill()
context.strokeStyle = "#058"
context.stroke() // 开始绘制
3.5绘制矩形
canvas提供了方法来绘制矩形
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
context.beginPath();
context.rect(100,100, 300, 200);
context.closePath();
context.lineWidth = 10
context.fillStyle = "yellow"
context.strokeStyle = "#058"
context.fill()
context.stroke()
context.fillRect(300, 300, 300, 200);
context.strokeRect(400, 400, 300, 200)
3.6 线条属性
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
context.lineWidth = 50;
context.strokeStyle = "#27a";
context.beginPath();
context.moveTo(100, 200)
context.lineTo(700, 200)
context.lineCap = "butt"
context.stroke()
context.beginPath();
context.moveTo(100, 400)
context.lineTo(700, 400)
context.lineCap = "round"
context.stroke()
context.beginPath();
context.moveTo(100, 600)
context.lineTo(700, 600)
context.lineCap = "square"
context.stroke()
// base基线
context.lineWidth = 1
context.strokeStyle = "#27a"
context.moveTo(100, 100)
context.lineTo(100, 700)
context.moveTo(700, 100)
context.lineTo(700, 700)
context.stroke()
3.7绘制五角星
window.onload = function () {
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
drawStar(context, 300, 150, 400, 400, 0)
}
function drawStar(context, R, r, x, y, rot) {
context.beginPath();
for(var i = 0; i<5; i++) {
context.lineTo(Math.cos((18+i*72 + rot)/180 * Math.PI)*R+x,
-Math.sin((18+i*72 + rot)/180 * Math.PI)*R+y);
context.lineTo(Math.cos((54+i*72 +rot)/180 * Math.PI)*r+x,
-Math.sin((54+i*72 +rot)/180 * Math.PI)*r+y);
}
context.closePath();
context.lineWidth = 10;
context.fillStyle = "yellow"
context.strokeStyle = "#058"
context.fill()
context.stroke()
}
图形变化
-- translate(x, y): 将canvas画布进行位移显示。将坐标原点移动到(x,y)的位置,translate将原点移动之后,如果再次调用translate进行移动,那么会依照上一个translate移动之后作为原点参考
-- rotate(deg): 旋转deg的度数
--scale(sx, sy): 在横向进行sx倍的缩放,在纵项进行sy倍的缩放
图形变换会引起叠加效果
translate(x, y);
translate(-x, -y);
-- save(): 状态的保存
--restore(): 状态的恢复, 返回的是save()节点的状态
通过图形转换绘制满天星(先绘制基本的轮廓,再通过图形转换的方式改变其位移、旋转)
window.onload = function () {
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
// 线性渐变
var skyStyle = context.createLinearGradient(0, 0, 0, canvas.height)
skyStyle.addColorStop(0.0, "black")
skyStyle.addColorStop(1.0, "#035")
context.fillStyle = skyStyle
context.fillRect(0, 0, canvas.width, canvas.height)
for(var i = 0; i<200; i++) {
var r = Math.random() * 10 + 10;
var x = Math.random() * canvas.width;
var y = Math.random() * canvas.height * 0.65;
var a = Math.random() * 360;
drawStar(context, 20, 10, a, x, y)
}
}
function drawStar(context, R, r, a, x, y) {
context.save();
context.translate(x, y)
context.rotate(a /180 * Math.PI)
context.beginPath();
for(var i = 0; i<5; i++) {
context.lineTo(Math.cos((18+i*72)/180 * Math.PI)*R,
-Math.sin((18+i*72)/180 * Math.PI)*R);
context.lineTo(Math.cos((54+i*72)/180 * Math.PI)*r,
-Math.sin((54+i*72)/180 * Math.PI)*r);
}
context.closePath();
context.fillStyle = "#fb3"
context.strokeStyle = "#fd5"
context.lineWidth = 3;
context.linejoin = "round";
context.fill()
context.stroke()
context.restore()
}
当绘制一个整体元素的时候,特别是涉及到图形变化的时候,都应该先save一下,等绘制结束后再restore,从而保证canvas绘图状态的正确。
unction drawShape(ctx){
ctx.save(); // 状态保存
// 绘制路径
shapePath(ctx);
// 进行填充或者绘制
// ...
ctx.restore(); // 状态恢复
}
function shapePath(ctx){
ctx.beginPath();
// 图形路径
// ...
ctx.closePath();
}
4. 曲线绘制
canvas里有四个曲线绘制相关的函数
- context.arc(x, y, radius, startAngle, endAngle [, anticlockwise]
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
context.lineWidth = 5
context.strokeStyle = '#005588'
// 不连续弧
for (let i = 0; i < 10; i++) {
context.beginPath()
context.arc(50 + i*100, 60, 40, 0, 2*Math.PI*(i+1)/10)
context.stroke()
}
// 连续弧
for (let i = 0; i < 10; i++) {
context.beginPath()
context.arc(50 + i*100, 180, 40, 0, 2*Math.PI*(i+1)/10)
context.closePath()
context.stroke()
}
// 不连续弧 逆时针
for (let i = 0; i < 10; i++) {
context.beginPath()
context.arc(50 + i*100, 300, 40, 0, 2*Math.PI*(i+1)/10, true)
context.stroke()
}
// 连续弧 逆时针
for (let i = 0; i < 10; i++) {
context.beginPath()
context.arc(50 + i*100, 420, 40, 0, 2*Math.PI*(i+1)/10, true)
context.closePath()
context.stroke()
}
// 填充
context.fillStyle = "#005588"
for (let i = 0; i < 10; i++) {
context.beginPath()
context.arc(50 + i*100, 540, 40, 0, 2*Math.PI*(i+1)/10)
context.fill()
}
// 填充
context.fillStyle = "#005588"
for (let i = 0; i < 10; i++) {
context.beginPath()
context.arc(50 + i*100, 660, 40, 0, 2*Math.PI*(i+1)/10)
context.closePath()
context.fill()
}
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
context.beginPath()
context.moveTo(150, 150)
context.arcTo(650, 150, 650, 650, 500)
context.lineWidth = 6
context.strokeStyle = "red"
context.stroke()
// 基线
context.beginPath()
context.moveTo(150, 150)
context.lineTo(650, 150)
context.lineTo(650,650)
context.lineWidth = 2
context.strokeStyle = "gray"
context.stroke()
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
context.font = "bold 40px Arial"
context.fillStyle = "#508"
context.fillText("canvas 绘图 字体 bold 50px ", 100, 100)
context.strokeStyle = "#508"
context.strokeText("canvas 绘图 字体 bold 50px ", 100, 200)
context.fillText("canvas 绘图 字体 bold 50px ", 100, 300, 400)
context.strokeText("canvas 绘图 字体 bold 50px ", 100, 400, 400)
6. 使用场景
图像标注:
物体识别
initCanvas() {
this.img = document.getElementById("choose-area") as HTMLImageElement;
this.canvasIns = document.getElementById("choose-canvas") as HTMLCanvasElement;
this.ctxIns = this.canvasIns.getContext("2d") as CanvasRenderingContext2D;
this.canvasIns.onclick = (e) => {
this.onClick(e);
};
this.canvasIns.onmousemove = (e) => {
this.mouseMove(e);
};
this.img.onload = () => {
const w = this.img.offsetWidth;
const h = this.img.offsetHeight;
this.canvasIns.style.cursor = "crosshair";
this.canvasIns.width = w;
this.canvasIns.height = h;
this.width = w;
this.height = h;
};
}