巧用三角函数绘制canvas时钟

最近看到一篇资讯:




点击链接去看了一眼,果然又是canvas,想到了开始接触canvas的时候也写过一个时钟,不过当时实现的代码比较坑……

        //弧度=角度*Math.PI/180
        ogc.save(); //保存绘图
        ogc.arc(100, 100, 50, 0, 90 * Math.PI / 180, false); //圆心坐标,半径,起始弧度,结束弧度,旋转方向(默认false顺时针)
        ogc.stroke();
        ogc.restore(); //恢复绘图

canvas绘图与我们一般对绘图的理解不同,它不是所见即所得并且运用了很多数学知识(毕竟是用代码写出来的……)

上面代码就是用canvas的arc()方法绘制了一个90度角的弧线,由于canvas的坐标系比较特殊,所以效果是这样的:


可能不太明显,那么用canvas绘制一个扇形:

        //弧度=角度*Math.PI/180
        ogc.save(); //保存绘图
        ogc.beginPath();//开始一段路径
        ogc.moveTo(100,100);
        ogc.arc(100, 100, 50, 0, 90 * Math.PI / 180, false); //圆心坐标,半径,起始弧度,结束弧度,旋转方向(默认false顺时针)
        ogc.closePath();//闭合路径
        ogc.stroke();
        ogc.restore(); //恢复绘图

其中,beginPath()方法和closePath()方法完全是两个不同的概念,当初被这个绕了好久……关于canvas的基础知识就不说了,这里主要说说当时的思路吧,用arc()方法绘制基于角度的直线,也是一个有趣的用法。

        function toDraw() {
            var oDate = new Date();
            var oHours = oDate.getHours();
            var oMin = oDate.getMinutes();
            var oSen = oDate.getSeconds();

            ogc.save();

            var x = 100;
            var y = 100;
            var r = 100;

            var oHoursValue = (-90 + oHours * 30 + oMin / 2) * Math.PI / 180; //时针弧度
            var oMinValue = (-90 + oMin * 6 + oSen / 10) * Math.PI / 180; //分针弧度
            var oSenValue = (-90 + oSen * 6) * Math.PI / 180; //秒针弧度

            ogc.beginPath();
            for (var i = 0; i < 60; i++) {
                ogc.moveTo(x, y);
                ogc.arc(x, y, r, 6 * i * Math.PI / 180, 6 * (i + 1) * Math.PI / 180);
            };
            ogc.closePath();
            ogc.stroke();

            ogc.beginPath();
            ogc.fillStyle = '#fff';
            ogc.moveTo(x, y);
            ogc.arc(x, y, r * 19 / 20, 0, 360 * Math.PI / 180);
            ogc.closePath();
            ogc.fill(); //填充

            ogc.beginPath();
            ogc.lineWidth = 3;
            for (var i = 0; i < 12; i++) {
                ogc.moveTo(x, y);
                ogc.arc(x, y, r, 30 * i * Math.PI / 180, 30 * (i + 1) * Math.PI / 180);
            };
            ogc.closePath();
            ogc.stroke();

            ogc.beginPath();
            ogc.fillStyle = '#fff';
            ogc.moveTo(x, y);
            ogc.arc(x, y, r * 18 / 20, 0, 360 * Math.PI / 180);
            ogc.closePath();
            ogc.fill(); //填充

            //时针
            ogc.beginPath();
            ogc.moveTo(x, y);
            ogc.arc(x, y, r * 12 / 20, oHoursValue, oHoursValue);
            ogc.closePath();
            ogc.stroke();

            //分针
            ogc.beginPath();
            ogc.moveTo(x, y);
            ogc.arc(x, y, r * 16 / 20, oMinValue, oMinValue);
            ogc.closePath();
            ogc.stroke();

            //秒针
            ogc.beginPath();
            ogc.lineWidth = 1.5;
            ogc.moveTo(x, y);
            ogc.arc(x, y, r * 18 / 20, oSenValue, oSenValue);
            ogc.closePath();
            ogc.stroke();

            ogc.restore();

            for (var i = 0; i <= 330; i += 30) {
                ogc.save();
                ogc.translate(x, y); //移动画布的原点
                ogc.rotate((i) * Math.PI / 180); //旋转图形(旋转的中心基于画布原点)
                ogc.translate(0, -r * 15 / 20);
                ogc.rotate((-i) * Math.PI / 180); //旋转图形(旋转的中心基于画布原点)
                ogc.translate(0, r * 16.5 / 20);
                ogc.font = r * 0.2+"px Arial";
                ogc.textAlign = "center";
                i == 0 ? ogc.fillText(12, 0, -r * 15 / 20) : ogc.fillText(i / 30, 0, -r * 15 / 20);
                ogc.restore();
            };
        }
        setInterval(toDraw, 1000);

先循环60次扇形画分针刻度,然后画个圆盖住,再然后循环12次时针刻度,然后画个圆盖住,三针,数字表盘,封装函数,每秒重绘执行一次。(代码执行状态如下图)


有点ps图层的意思,不过现在来看这段代码实在是看不下去,尤其是最后绘制数字表盘的循环,也是佩服自己当时的脑洞怎么开的这么大……

如果换个思路,已知圆心坐标和半径,能否利用lineTo()方法去绘制刻度和表针呢?

            ogc.save();
            ogc.translate(x, y);
            for (var angle = 0; angle < 360; angle += 6) {
                var tickWidth = angle % 5 === 0 ? 15 : 15 / 2;
                var radian = angle * Math.PI / 180;
                ogc.beginPath();
                ogc.moveTo((r - tickWidth) * Math.cos(radian), (r - tickWidth) * Math.sin(radian));
                ogc.lineTo(r * Math.cos(radian), r * Math.sin(radian));
                ogc.strokeStyle = 'rgba(100, 140, 230, 0.7)';
                ogc.stroke();


                ogc.beginPath();
                ogc.textAlign = "center";
                ogc.textBaseline = "middle";
                ogc.font = '12px Helvetica';
                if (angle % 30 === 0) {
                    var num = angle / 30 - 9 > 0 ? angle / 30 - 9 : angle / 30 + 3; //刻度
                    ogc.fillText(num, (r - 30) * Math.cos(radian), (r - 30) * Math.sin(radian));
                };
            }
            ogc.restore();


基于之前的代码删改了下,只改了刻度与表盘的绘制,效果还是可以的。


lineTo()方法需要传递一个坐标,至于坐标点的位置怎么得到,就要用到三角函数了,直接上图,方便理解。


猜你喜欢

转载自blog.csdn.net/u010815486/article/details/52266727