canvas 动画
太阳系动画
注意这两个 API 的使用:
js
// 返回当前时间的秒数,范围 0 - 60
time.getSeconds()
// getMilliseconds() 方法返回一个 0 到 999 的整数。
time.getMilliseconds()vue
<script setup lang="ts">
import { onMounted } from 'vue';
import { withBase } from 'vitepress'
onMounted(() => {
// 第一个路径
const sun = new Image();
const moon = new Image();
const earth = new Image();
init();
function init(){
// 加载三张图片
sun.src = withBase('https://lukecheng2.oss-cn-chengdu.aliyuncs.com/public/img/canvas_sun.png');
moon.src = withBase('https://lukecheng2.oss-cn-chengdu.aliyuncs.com/public/img/canvas_moon.png');
earth.src = withBase('https://lukecheng2.oss-cn-chengdu.aliyuncs.com/public/img/canvas_earth.png');
// 开始绘画
window.requestAnimationFrame(draw);
}
function draw() {
// 获取画布
const ctx = (document.getElementById('solar-system') as HTMLCanvasElement).getContext('2d')!
// 设置合成属性为 destination-over
ctx.globalCompositeOperation = 'destination-over';
// 清空画布
ctx.clearRect(0, 0, 300, 300);
// 设置填充颜色为带点透明的黑色
ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
// 设置轮廓颜色
ctx.strokeStyle = 'rgba(0, 153, 255, 0.4)';
// 第一次保存
ctx.save();
// 把坐标系原点移到画布中间
ctx.translate(150, 150);
// 绘制地球
const time = new Date();
// 60 秒转一圈,也就是说,60 秒旋转的角度有 2 * Math.PI。
// ((2 * Math.PI) / 60000) * time.getMilliseconds():精确到毫秒。
//
ctx.rotate( ((2 * Math.PI) / 60) * time.getSeconds() + ((2 * Math.PI) / 60000) * time.getMilliseconds() );
// 把坐标系移到相对于 (150, 150) 的 (105, 0) 处。
ctx.translate(105, 0); // 不能和上面的 ctx.rotate 调整顺序,否则地球不转了。
// 绘制阴影
ctx.fillRect(0, -12, 50, 24); // Shadow
// 绘制地球
ctx.drawImage(earth, -12, -12);
// 绘制月亮
// 第二次保存
ctx.save();
// 围绕地球旋转。6 秒转一圈,也就是说,6 秒旋转的角度有 2 * Math.PI。
// 精度也达到毫秒级别。
ctx.rotate( ((2 * Math.PI) / 6) * time.getSeconds() + ((2 * Math.PI) / 6000) * time.getMilliseconds() );
// 有调整坐标系原点
ctx.translate(0, 28.5);
// 绘制月亮
ctx.drawImage(moon, -3.5, -3.5);
// 第一次恢复
ctx.restore();
// 第二次恢复
ctx.restore();
ctx.beginPath();
// 绘制地球轨道
ctx.arc(150, 150, 105, 0, Math.PI * 2, false);
ctx.stroke();
// 绘制太阳
ctx.drawImage(sun, 0, 0, 300, 300);
// 重复绘画(rotate 会在上一次旋转的基础上进行旋转)
window.requestAnimationFrame(draw);
}
})
</script>
<template>
<div class="border-t-1 border-b-1 border-l-none border-r-none border-solid border-t-gray-700 border-b-gray-700 pa-4 my-4">
<canvas id="solar-system" width="300" height="300" class="border border-solid border-red">
</canvas>
</div>
</template>时钟
鼠标跟踪动画
c 如何实现 canvas 的拖尾效果?
这里的技巧在于,在重绘制下一帧前,不是调用 clearRect 清除掉上一帧的内容,而是在上一帧基础上加上一个半透明蒙层,然后继续画下一帧。画的帧数多了,也就有了拖尾效果,拖尾效果的长短,跟蒙层的透明度有关,透明度越小,拖尾越长。