此项目的效果是太阳保持不动,地球围绕太阳转,月亮围绕地球转,要注意的是有两个原点位置的变换,地球和月亮的旋转点是不同的。如下图:
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
background-color: #666;
display: block;
margin: auto;
}
</style>
</head>
<body>
<script>
// 1、准备一个canvas
const canvas = document.createElement('canvas')
canvas.width = 800
canvas.height = 800
document.body.append(canvas)
const context = canvas.getContext('2d')
// 准备一个地球旋转的角度
let earthAngle = 0
// 月球旋转的角度
let moonAngle = 0
function loop() {
context.save()
// 清除之前画在画布上的内容 循环重复的内容
context.clearRect(0, 0, canvas.width, canvas.height)
// 画太阳
context.beginPath()
context.arc(400, 300, 100, 0, 2 * Math.PI)
context.fillStyle = '#e3fa14'
context.shadowColor = '#e3fa14'
context.shadowBlur = 10
context.fill()
context.closePath()
// 画地球
context.beginPath()
// 先把 canvas 原点移动到正中心
context.translate(400, 300)
// 然后记画笔旋转角度 围绕太阳中心 原点位置400,300
context.rotate((earthAngle += 0.3) * Math.PI / 180)
// 为了后面画月亮的动画,要把原点变成和地球中心完全一致
context.translate(200,0)
// 再画地球 原点和地球中心一致 此时原点中心在600,300
// 此时原点中心已经又移动,为了原点和地球中心一致,所以(0,0)
context.arc(0, 0, 20, 0, 2 * Math.PI)
context.fillStyle = 'blue'
context.shadowBlur = 0
context.fill()
context.closePath()
// 画月亮
context.beginPath()
context.rotate((moonAngle += 2) * Math.PI / 180) // 月亮旋转 围绕地球中心,原点位置600,300
context.arc(40, 0, 6, 0, 2 * Math.PI) // 此时原点中心在(600,300)
context.fillStyle = '#fff'
context.fill()
context.closePath()
context.restore()
requestAnimationFrame(loop)
}
requestAnimationFrame(loop)
</script>
</body>
</html>
画太阳时,太阳的中心点在(400,300)
画地球时,为了让地球围绕太阳转,所以先把原点移动到了(400,300),然后再旋转
而为了月亮绕着地球转,又把原点移动了(200,0),此时的原点位置就在了(600,300)的位置,但600,300这个数值后面不用,只是为了清楚坐标移动。需要注意的是:第二次移动坐标原点时,此时地球的中心位置变成了(0,0),是为了把原点变成和地球中心完全一致
而此时的原点已经和地球中心一致,所以画月亮时,就要月亮根据地球中心的位置距离来画
注意:移动了两次原点位置,为了保持旋转各自绕着各自的旋转点旋转,要先移动原点,再旋转,最后根据原点来画圆
context.save()
方法会将当前画布(Canvas)的状态压入一个保存状态的栈中。这个状态包括当前的变换矩阵、剪切区域、画笔样式(strokeStyle, fillStyle, lineWidth等)、字体样式(font)、全局透明度(globalAlpha)、全局复合操作(globalCompositeOperation)等。
当你需要改变这些状态进行某些绘制操作,但又不希望这些改变影响到后续绘制时,可以使用 context.save()
来保存当前状态,之后进行的任何状态改变都不会影响到之前保存的状态。
context.restore()
方法会从状态栈中弹出最近保存的状态,并将Canvas的渲染上下文恢复到这个状态。这意味着所有在 context.save()
之后做的状态改变(如变换、设置透明度等)都会被撤销,Canvas的状态会回到 context.save()
被调用时的状态。
这两个方法通常成对使用,在你需要临时改变Canvas状态进行绘制时非常有用。例如,你可能需要在一个特定的透明度下绘制某些图形,但之后又要恢复到原来的透明度进行其他绘制。这时,你可以在改变透明度之前调用 context.save()
,绘制完毕后调用 context.restore()
来恢复到原来的透明度设置。
Video_2024-10-14_142041