时钟数字
现代 Canvas 时钟:添加数字
Section titled “现代 Canvas 时钟:添加数字”第三部分:绘制时钟数字
Section titled “第三部分:绘制时钟数字”现在我们已经有了时钟盘面,接下来添加数字(1到12)。我们将创建一个 JavaScript 函数 drawNumbers,用于计算每个数字在时钟上的位置并进行绘制。
drawClock 函数将被更新,以调用 drawFace(来自第一部分)和我们新的 drawNumbers 函数。
// 假设 drawFace(ctx, radius) 已在前一部分中定义。
function drawNumbers(context, clockRadius) { context.fillStyle = '#333'; // 设置文本颜色 context.font = `${clockRadius * 0.15}px Arial, sans-serif`; // 字体大小相对于半径 context.textBaseline = "middle"; // 垂直对齐文本到中心 context.textAlign = "center"; // 水平对齐文本到中心
for (let num = 1; num <= 12; num++) { // 计算每个数字的角度 // 每个数字间隔 30 度 (PI/6 弧度) // 如果不调整,num * Math.PI / 6 会将数字顺时针定位,从 3 点方向开始。 // 为了将 '12' 放在顶部(相当于 0 弧度),'1' 放在 PI/6 等, // 我们可以将角度计算为 (num * Math.PI / 6)。 // 实际绘制将在旋转后的 context 中的 (0, -距离) 位置进行。 const angle = num * Math.PI / 6;
// 保存当前 context 状态(可选,但适用于复杂的变换) // context.save();
// 旋转到数字应该放置的角度 context.rotate(angle); // 沿着新的 y 轴(指向数字位置)向外平移 context.translate(0, -clockRadius * 0.85); // 将数字放置在半径的 85% 位置 // 旋转回来,使数字竖直绘制,而不是倾斜 context.rotate(-angle);
context.fillText(num.toString(), 0, 0);
// 恢复变换,以便正确绘制下一个数字 // (撤销为当前数字进行的旋转和平移) context.rotate(angle); // 旋转回数字的角度位置 context.translate(0, clockRadius * 0.85); // 平移回时钟中心 context.rotate(-angle); // 将 canvas 旋转回其原始对齐状态(0 度指向 3 点方向)
// 使用 save/restore 的替代方法(适用于每个数字): // context.restore(); }}
// 更新后的主绘制函数function drawClock(context, clockRadius) { drawFace(context, clockRadius); drawNumbers(context, clockRadius);}// 此部分的完整脚本(包含 drawFace 以供参考) const canvas = document.getElementById(“canvas”); const ctx = canvas.getContext(“2d”); let radius = canvas.height / 2; ctx.translate(radius, radius); radius *= 0.90;
function drawFace(context, r) { context.beginPath(); context.arc(0, 0, r, 0, 2 * Math.PI); context.fillStyle = ‘white’; context.fill(); context.strokeStyle = ‘#333’; context.lineWidth = r * 0.1; context.stroke(); context.beginPath(); context.arc(0, 0, r * 0.05, 0, 2 * Math.PI); context.fillStyle = ‘#333’; context.fill(); }
function drawNumbers(context, clockRadius) {
context.fillStyle = ‘#333’;
context.font = ${clockRadius * 0.15}px Arial, sans-serif;
context.textBaseline = “middle”;
context.textAlign = “center”;
for (let num = 1; num <= 12; num++) { const angle = num * Math.PI / 6; context.rotate(angle); context.translate(0, -clockRadius * 0.85); context.rotate(-angle); context.fillText(num.toString(), 0, 0); context.rotate(angle); context.translate(0, clockRadius * 0.85); context.rotate(-angle);}}
function drawClock(context, r) { drawFace(context, r); drawNumbers(context, r); }
drawClock(ctx, radius);
- 字体和文本对齐:
context.fillStyle = '#333';: 设置数字的颜色。context.font =${clockRadius * 0.15}px Arial, sans-serif;: 根据时钟半径的 15% 动态设置字体大小。使用模板字符串(template literal)进行字符串构建是现代 JS 的一个特性。‘Arial, sans-serif’ 提供了一种常见字体和通用回退字体。context.textBaseline = "middle";和context.textAlign = "center";: 这些对于精确的文本定位至关重要。它们确保在fillText中使用的坐标(0,0)指的是文本的水平和垂直中心。
context.font = `${clockRadius * 0.15}px Arial, sans-serif`;context.textBaseline = "middle";context.textAlign = "center";-
循环遍历数字 (1 到 12):
- 一个
for循环从num = 1迭代到12。
- 一个
-
计算数字位置(角度、旋转、平移):
const angle = num * Math.PI / 6;: 计算每个数字的角度。12 个数字间隔 30 度(Math.PI / 6弧度)。num = 1(1 点方向)将在 30 度位置,num = 2(2 点方向)在 60 度位置,依此类推,这些角度是相对于 3 点方向的(在标准笛卡尔坐标系中,3 点方向是 0 弧度,但由于我们在旋转后使用了lineTo(0, -length)的绘制方法,我们的绘制实际上使得 0 弧度对应于时钟的 12 点方向)。context.rotate(angle);: 将 canvas 坐标系旋转到当前数字的角度位置。context.translate(0, -clockRadius * 0.85);: 沿着新的 Y 轴(现在指向数字的位置)向外平移半径的 85%。这是数字将被绘制的位置。context.rotate(-angle);: 这点很重要!在旋转 canvas 以定位绘制点之后,我们将其旋转回来,旋转角度为其负值。这确保数字本身是竖直绘制的,而不会随主旋转一起倾斜。context.fillText(num.toString(), 0, 0);: 在新的(0,0)位置(即平移和重新旋转后的位置)绘制数字。- 恢复变换:绘制完一个数字后,必须撤销为该数字应用的变换,以便正确地定位下一个数字。这包括:
context.rotate(angle);(重新应用旋转,回到数字的放置方向)context.translate(0, clockRadius * 0.85);(沿着该方向平移回时钟中心)context.rotate(-angle);(将系统旋转回本次迭代之前的原始状态)
- 这种显式地撤销变换的操作,也可以更简单地通过在对一个数字进行变换之前使用
context.save()并并在绘制完成后使用context.restore()来管理。这样可以隔离每个数字的变换操作。
for (let num = 1; num <= 12; num++) { const angle = num * Math.PI / 6; context.rotate(angle); // 指向数字应在的位置 context.translate(0, -clockRadius * 0.85); // 移动到该位置 context.rotate(-angle); // 使 canvas 竖直,以便绘制文本
context.fillText(num.toString(), 0, 0);
// 撤销变换,为下一次迭代做准备 context.rotate(angle); // 旋转回数字的角度 context.translate(0, clockRadius * 0.85); // 移回中心 context.rotate(-angle); // 将 canvas 旋转重置回原始状态}通过这些步骤,数字整齐地绘制在了时钟盘面周围。下一部分将介绍如何绘制时钟指针。