canvas实现的太阳系动画思路总结
canvas实现的太阳系动画思路总结
效果
源码
点击查看代码
<!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>太阳系动画</title>
<style>
canvas {
margin: 50px auto;
display: block;
box-shadow: 0 0 10px rgb(0 0 0 / 50%);
}
</style>
</head>
<body>
<canvas width="800" height="500"></canvas>
<script>
const sun = new Image()
const moon = new Image()
const earth = new Image()
// 地球轨道半径
const orbitRadius = 105
const sunSize = 300
const earthSize = 24
init()
function init() {
sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png'
moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png'
earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png'
// 充当一次,图片load事件
window.requestAnimationFrame(draw)
}
function draw() {
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
// 清空图像(为动画做准备)
ctx.clearRect(0, 0, canvas.width, canvas.height)
// 保存绘制的默认状态
ctx.save()
// 移动原点(可有可无)
ctx.translate(100, 100)
drawSun(ctx)
// 调整绘制原点,使地球以太阳圆心为旋转中线旋转 => 地球沿着轨道走(重点)
ctx.translate(sunSize / 2, sunSize / 2)
drawEarth(ctx)
// 保存绘制原点在轨道上的状态即旋转状态
ctx.save()
drawMoon(ctx)
drawShadow(ctx)
// 恢复画笔状态(绘制原点在轨道上)
ctx.restore()
// 恢复默认状态,避免影响下次绘制
ctx.restore()
window.requestAnimationFrame(draw)
}
function drawSun(ctx) {
ctx.save()
// 绘制太阳
ctx.drawImage(sun, 0, 0, sunSize, sunSize)
// 设置描边颜色
ctx.strokeStyle = 'rgba(0,153,255,0.4)'
ctx.beginPath()
// 绘制地球的轨道(太阳图片大小是300*300 => 太阳中心的位置(150, 150))
ctx.arc(sunSize / 2, sunSize / 2, orbitRadius, 0, Math.PI * 2)
ctx.stroke()
ctx.restore()
}
function drawEarth(ctx) {
const time = new Date()
/*
绘制地球 这个6和6000是什么意思?
按照钟表上的旋转角度:1s转6度
// 每秒转60度(月亮转,比地球转的快)
当前度数 = (360 / 6) * 当前秒数
// 每秒转6度 (地球转)
当前度数 = (360 / 60) * 当前秒数
=> 效果上月亮绕地球的转速比地球绕太阳的转速快10倍
*/
ctx.rotate(
((2 * Math.PI) / 60) * time.getSeconds() +
((2 * Math.PI) / 60000) * time.getMilliseconds()
)
// 在原点在太阳中心的基础上,移动绘制原点到地球中心
ctx.translate(orbitRadius, 0)
// 怎么沿着轨道走?先以太阳中心为旋转中心旋转,再调整距离
ctx.drawImage(
earth,
-earthSize / 2, // 使地球中心的x坐标在轨道上
-earthSize / 2, // 使地球中心的y坐标在轨道上
earthSize,
earthSize
)
}
function drawMoon(ctx) {
// 保存画笔状态,接下里有原点位移、旋转效果修改
ctx.save()
const time = new Date()
ctx.rotate(
((2 * Math.PI) / 6) * time.getSeconds() +
((2 * Math.PI) / 6000) * time.getMilliseconds()
)
// 20: 地球轨道与月亮的间距
ctx.translate(20, 0)
// 绘制月亮
ctx.drawImage(moon, 0, 0)
// 恢复状态,避免影响接下来的操作
ctx.restore()
}
function drawShadow(ctx) {
ctx.save()
ctx.fillStyle = 'rgba(0,0,0,0.4)'
ctx.fillRect(
0,
-earthSize / 2, // 让矩形纵向中心与地球的纵向中心持平
40, // 能遮着一般地球和月亮就行
earthSize
)
ctx.restore()
}
</script>
</body>
</html>
思路
绘制地球、月亮、阴影的时候,将他们看作与太阳中心在一水平线上。
- 保存原始画笔状态
- 绘制太阳
- 绘制地球轨道
- 以太阳中心为绘制原点画圆弧
- 绘制地球
- 以太阳中心为旋转中心,旋转地球。旋转角度对照钟表旋转,以秒来计算
- 以与太阳中心纵向持平的轨道位置(A点)为绘制原点绘制地球。
- 绘制月亮
- 月亮的绘制不影响其他图形的绘制,所有绘制月亮前后要保存和恢复画笔状态
- 以A点为中心旋转
- 设置月亮与A点的距离(效果上是设置地球与月亮的距离)
- 绘制地球遮挡月亮太阳光照射的效果
- 阴影的绘制不影响其他图形的绘制,所有绘制阴影前后要保存和恢复画笔状态
- 以A点画矩形,高为地球的高度,宽以能遮着月亮为主,要遮着一半的地球(矩形纵向中心要与地球纵向中心持平)
- 恢复原始画笔状态,避免影响下次绘制
总结
beginPath
画圆弧的时候,一定要调用beginPath()方法,否则地球轨道的颜色就很亮。至于为什么,现在还不知道。
translate
每次移动是以上次的原点为基准点移动绘制原点。初始绘制原点坐标为(0,0)
rotate
每次都是以上一次的旋转角度为基准,以原点为中心旋转。初始角度为0°
translate与rotate的前后顺序
假设,初始坐标原点为(0, 0);位移坐标为(100, 100);旋转角度为45度
- 先translate再rotate
- 先移动坐标原点到(100, 100)
- 会以(100,100)为中心旋转45度
- 先rotate再translate
- 先以(0, 0)为中心旋转45度
- 再移动原点到(100, 100)为下次绘画做准备
参考
https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Basic_animations

浙公网安备 33010602011771号