使用canavs制作时钟的功能

Posted by Chen Blog on June 29, 2018

上班发呆的时候。看到那个时钟在转。于是就想着,我也做个时钟出来吧。这个东西经常看到,但是没有自己亲自做过。那就动手试试看。看看到底难不难。

效果

先直接上效果图。只做出了功能。样子一般般

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<canvas id="myCanvas" width="200" height="200" style="border:1px solid #000000;"></canvas>
<script>
    var c=document.getElementById("myCanvas");
    var ctx=c.getContext("2d");

    window.onload = function(){

        setInterval(function(){
            //把画布画成白色
            ctx.fillStyle="#fff";
            ctx.beginPath();  
            ctx.fillRect(0,0,c.width,c.height);

            var date = new Date();
            //时针
            var hours = date.getHours();
            hours = hours % 12;
            draw_line(50,hours*5,100,100);

            //分针
            var minute = date.getMinutes();
            draw_line(60,minute,100,100);

            //秒针
            var second = date.getSeconds();
            draw_line(80,second,100,100);
            
        },1000)
        
    }

    /**
     * 画线
     * @param  int line_length 线条的长度
     * @param  int miao     指向几秒
     * @return {[type]}             [description]
     */
    function draw_line(line_length,miao,x0,y0){
        //度数
        /*
            因为钟表的开始是在坐标系的正90度位置开始。所有使用90-开始计算
         */
        var du = 90 - (6 * miao);

        //求y坐标 
        /*
            因为Y的坐标方向和数学里面的坐标方向相反。所以这里Y轴需要用减法
         */
        var y = y0 - Math.sin(2 * Math.PI / 360 * du) * line_length;

        //求x坐标
        var x = x0 + Math.cos(2 * Math.PI / 360 * du) * line_length;

        //定义一个
        ctx.moveTo(x0,y0);
        ctx.lineTo(x,y);
        ctx.stroke();
    }
</script>

理解求x,y的值

坐标系图

线段c是我们钟表的指针。是一个已知的数。还有一个已知数我们可以知道。那就是∠A的角度。我们可以通过360÷60就知道每一秒之间的相隔的度数。得到的答案就是6°

有了以上的两个已知数之后。我们再来看看三角形的三角函数。

我们要求的就是a边和b边。通过上面的公式可以看到sinA=a/c。通过这个公式。可以看到我们已经有两个已知数了。还差a我们不知道。刚好我们要求的也就是a。 那么怎么求出a的长度啦?

1
2
3
4
5
6
设 : 假设我们现在的∠A是30度。c的长度是6厘米
∵ sinA = a/c
∴ sin30 = a/6
    两边都乘6
∴ a = sin30 * 6
∴ a = 3

cos同理

所以得出结论

1
2
3
4
5
a = sin∠A * c
b = cos∠A * c
    也就是说
x = cos∠A * c
y = sin∠A * c

将每一条线画出来

上面的准备工作我们都做完了。已经知道计算公式怎么算。怎么得到结果。现在就是把60条线画出来看看效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #000000;">
</canvas>
<script>
    var c=document.getElementById("myCanvas");
    var ctx=c.getContext("2d");

    for(var i=1;i<61;i++){
        //角度
        var du = 6*i;

        //线条的长度
        var line_length = 200;

        //求Y坐标
        var y = 250 - Math.sin(2 * Math.PI / 360 * du) * line_length;

        //求x坐标
        var x = 250 + Math.cos(2 * Math.PI / 360 * du) * line_length;

        ctx.moveTo(250,250);
        ctx.lineTo(x,y);
        ctx.stroke();
    }
</script>

可以得到下面这样的结果

现在已经离成功不远了。因为我们已经画出60条线了。也就是说。我们的算法没错。理解也没错。

写一个定时执行的方法

现在。我们可以写一个方法。再用定时执行的办法。每一秒调用一次。生成一条线。就可以看到每一秒的效果了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #000000;">
</canvas>
<script>
    var c=document.getElementById("myCanvas");
    var ctx=c.getContext("2d");

    /**
     * 定时执行的方法
     * @param  int miao 当前为第几秒
     * @return {[type]}      [description]
     */
    function timer(miao){
        //角度
        var du = 6*miao;

        //线条的长度
        var line_length = 200;

        //求Y坐标
        var y = 250 - Math.sin(2 * Math.PI / 360 * du) * line_length;

        //求x坐标
        var x = 250 + Math.cos(2 * Math.PI / 360 * du) * line_length;

        ctx.moveTo(250,250);
        ctx.lineTo(x,y);
        ctx.stroke();
    }

    window.onload = function(){
        setInterval(function(){
            var date = new Date();
            var second = date.getSeconds();
            timer(second);
        })
    }
</script>

结果就变成了这个样子。虽然接近我们想要的效果了。但是发现有两个问题。

  1. 指针是逆时针旋转的
  2. 每新的一秒,之前画的线没有删除,而还在继续新增线条

解决方法如下

  1. 因为圆的角度就是往逆时针方向增大的。而且时钟的指针是在圆的正90度开始的。所以。就需要将角度修改一下。改成 90 - (6 * miao)这样就解决了问题
  2. 因为canvas的原因。在画一条新的线之前。需要把原来的界面都搽除掉。所以我使用了。
    1
    2
    3
    4
    
    //把画布画成白色
    ctx.fillStyle="#fff";
    ctx.beginPath();  
    ctx.fillRect(0,0,c.width,c.height);
    

最后修改成了最上面看到的代码那样正确的执行

其他补充

  • 数学里面的坐标系 X轴是向右增加的、Y轴是向上增加的。但是在html的规则里面。Y轴的方向是和正常的坐标系正好相反的。所以我们使用减法来改变坐标的方向。
  • Math.sin(X)其中的X是弧度。不是角度。所以需要将角度转换为弧度2 * Math.PI / 360 * 角度或者Math.PI / 180 * 角度

参考资料

canvas的一些方法和属性

html5清空画布方法

js的sin和cos使用方法