Skip to content

时钟数字

现在我们已经有了时钟盘面,接下来添加数字(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);

  1. 字体和文本对齐:
    • 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. 循环遍历数字 (1 到 12):

    • 一个 for 循环从 num = 1 迭代到 12。
  2. 计算数字位置(角度、旋转、平移):

    • 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 旋转重置回原始状态
}

通过这些步骤,数字整齐地绘制在了时钟盘面周围。下一部分将介绍如何绘制时钟指针。