想象一下,用几行代码就能创造出如此逼真的图像和动画,仿佛将艺术与科技完美融合,前端开发的Canvas技术正是这个数字化时代中最具魔力的一环,它不仅仅是网页的一部分,更是一个无限创意的画布,一个让你的想象力自由驰骋的平台。
目录
基础页面搭建
设置时分秒针
最终总结
基础页面搭建
接下来借助canvas实现一个简易的动态时钟操作,首先这里我们设置一下画布的一些基础布局样式:
<canvas id="canvas" width="800" height="600"></canvas>
<script>
// 获取canvas画布绘制上下文对象以及获取2d画笔对象
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
</script>
接下来我们通过两个for循环,实现小时与分钟的刻度显示,其中ctx.save()和ctx.restore()用于管理绘图状态,确保不同的绘图操作不会相互影响,代价如下:
ctx.save() // 存档
ctx.translate(400, 300) // 平移
ctx.rotate(-Math.PI / 2) // 旋转
ctx.save() // 存档
for (let i = 0; i < 12; i++) {
// 绘制小时的刻度
ctx.beginPath()
ctx.moveTo(170, 0)
ctx.lineTo(190, 0)
ctx.strokeStyle = 'gray';
ctx.lineWidth = 8
ctx.stroke()
ctx.closePath()
ctx.rotate(2 * Math.PI / 12)
}
ctx.restore() // 恢复上一层坐标
ctx.save() // 存档
for (let i = 0; i < 60; i++) {
// 绘制分钟的刻度
ctx.beginPath()
ctx.moveTo(180, 0)
ctx.lineTo(190, 0)
ctx.strokeStyle = 'gray';
ctx.lineWidth = 2
ctx.stroke()
ctx.closePath()
ctx.rotate(2 * Math.PI / 60)
}
ctx.restore() // 恢复上一层坐标
ctx.save() // 存档
最终呈现的效果如下所示:
设置时分秒针
接下来我们借助设计时分秒针的样式,通过date函数获取当前的时分秒的时间,然后通过借助JS的动态渲染函数requestAnimationFrame来动态改变当前的时间,具体的步骤如下所示,整段代码就完成了利用 Canvas 绘制时钟的功能,根据当前时间动态绘制时、分、秒针的效果:
// 获取当前时间
let time = new Date();
let hour = time.getHours();
let min = time.getMinutes();
let sec = time.getSeconds();
hour = hour >= 12 ? hour - 12 : hour; // 12小时制
// 绘制时针
ctx.rotate(2 * Math.PI / 12 * hour + 2 * Math.PI / 12 / 60 * min + 2 * Math.PI / 12 / 60 / 60 * sec)
ctx.beginPath()
ctx.moveTo(-15, 0)
ctx.lineTo(110, 0)
ctx.strokeStyle = '#333';
ctx.lineWidth = 8
ctx.stroke()
ctx.closePath()
ctx.restore() // 恢复上一层坐标
ctx.save() // 存档
// 绘制分针
ctx.rotate(2 * Math.PI / 60 * min + 2 * Math.PI / 60 / 60 * sec)
ctx.beginPath()
ctx.moveTo(-20, 0)
ctx.lineTo(130, 0)
ctx.strokeStyle = '#888';
ctx.lineWidth = 4
ctx.stroke()
ctx.closePath()
ctx.restore() // 恢复上一层坐标
ctx.save() // 存档
// 绘制秒针
ctx.rotate(2 * Math.PI / 60 * sec)
ctx.beginPath()
ctx.moveTo(-30, 0)
ctx.lineTo(190, 0)
ctx.strokeStyle = 'red';
ctx.lineWidth = 2
ctx.stroke()
ctx.closePath()
ctx.restore() // 恢复上一层坐标
ctx.restore() // 恢复上一层坐标
为了让时钟能够动态的进行时间的跳转,这里我们记住requestAnimationFrame函数进行动画帧的渲染,代码如下:
requestAnimationFrame(render)
最终呈现的效果如下所示:
最终总结
接下来对代码实现了一个基于 HTML5 Canvas 的动态时钟效果,让我逐步解释其实现思路:
清除画布:ctx.clearRect(0, 0, 800, 600); 每帧开始时清除整个画布,避免之前的绘制残留。
变换画布原点:ctx.translate(400, 300); 将画布原点移到中心点位置,便于后续绘制钟表。
旋转坐标系:ctx.rotate(-Math.PI / 2); 将坐标系旋转,使得钟表的 12 点方向朝上。
绘制钟表刻度:分别用循环绘制小时和分钟的刻度,每次绘制完成后旋转坐标系到下一个刻度位置。
获取当前时间:使用 new Date() 获取当前时间的小时、分钟和秒数。
绘制时针、分针、秒针:根据当前时间计算旋转角度,分别绘制时针、分针和秒针。
动画效果:使用 requestAnimationFrame(render) 实现每秒钟更新一次画面,形成动态的钟表效果。
按照上面的步骤,这样整个代码就完成了一个基于 Canvas 的动态时钟绘制,实现了钟表的时、分、秒针的动态变化,以及刻度的静态绘制,完整代码如下所示:
<body>
<canvas id="canvas" width="800" height="600"></canvas>
<script>
// 获取canvas画布绘制上下文对象以及获取2d画笔对象
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
function render() {
ctx.clearRect(0, 0, 800, 600);
ctx.save() // 存档
ctx.translate(400, 300) // 平移
ctx.rotate(-Math.PI / 2) // 旋转
ctx.save() // 存档
for (let i = 0; i < 12; i++) {
// 绘制小时的刻度
ctx.beginPath()
ctx.moveTo(170, 0)
ctx.lineTo(190, 0)
ctx.strokeStyle = 'gray';
ctx.lineWidth = 8
ctx.stroke()
ctx.closePath()
ctx.rotate(2 * Math.PI / 12)
}
ctx.restore() // 恢复上一层坐标
ctx.save() // 存档
for (let i = 0; i < 60; i++) {
// 绘制分钟的刻度
ctx.beginPath()
ctx.moveTo(180, 0)
ctx.lineTo(190, 0)
ctx.strokeStyle = 'gray';
ctx.lineWidth = 2
ctx.stroke()
ctx.closePath()
ctx.rotate(2 * Math.PI / 60)
}
ctx.restore() // 恢复上一层坐标
ctx.save() // 存档
// 获取当前时间
let time = new Date();
let hour = time.getHours();
let min = time.getMinutes();
let sec = time.getSeconds();
hour = hour >= 12 ? hour - 12 : hour; // 12小时制
// 绘制时针
ctx.rotate(2 * Math.PI / 12 * hour + 2 * Math.PI / 12 / 60 * min + 2 * Math.PI / 12 / 60 / 60 * sec)
ctx.beginPath()
ctx.moveTo(-15, 0)
ctx.lineTo(110, 0)
ctx.strokeStyle = '#333';
ctx.lineWidth = 8
ctx.stroke()
ctx.closePath()
ctx.restore() // 恢复上一层坐标
ctx.save() // 存档
// 绘制分针
ctx.rotate(2 * Math.PI / 60 * min + 2 * Math.PI / 60 / 60 * sec)
ctx.beginPath()
ctx.moveTo(-20, 0)
ctx.lineTo(130, 0)
ctx.strokeStyle = '#888';
ctx.lineWidth = 4
ctx.stroke()
ctx.closePath()
ctx.restore() // 恢复上一层坐标
ctx.save() // 存档
// 绘制秒针
ctx.rotate(2 * Math.PI / 60 * sec)
ctx.beginPath()
ctx.moveTo(-30, 0)
ctx.lineTo(190, 0)
ctx.strokeStyle = 'red';
ctx.lineWidth = 2
ctx.stroke()
ctx.closePath()
ctx.restore() // 恢复上一层坐标
ctx.restore() // 恢复上一层坐标
requestAnimationFrame(render)
}
render()
</script>
</body>