什么是Canvas

在Web开发中,Canvas通常指的是HTML5中的<canvas>元素,它提供了一种通过JavaScript绘制图形的方式。开发者可以使用Canvas API在网页上动态绘制图形、动画等。

简单例子

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#canvas{
background: brown;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d'); // 获取绘图环境
ctx.beginPath(); // 开始绘制路径
ctx.moveTo(0,0); // 设置路径的起始点
ctx.lineTo(100,0); // 设置路径的终点
ctx.lineTo(100,100); // 设置路径的终点
ctx.closePath(); // 闭合路径
ctx.stroke(); // 绘制路径
ctx.fill(); // 填充路径

// 绘制矩形
// 第一个参数: 矩形左上角的x坐标
// 第二个参数: 矩形左上角的y坐标
// 第三个参数: 矩形的宽度
// 第四个参数: 矩形的高度
ctx.strokeRect(150,0,100,100);


// 填充矩形
ctx.fillRect(300,0,100,100);


// 将一个矩形中的任何痕迹都擦除
ctx.clearRect(325,25,50,50);


// 绘制圆弧
// 第一个参数: 圆弧中心的x坐标
// 第二个参数: 圆弧中心的y坐标
// 第三个参数: 圆弧的半径
// 第四个参数: 开始绘制的角度
// 第五个参数: 结束绘制的角度
// 第六个参数: 绘制的方向(true表示逆时针,false表示顺时针)
ctx.beginPath();
ctx.arc(50,200,50,0,Math.PI,true);
ctx.stroke(); // 绘制路径
ctx.fill(); // 实心

// 绘制空心文本
ctx.font = '30px Arial';
ctx.strokeText('hello',100,200);

// 绘制实心文本
ctx.fillText('hello',200,200);
</script>
</body>
</html>

设置样式

设置填充样式和轮廓样式

默认下填充样式和轮廓样式都是黑色的,可以使用ctx.fillStylectx.strokeStyle设置填充样式和轮廓样式。

// 设置填充样式
ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255,165,0)";
ctx.fillStyle = "rgba(255,165,0,0.5)";

注意:如果一个上下文设置了样式,后序使用都是同一个样式,直到再次修改才会改变样式。

路径形状样式

  • ctx.lineWidth:默认值是1

  • ctx.lineCap:设置线段末端的形状,butt以方形结尾不加帽子,round以圆形结尾,square以方形结束,加了个正方形帽子

样式的存储和恢复

  • ctx.save():将当前状态存档

  • ctx.restore():恢复之前的状态

变形的操作

  • ctx.translate(x, y):移动坐标轴的原点,没有移动画板,只是移动了坐标系。

  • ctx.rotate(angle):旋转坐标系,角度还是弧度制

  • ctx.scale(x, y):修改坐标轴的比例,比如ctx.scale(2, 0.5)可以将坐标轴在水平方向上放大两倍,垂直方向上缩写一半。

实现画板

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#canvas {
border: 1px solid #000;
}
</style>
</head>

<body>
<div>
<button id="clear">重置</button>
<button id="save">保存</button>
</div>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
let painting = false;
let startPoint = { x: undefined, y: undefined };

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

canvas.onmousedown = (e) => {
let x = e.offsetX;
let y = e.offsetY;
startPoint = { x: x, y: y };
painting = true;
}

canvas.onmousemove = (e) => {
let x = e.offsetX;
let y = e.offsetY;
let newPoint = { x: x, y: y };
if (painting === true) {
drawLine(startPoint, newPoint);
startPoint = newPoint;
}
}

canvas.onmouseup = () => {
painting = false;
}

function drawLine(start, end) {
ctx.beginPath();
ctx.lineWidth = 5;
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
ctx.stroke();
ctx.closePath();
}

clear.onclick = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}

save.onclick = () => {
// 这里需要将canvas转成图片,然后保存
// Data URL 包含了图像的完整编码
let url = canvas.toDataURL("image/jpg");
let a = document.createElement('a');
a.href = url;
a.download = '画板'; // 设置保存的文件名为画板
a.target = '_blank'; // 重新打开一个标签页
a.click();
}
</script>
</body>

</html>

实现一个时钟

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
<script>
function animate(time){
const now = new Date();
const sec = now.getSeconds();
const min = now.getMinutes();
let hr = now.getHours() % 12;

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

ctx.save();
ctx.clearRect(0,0,canvas.width,canvas.height);

// 移动坐标轴位置,然后旋转
ctx.translate(300,300);
ctx.rotate(-Math.PI/2);

ctx.strokeStyle = 'black';
ctx.lineWidth = 5;
ctx.lineCap = 'round';

ctx.save()
for(var i=0;i<12;i++) {
ctx.beginPath();
ctx.rotate(Math.PI/6);
ctx.moveTo(100,0);
ctx.lineTo(120,0);
ctx.stroke();
}
ctx.restore()

ctx.save()
ctx.lineWidth = 3;
for(var i=0;i<60;i++) {
ctx.beginPath();
ctx.rotate(Math.PI/30);
ctx.moveTo(110,0);
ctx.lineTo(120,0);
ctx.stroke();
}
ctx.restore()

// 绘制时针
ctx.save();
ctx.rotate(hr*(Math.PI/6)+min*(Math.PI/360)+sec*(Math.PI/21600));
ctx.lineWidth = 14;
ctx.beginPath()
ctx.moveTo(-20,0);
ctx.lineTo(80,0);
ctx.stroke();
ctx.restore()

// 绘制分针
ctx.save();
ctx.rotate(min*(Math.PI/30)+sec*(Math.PI/1800));
ctx.lineWidth = 10;
ctx.beginPath()
ctx.moveTo(-28,0);
ctx.lineTo(105,0);
ctx.stroke();
ctx.restore()

// 绘制秒针
ctx.save();
ctx.rotate(sec*(Math.PI/30));
ctx.strokeStyle = "#D40000";
ctx.fillStyle = "#D40000";
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(-30,0);
ctx.lineTo(110,0);
ctx.stroke();
ctx.beginPath();
ctx.arc(0,0,10,0,Math.PI*2,true);
ctx.fill();
ctx.fillStyle = "rgba(0,0,0,0)";
ctx.arc(0,0,3,0,Math.PI*2,true);
ctx.fill();
ctx.restore()

ctx.restore();
window.requestAnimationFrame(animate);
}
// 启动动画
window.requestAnimationFrame(animate);

</script>
</body>
</html>