1、指针的案例
-
注意点:
1、如果没有闭合路径 即时save和restore了之后 还是会覆盖掉之前的颜色
2、要先偏移、旋转 然后在进行绘画 -
window.requestAnimationFrame()
1、 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
2、不适用setTimout()
是因为它会受到宏任务和微任务的影响,导致动画更新的频率不统一
<!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>
<style>
body {
padding: 0;
margin: 0;
background-image: url(../images/grid.png);
}
canvas {}
</style>
<body>
<canvas id="tutorial" width="300" height="300px">
</canvas>
<script>
window.onload = function () {
const canvasEl = document.getElementById('tutorial')
if (!canvasEl.getContext) return
const ctx = canvasEl.getContext('2d')
function draw() {
console.log('执行函数')
let second = new Date().getSeconds()
// 在执行函数前清空画布
ctx.clearRect(0, 0, 300, 300);
// 1.开始执行
// 1.1 先画一个圆
ctx.save()
ctx.translate(100, 100);
ctx.strokeStyle = "#0000ff";
ctx.beginPath()
ctx.arc(0, 0, 50, 0, Math.PI * 2, true)
ctx.stroke()
ctx.closePath()
ctx.restore()
// 2.开始画出指针
ctx.save()
ctx.translate(100, 100)
ctx.rotate(Math.PI * 2 / 60 * second)
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.lineTo(0, -50)
ctx.strokeStyle = 'red'
ctx.stroke()
ctx.closePath()
ctx.restore()
requestAnimationFrame(draw)
}
requestAnimationFrame(draw)
}
</script>
</body>
</html>
2、太阳系
- 注意点
1、new image() 之后不能直接写src 要等到图片onload
2、save()
和restore()
成对出现
<style>
body {
padding: 0;
margin: 0;
background-image: url(../../images/grid.png);
}
canvas {
background-color: rgba(255, 0, 0, 0.1);
}
</style>
<body>
<canvas id="tutorial" width="300" height="300px">
你的浏览器不兼容Canvas,请升级您的浏览器!
</canvas>
<script>
window.onload = function () {
// 先拿到所有的图片
let sun = new Image();
sun.src = "../../images/canvas_sun.png";
let earth = new Image();
earth.src = "../../images/canvas_earth.png";
let moon = new Image();
moon.src = "../../images/canvas_moon.png";
let canvasEl = document.getElementById("tutorial");
if (!canvasEl.getContext) {
return;
}
const ctx = canvasEl.getContext("2d");
requestAnimationFrame(draw);
function draw() {
// 获取时间戳
const time = new Date();
const second = time.getSeconds();
const milliseconds = time.getMilliseconds();
console.log("draw");
ctx.clearRect(0, 0, 300, 300);
ctx.save();
// 1.绘制背景
ctx.save();
ctx.drawImage(sun, 0, 0, 300, 300); // 背景图
ctx.translate(150, 150);
ctx.beginPath();
ctx.arc(0, 0, 105, 0, Math.PI * 2);
ctx.strokeStyle = "rgba(0, 153, 255, 0.4)";
ctx.stroke();
ctx.closePath();
ctx.restore();
// 2.绘制地球
// 地球60s旋转一周 为了避免卡顿 变成时钟的样子进行偏移 所以要算上毫秒
const sixSendsRound = (Math.PI * 2) / 60;
// 毫秒 = sixSendsRound/10000
ctx.save();
ctx.translate(150, 150); // 先偏移到圆心
// 围绕太阳的坐标做旋转
ctx.rotate(
sixSendsRound * second + (sixSendsRound / 1000) * milliseconds
);
ctx.translate(105, 0); // 偏移到轨道上
ctx.drawImage(earth, -12, -12); // 要使得圆的中心在轨道上 通过偏移
// 3.绘制月亮
// 10s 围绕地球转一圈
ctx.save();
const tenSendsRound = (Math.PI * 2) / 10;
ctx.rotate(
tenSendsRound * second + (tenSendsRound / 1000) * milliseconds
);
ctx.translate(0, 28);
ctx.drawImage(moon, -3.5, -3.5);
ctx.restore();
// 背对着太阳的月亮有阴影 所以要添加一个遮罩层
ctx.save();
ctx.fillStyle = "rgba(0, 0, 0, 0.4)";
ctx.fillRect(0, -12, 40, 40);
ctx.restore();
// 前面save 了两次 需要restore两次
ctx.restore();
requestAnimationFrame(draw);
}
};
</script>
</body>
3、时钟案例
<style>
body {
padding: 0;
margin: 0;
background-image: url(../../images/grid.png);
}
canvas {
/* background-color: yellow; */
}
</style>
<body>
<canvas id="tutorial" width="300" height="300"></canvas>
<script>
window.onload = function () {
let canvasEl = document.getElementById("tutorial");
if (!canvasEl.getContext) return;
const ctx = canvasEl.getContext("2d");
requestAnimationFrame(draw);
function draw() {
ctx.clearRect(0, 0, 300, 300);
/**
* 1、画出圆角矩形
* 1.1 先画出一个直角矩形
* 1.2 确定四个顶点的位置
* 1.3.画出圆角
*/
ctx.save();
ctx.translate(50, 50);
const ptA = [0, 0];
const ptB = [200, 0];
const ptC = [200, 200];
const ptD = [0, 200];
// 圆角矩形
// 弧度为起点 和 第一个点组成的线条
// 第一个点 和 第二个点组成的线条 的交线画出内切圆
ctx.moveTo(25, 0);
ctx.arcTo(ptB[0], ptB[1], ptC[0], ptC[1], 25);
ctx.arcTo(ptC[0], ptC[1], ptD[0], ptD[1], 25);
ctx.arcTo(ptD[0], ptD[1], ptA[0], ptA[1], 25);
ctx.arcTo(ptA[0], ptA[1], ptB[0], ptB[1], 25);
ctx.fill();
ctx.restore();
// ctx.beginClose();
/**
* 2、画出白色圆形表盘
*/
ctx.save();
ctx.beginPath();
ctx.translate(150, 150);
ctx.fillStyle = "white";
ctx.arc(0, 0, 85, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
/**
* 3、画出表盘上的数字
* 3.1 表盘上面有12个数字 每个数字的弧度 = Math *PI * 2 / 12
* 3.2 起始位置 为 90° 不能将他旋转到0° 因为数字会被颠倒
* 3.3 为了避免数字的位置偏差 所以需要将文字居中 以中线对齐
*/
ctx.save();
let numbers = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2];
ctx.font = "18px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.translate(150, 150);
for (let i = 0; i < numbers.length; i++) {
// 从0° 开始
let x = Math.cos(((Math.PI * 2) / 12) * i) * 60;
let y = Math.sin(((Math.PI * 2) / 12) * i) * 60;
ctx.fillText(numbers[i], x, y);
}
ctx.restore();
/**
* 4、绘制时针
* 4.1 绘制时针
*/
const time = new Date();
const hours = time.getHours();
const minutes = time.getMinutes();
const seconds = time.getSeconds();
const pai = Math.PI * 2;
ctx.save();
ctx.translate(150, 150);
ctx.rotate(
(pai / 12) * hours +
(pai / 12 / 60) * minutes +
(pai / 12 / 60 / 60) * seconds
);
ctx.lineWidth = 4;
ctx.lineCap = "round";
// 将点移动回来 否则会从上一个路径开始
ctx.moveTo(0, 0);
ctx.lineTo(0, -40);
ctx.stroke();
ctx.restore();
/**
* 4、绘制时针
* 4.1 绘制时针
* 4.2 绘制分钟针
*/
ctx.save();
ctx.translate(150, 150);
ctx.moveTo(0, 0);
ctx.rotate((pai / 60) * minutes + (pai / 60 / 60) * seconds);
ctx.lineWidth = 3;
ctx.lineCap = "round";
ctx.lineTo(0, -50);
ctx.stroke();
ctx.restore();
/**
* 4、绘制时针
* 4.1 绘制时针
* 4.2 绘制分钟针
* 4.3 绘制秒针
*/
ctx.save();
ctx.beginPath();
ctx.translate(150, 150);
ctx.moveTo(0, 0);
ctx.lineWidth = 2;
ctx.lineCap = "round";
ctx.strokeStyle = "red";
ctx.rotate((pai / 60) * seconds);
ctx.lineTo(0, -55);
ctx.stroke();
ctx.restore();
/**
* 5、绘制圆心点
*/
ctx.save();
ctx.beginPath();
ctx.translate(150, 150);
ctx.arc(0, 0, 5, 0, pai, true);
ctx.fill();
ctx.restore();
ctx.save();
ctx.beginPath();
ctx.fillStyle = "gray";
ctx.translate(150, 150);
ctx.arc(0, 0, 3, 0, pai, true);
ctx.fill();
ctx.restore();
requestAnimationFrame(draw);
/**
* 6、画出刻度表
* 6.1 画出时刻刻度
*/
ctx.save();
ctx.translate(150, 150);
ctx.lineWidth = 3;
for (let i = 0; i < 12; i++) {
// 每次位移都是以新的位移点作为起点
ctx.rotate(pai / 12);
ctx.moveTo(0, -78);
ctx.lineTo(0, -85);
ctx.stroke();
}
ctx.restore();
/**
* 6、画出刻度表
* 6.1 画出时刻刻度
*/
ctx.save();
ctx.translate(150, 150);
ctx.lineWidth = 1;
for (let i = 0; i < 60; i++) {
ctx.rotate(pai / 60);
ctx.moveTo(0, -80);
ctx.lineTo(0, -85);
ctx.stroke();
}
ctx.restore();
}
};
</script>
</body>