巧用三角函数绘制canvas时钟

来源:互联网 发布:一体打印机推荐 知乎 编辑:程序博客网 时间:2024/06/05 06:26

最近看到一篇资讯:




点击链接去看了一眼,果然又是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()方法需要传递一个坐标,至于坐标点的位置怎么得到,就要用到三角函数了,直接上图,方便理解。


0 0