IT技术博客大学习 共学习 共进步

使用canvas绘制时钟

Heero's Blog 2011-09-04 22:35:51 浏览 3,602 次

准备工作

在HTML中指定一个区域放置时钟:

<div id="clock" style="position: relative;"></div>

时钟的一些外观设定:

var width = 260; // 桌布宽度
var height= 260; // 桌布高度
var dot = {
  x : width / 2,
  y : height / 2,
  radius : 6
}; // 圆点位置、半径
var radius = 120; // 圆半径
var borderWidth = 6; // 圆边框宽度

创建<canvas>元素:

var clock = document.getElementById('clock');
var clockBg = document.createElement('canvas');
var clockPointers = document.createElement('canvas');
  
clockPointers.width = clockBg.width = width;
clockPointers.height = clockBg.height = height;
clockPointers.style.position = 'absolute';
clockPointers.style.left = 0;
clockPointers.style.right = 0;

clock.appendChild(clockBg);
clock.appendChild(clockPointers);

这里要创建两个<canvas>元素,目的在于把时钟的圆盘跟指针分离开。这是因为指针要根据当前时间擦除重绘,如果放置在一个<canvas>中,擦除的时候就会把圆盘也给擦掉了。

绘制圆盘

但凡要在<canvas>中绘图,都要先获得其上下文,对应的接口是 canvas.getContext

var bgCtx = clockBg.getContext('2d');

目前canvas.getContext接口的唯一一个合法参数是'2d',将来应该会支持3D绘图。

先来绘制最外面的圆框:

bgCtx.beginPath();
bgCtx.lineWidth = borderWidth;
bgCtx.strokeStyle = '#000';
bgCtx.arc(dot.x, dot.y, radius, 0, 2 * Math.PI, true);
bgCtx.stroke();
bgCtx.closePath();

绘图的流程其实都是类似的:

  1. 调用 context.beginPath() 新建路径;
  2. 设置颜色等样式;
  3. 调用路径函数生成路径;
  4. 画线(stroke)或者填充(fill);
  5. 调用 context.closePath() 关闭路径;

上面用到的的 context.arc 接口可以生成圆弧路径,其详细说明参见此处

用类似的方法,画出圆点:

bgCtx.beginPath();
bgCtx.fillStyle = '#000';
bgCtx.arc(dot.x, dot.y, dot.radius, 0, 2 * Math.PI, true);
bgCtx.fill();
bgCtx.closePath();

此时,结果如下图所示:

时钟圆框和圆点

绘制刻度

最复杂的地方就是画刻度了,这里要先复习一下数学中的三角函数

三角函数

刻度的起始位置就是圆框上的一个点,第一步就是要知道这个点的坐标。上图中:

sinθ = AC / AO
cosθ = OC / AO

其中AO即为圆半径,而θ的值则根据刻度而定。0是π/2,3是0,6是3π/2,9是π:

三角函数坐标

由此可得到刻度起始点的位置为:

x = 圆点横坐标 + AO * cosθ
y = 圆点纵坐标 + AO * sinθ

同理可算出刻度结束点的位置为(结束点相当于在一个半径为圆框半径-刻度长度的圆上):

x = 圆点横坐标 + (AO - 刻度长度) * cosθ
y = 圆点纵坐标 + (AO - 刻度长度) * sinθ

于是,这程序可以写了:

for (var i = 0, angle = 0, tmp, len; i < 60; i++) {
  bgCtx.beginPath();
  
  // 突出显示能被5除尽的刻度
  if (0 === i % 5) {
    bgCtx.lineWidth = 5;
    len = 12;
    bgCtx.strokeStyle = '#000';
  } else {
    bgCtx.lineWidth = 2;
    len = 6;
    bgCtx.strokeStyle = '#999';
  }
  
  tmp = radius - borderWidth / 2; // 因为圆有边框,所以要减去边框宽度
  bgCtx.moveTo(
    dot.x + tmp * Math.cos(angle),
    dot.y + tmp * Math.sin(angle)
  );
  tmp -= len;
  bgCtx.lineTo(dot.x + tmp * Math.cos(angle), dot.y + tmp * Math.sin(angle));
  bgCtx.stroke();
  bgCtx.closePath();
  
  angle += Math.PI / 30; // 每次递增1/30π
}

画好刻度后,结果应该是这样:

时钟刻度

画指针

先得获取指针<canvas>的上下文:

var ptxContext = clockPointers.getContext('2d');

由于画指针的操作每隔一秒都要执行一次,所以这里就写成一个函数,方便传给setInterval调用:

function updatePointers() {
  ptCtx.clearRect(0, 0, width, height);  // 清掉原来的指针
  
  // 获取当前时间
  var now = new Date();
  var h = now.getHours();
  var m = now.getMinutes();
  var s = now.getSeconds();
  
  // 算出时分秒指针现在应指向圆的几分之几处
  h = h > 12 ? h - 12 : h;
  h = h + m / 60;
  h = h / 12;
  m = m / 60;
  s = s / 60;
  
  drawPointers(s, 2, 92); // 画秒针
  drawPointers(m, 4, 82); // 画分针
  drawPointers(h, 6, 65); // 画时针
}

drawPointers函数的实现是:

// angle是角度,lineWidth是指针宽度,length是指针长度
function drawPointers(angle, lineWidth, length) {
  angle = angle * Math.PI * 2 - Math.PI / 2;
  
  ptCtx.beginPath();
  ptCtx.lineWidth = lineWidth;
  ptCtx.strokeStyle = "#000";
  ptCtx.moveTo(dot.x, dot.y);
  ptCtx.lineTo(dot.x + length * Math.cos(angle), dot.y + length * Math.sin(angle));
  ptCtx.stroke();
  ptCtx.closePath();
}

这里主要也是用到三角函数,就不

建议继续学习

  1. Canvas学习教程 : Canvas介绍 (阅读 4,201)
  2. HTML5 Canvas(画布)教程 (阅读 3,542)
  3. 使用JavaScript和Canvas开发游戏 (阅读 3,521)
  4. 云标签,关键字图排版 html5 canvas版(一) (阅读 3,364)
  5. 使用JavaScript和Canvas开发游戏(一) (阅读 3,021)
  6. Canvas高级特性 (阅读 2,964)
  7. 使用JavaScript和Canvas开发游戏(三) (阅读 2,641)
  8. 使用JavaScript和Canvas开发游戏(二) (阅读 2,561)
  9. 使用逻辑时钟重述paxos协议 (阅读 2,384)
  10. 使用JavaScript和Canvas开发游戏(五) (阅读 2,302)