0

使用Canvas绘制罗盘时钟

 2 years ago
source link: https://tomoya92.github.io/2021/12/24/canvas-clock/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

使用Canvas绘制罗盘时钟 | 朋也的博客

朋也的博客 » 首页 » 文章

使用Canvas绘制罗盘时钟

作者:朋也
日期:2021-12-24
类别:javascript学习笔记 


版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证

惯例先上效果

<canvas width="400" height="400" id="canvas" style="border:1px solid #333;"></canvas>
<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    // 开始路径绘制
    ctx.beginPath();
    // 将画笔移动到原点
    ctx.moveTo(10, 10);
    // 绘制一条线
    ctx.lineTo(200,10);
    // 再绘制一条线
    ctx.lineTo(10,200);
    // 关闭路径
    ctx.closePath();
    // 绘制路径
    ctx.stroke();
</script>

如果要绘制填充的话,可以使用fill()方法,如下:

<canvas width="400" height="400" id="canvas" style="border:1px solid #333;"></canvas>
<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    // 开始路径绘制
    ctx.beginPath();
    // 将画笔移动到原点
    ctx.moveTo(10, 10);
    // 绘制一条线
    ctx.lineTo(200,10);
    // 再绘制一条线
    ctx.lineTo(10,200);
    // 关闭路径
    ctx.closePath();
    // 绘制路径
    ctx.fill();
</script>
<canvas width="400" height="400" id="canvas" style="border:1px solid #333;"></canvas>
<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.rect(10, 10, 220, 120);
    ctx.closePath();
    ctx.stroke();

</script>

使用 context.arc(x,y,r,sAngle,eAngle,counterclockwise);来绘制圆

参数分别是 圆心 x,y, 半径 r ,起始角度 sAngle, 结束角度 eAngle, 是否逆时针(默认是false)

值得注意的是,起始角度与结束角度是固定的,不管是顺时针还是逆时针,都是右0或2,下1/2,左1,上3/2

<canvas width="400" height="400" id="canvas" style="border:1px solid #333;"></canvas>
<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    // 绘制一个圆点在(200,200)半径:100,从0度顺时针绘制到3/2*PI的圆弧
    ctx.arc(200, 200, 100, 0, 3/2 * Math.PI, false);
    // 如果关闭路径,绘制后就是一个封闭的图形
    // ctx.closePath();
    // 同样的,fill() 表示填充
    ctx.stroke();
</script>

换成逆时针画

<canvas width="400" height="400" id="canvas" style="border:1px solid #333;"></canvas>
<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.arc(200, 200, 100, 0, 3/2 * Math.PI, true);
    ctx.stroke();
</script>

使用 fillText(text, x, y) 函数绘制文字

<canvas width="400" height="400" id="canvas" style="border:1px solid #333;"></canvas>
<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    ctx.fillText("hello", 100, 100);

</script>

对齐&样式

canvas绘制时,可以像css那样设置对齐方式和线条颜色大小等

<canvas width="400" height="400" id="canvas" style="border:1px solid #333;"></canvas>
<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    ctx.fillStyle = 'red';
    ctx.beginPath();
    ctx.moveTo(10,10);
    ctx.lineTo(10, 200);
    ctx.lineTo(200, 10);
    ctx.closePath();
    ctx.fill();

    ctx.strokeStyle = 'blue';
    ctx.lineWidth = 4;
    ctx.beginPath();
    ctx.arc(220, 220, 50, 0, Math.PI*2);
    ctx.closePath();
    ctx.stroke();

    ctx.fillStyle = "green";
    ctx.font = "20px Arial";
    ctx.textBaseline = 'middle'; // 垂直方向
    ctx.textAlign = 'center'; // 水平方向
    ctx.fillText("Hello World", 330, 330);

</script>

canvas绘制原点是左上角(0,0),可以通过 translate(x,y) 将原点移动到指定位置

<canvas width="400" height="400" id="canvas" style="border:1px solid #333;"></canvas>
<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    // 将原点移动到画布中心
    ctx.translate(canvas.width/2, canvas.height/2);
    ctx.beginPath();
    // 此时起始点就是画布中心了
    ctx.rect(0, 0, 150, 100);
    ctx.closePath();
    ctx.stroke();

</script>

使用 scale(x,y) 函数缩放

<canvas width="400" height="400" id="canvas" style="border:1px solid #333;"></canvas>
<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    ctx.scale(0.5, 0.5);
    ctx.beginPath();
    ctx.rect(0, 0, 200, 200);
    ctx.closePath();
    ctx.stroke();

</script>

画布是400x400的,代码画的是一个200x200大小的矩形,但在画之前先将画布缩小了一半,所以绘制的矩形也变成了100x100的了

旋转画布的方法是使用 rotate(deg) 函数

旋转是参数是弧度,所以需要将角度转换成弧度,转换方法是 deg * Math.PI / 180

比如要旋转10度,那么可以使用 rotate(10 * Math.PI / 180)

<canvas width="400" height="400" id="canvas" style="border:1px solid #333;"></canvas>
<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.rect(100, 100, 200, 200);
    ctx.closePath();
    ctx.stroke();

    ctx.rotate(10 * Math.PI / 180);
    ctx.beginPath();
    ctx.rect(100, 100, 200, 200);
    ctx.closePath();
    ctx.stroke();

</script>

保存画布状态与恢复

上面列举的位移,缩放,旋转都是对画布进行操作,直接会对后面再绘制的图形有影响

比如平移完后绘制完,还要再平移回初始的状态,这样才不会对后面绘制有影响,旋转,缩放也是一样,这样操作就会很麻烦

可以在使用位移,缩放,旋转这些操作之前,先保存一下画布的状态,绘制完后,再恢复到保存的状态,就不会对后面的绘制有影响了

<canvas width="400" height="400" id="canvas" style="border:1px solid #333;"></canvas>
<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    ctx.save();
    ctx.strokeStyle="blue";
    ctx.translate(200, 200);
    ctx.rotate(10 * Math.PI / 180);
    ctx.beginPath();
    ctx.rect(-100, -100, 200, 200);
    ctx.closePath();
    ctx.stroke();
    ctx.restore();

    ctx.save();
    ctx.beginPath();
    ctx.rect(100, 100, 200, 200);
    ctx.closePath();
    ctx.stroke();
    ctx.restore();
</script>

有了上面这些知识就可以写出来罗盘钟了,完整代码如下

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body, html {height: 100%;}
    </style>
</head>

<body>

    <canvas id="clock" width="800" height="800"></canvas>

    <script>
        (function () {
            var canvas = document.getElementById('clock');
            var ctx = canvas.getContext('2d');
            var cwidth = canvas.width,cheight = canvas.height;

            var init = false;
            var seconds = ["零秒", "一秒", "二秒", "三秒", "四秒", "五秒", "六秒", "七秒", "八秒", "九秒", "十秒", "十一秒", "十二秒", "十三秒", "十四秒", "十五秒", "十六秒", "十七秒", "十八秒", "十九秒", "二十秒", "二十一秒", "二十二秒", "二十三秒", "二十四秒", "二十五秒", "二十六秒", "二十七秒", "二十八秒", "二十九秒", "三十秒", "三十一秒", "三十二秒", "三十三秒", "三十四秒", "三十五秒", "三十六秒", "三十七秒", "三十八秒", "三十九秒", "四十秒", "四十一秒", "四十二秒", "四十三秒", "四十四秒", "四十五秒", "四十六秒", "四十七秒", "四十八秒", "四十九秒", "五十秒", "五十一秒", "五十二秒", "五十三秒", "五十四秒", "五十五秒", "五十六秒", "五十七秒", "五十八秒", "五十九秒"];
            var minutes = ["零分", "一分", "二分", "三分", "四分", "五分", "六分", "七分", "八分", "九分", "十分", "十一分", "十二分", "十三分", "十四分", "十五分", "十六分", "十七分", "十八分", "十九分", "二十分", "二十一分", "二十二分", "二十三分", "二十四分", "二十五分", "二十六分", "二十七分", "二十八分", "二十九分", "三十分", "三十一分", "三十二分", "三十三分", "三十四分", "三十五分", "三十六分", "三十七分", "三十八分", "三十九分", "四十分", "四十一分", "四十二分", "四十三分", "四十四分", "四十五分", "四十六分", "四十七分", "四十八分", "四十九分", "五十分", "五十一分", "五十二分", "五十三分", "五十四分", "五十五分", "五十六分", "五十七分", "五十八分", "五十九分"];
            let hours = ["十二时", "一时", "二时", "三时", "四时", "五时", "六时", "七时", "八时", "九时", "十时", "十一时"]
            let apm = ["上午", "下午"];
            let weeks = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
            let days = ["一号", "二号", "三号", "四号", "五号", "六号", "七号", "八号", "九号", "十号", "十一号", "十二号", "十三号", "十四号", "十五号", "十六号", "十七号", "十八号", "十九号", "二十号", "二十一号", "二十二号", "二十三号", "二十四号", "二十五号", "二十六号", "二十七号", "二十八号", "二十九号", "三十号", "三十一号"];
            let months = ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"];

            var secondoffset = 0, minuteoffset = 0, houroffset = 0, apoffset = 0, weekoffset = 0, dayoffset = 0, monthoffset = 0;
            var lastSecond = 0, lastMinute = 0, lastHour = 0, lastAp = 0, lastWeek = 0, lastDay = 0, lastMonth = 0;
            var lastUpdateTime = 0, fps = 0, lastfps = 0, lastFpsTime = 0;

            function getTime() {
                var now = new Date();
                var second = now.getSeconds();
                var minute = now.getMinutes();
                var hour = now.getHours();
                var ap = hour >= 12 ? 1 : 0;
                var week = now.getDay();
                var day = now.getDate();
                var month = now.getMonth();
                var year = now.getFullYear() + "年";
                hour = hour >= 12 ? hour - 12 : hour;
                return {
                    second: second,
                    minute: minute,
                    hour: hour,
                    ap: ap,
                    week: week,
                    day: day - 1,
                    month: month,
                    year: year
                }
            }

            function drawSecond(offset) {
                var radius = 350;
                var deg = 360 / seconds.length;
                var index = 0;
                for (var i = 0; i < seconds.length; i++) {
                    index = lastSecond + i;
                    if (index >= seconds.length) {
                        index -= seconds.length;
                    }
                    ctx.save();
                    var x = Math.cos((deg * i + offset) * Math.PI / 180) * radius + cwidth/2;
                    var y = Math.sin((deg * i + offset) * Math.PI / 180) * radius + cheight/2;
                    ctx.translate(x, y)
                    ctx.rotate((deg * i + offset) * Math.PI / 180);
                    ctx.fillText(seconds[index], 0, 0);
                    ctx.restore();
                }
            }
            function drawMinute(offset) {
                var radius = 300;
                var deg = 360 / minutes.length;
                var index = 0;
                for (var i = 0; i < minutes.length; i++) {
                    index = lastMinute + i;
                    if (index >= minutes.length) {
                        index -= minutes.length;
                    }
                    ctx.save();
                    var x = Math.cos((deg * i + offset) * Math.PI / 180) * radius + cwidth/2;
                    var y = Math.sin((deg * i + offset) * Math.PI / 180) * radius + cheight/2;
                    ctx.translate(x, y)
                    ctx.rotate((deg * i + offset) * Math.PI / 180);
                    ctx.fillText(minutes[index], 0, 0);
                    ctx.restore();
                }
            }
            function drawHour(offset) {
                var radius = 260;
                var deg = 360 / hours.length;
                var index = 0;
                for (var i = 0; i < hours.length; i++) {
                    index = lastHour + i;
                    if (index >= hours.length) {
                        index -= hours.length;
                    }
                    ctx.save();
                    var x = Math.cos((deg * i + offset) * Math.PI / 180) * radius + cwidth/2;
                    var y = Math.sin((deg * i + offset) * Math.PI / 180) * radius + cheight/2;
                    ctx.translate(x, y)
                    ctx.rotate((deg * i + offset) * Math.PI / 180);
                    ctx.fillText(hours[index], 0, 0);
                    ctx.restore();
                }
            }
            function drawAp(offset) {
                var radius = 220;
                var deg = 360 / apm.length;
                var index = 0;
                for (var i = 0; i < apm.length; i++) {
                    index = lastAp + i;
                    if (index >= apm.length) {
                        index -= apm.length;
                    }
                    ctx.save();
                    var x = Math.cos((deg * i + offset) * Math.PI / 180) * radius + cwidth/2;
                    var y = Math.sin((deg * i + offset) * Math.PI / 180) * radius + cheight/2;
                    ctx.translate(x, y)
                    ctx.rotate((deg * i + offset) * Math.PI / 180);
                    ctx.fillText(apm[index], 0, 0);
                    ctx.restore();
                }
            }
            function drawWeek(offset) {
                var radius = 180;
                var deg = 360 / weeks.length;
                var index = 0;
                for (var i = 0; i < weeks.length; i++) {
                    index = lastWeek + i;
                    if (index >= weeks.length) {
                        index -= weeks.length;
                    }
                    ctx.save();
                    var x = Math.cos((deg * i + offset) * Math.PI / 180) * radius + cwidth/2;
                    var y = Math.sin((deg * i + offset) * Math.PI / 180) * radius + cheight/2;
                    ctx.translate(x, y)
                    ctx.rotate((deg * i + offset) * Math.PI / 180);
                    ctx.fillText(weeks[index], 0, 0);
                    ctx.restore();
                }
            }
            function drawDay(offset) {
                var radius = 130;
                var deg = 360 / days.length;
                var index = 0;
                for (var i = 0; i < days.length; i++) {
                    index = lastDay + i;
                    if (index >= days.length) {
                        index -= days.length;
                    }
                    ctx.save();
                    var x = Math.cos((deg * i + offset) * Math.PI / 180) * radius + cwidth/2;
                    var y = Math.sin((deg * i + offset) * Math.PI / 180) * radius + cheight/2;
                    ctx.translate(x, y)
                    ctx.rotate((deg * i + offset) * Math.PI / 180);
                    ctx.fillText(days[index], 0, 0);
                    ctx.restore();
                }
            }
            function drawMonth(offset) {
                var radius = 80;
                var deg = 360 / months.length;
                var index = 0;
                for (var i = 0; i < months.length; i++) {
                    index = lastMonth + i;
                    if (index >= months.length) {
                        index -= months.length;
                    }
                    ctx.save();
                    var x = Math.cos((deg * i + offset) * Math.PI / 180) * radius + cwidth/2;
                    var y = Math.sin((deg * i + offset) * Math.PI / 180) * radius + cheight/2;
                    ctx.translate(x, y)
                    ctx.rotate((deg * i + offset) * Math.PI / 180);
                    ctx.fillText(months[index], 0, 0);
                    ctx.restore();
                }
            }

            function drawTime() {
                var data = getTime();
                var hour = data.hour;
                var minute = data.minute;
                var second = data.second;
                var time = (hour > 9 ? hour : "0" + hour) + ":" + (minute > 9 ? minute : "0" + minute) + ":" + (second > 9 ? second : "0" + second);
                ctx.fillText(time, 20, 20);
            }

            function drawYear() {
                var data = getTime();
                var year = data.year;
                ctx.fillText(year, cwidth/2, cheight/2);
            }

            function drawFps() {
                fps = calcFps();
                if (lastFpsTime >= 30) {
                    lastfps = fps;
                    lastFpsTime = 0;
                }
                ctx.fillText("FPS: " + lastfps, cwidth-30, 20);
            }

            function drawLine() {
                ctx.lineWidth = 1;
                ctx.strokeStyle = "#bbb";
                ctx.beginPath();
                ctx.moveTo(30, cheight/2);
                ctx.lineTo(cwidth-30, cheight/2);
                ctx.closePath();
                ctx.stroke();

                ctx.lineWidth = 1;
                ctx.strokeStyle = "#bbb";
                ctx.beginPath();
                ctx.moveTo(cwidth/2, 30);
                ctx.lineTo(cwidth/2, cheight-30);
                ctx.closePath();
                ctx.stroke();
            }

            function render(secondoffset, minuteoffset, houroffset, apoffset, weekoffset, dayoffset, monthoffset) {
                ctx.clearRect(0, 0, cwidth, cheight);
                ctx.textBaseline = 'middle';
                ctx.textAlign = 'center';
                drawLine();
                drawTime();
                drawYear();
                drawSecond(secondoffset);
                drawMinute(minuteoffset);
                drawHour(houroffset);
                drawAp(apoffset);
                drawWeek(weekoffset);
                drawDay(dayoffset);
                drawMonth(monthoffset);

                drawFps();
            }

            function calcFps() {
                let now = Date.now();
                let fps = (1000/(now - lastUpdateTime)).toFixed();
                lastUpdateTime = now;
                return fps;
            }

            function update() {
                lastFpsTime++;

                var data = getTime();
                var currentSecond = data.second;
                var currentMinute = data.minute;
                var currentHour = data.hour;
                var currentAp = data.ap;
                var currentWeek = data.week;
                var currentDay = data.day;
                var currentMonth = data.month;
                var currentYear = data.year;

                if (currentSecond != lastSecond) {
                    secondoffset -= 360 / seconds.length / 60 * 2;
                    if (secondoffset < -360 / seconds.length) {
                        lastSecond = currentSecond;
                        secondoffset = 0;
                    }
                }
                if (currentMinute != lastMinute) {
                    minuteoffset -= 360 / minutes.length / 60 * 2;
                    if (minuteoffset < -360 / minutes.length) {
                        lastMinute = currentMinute;
                        minuteoffset = 0;
                    }
                }
                if (currentHour != lastHour) {
                    houroffset -= 360 / hours.length / 60 * 2;
                    if (houroffset < -360 / hours.length) {
                        lastHour = currentHour;
                        houroffset = 0;
                    }
                }
                if (currentAp != lastAp) {
                    apoffset -= 360 / apm.length / 60 * 2;
                    if (apoffset < -360 / apm.length) {
                        lastAp = currentAp;
                        apoffset = 0;
                    }
                }
                if (currentWeek != lastWeek) {
                    weekoffset -= 360 / weeks.length / 60 * 2;
                    if (weekoffset < -360 / weeks.length) {
                        lastWeek = currentWeek;
                        weekoffset = 0;
                    }
                }
                if (currentDay != lastDay) {
                    dayoffset -= 360 / days.length / 60 * 2;
                    if (dayoffset < -360 / days.length) {
                        lastDay = currentDay;
                        dayoffset = 0;
                    }
                }
                if (currentMonth != lastMonth) {
                    monthoffset -= 360 / months.length / 60 * 2;
                    if (monthoffset < -360 / months.length) {
                        lastMonth = currentMonth;
                        monthoffset = 0;
                    }
                }
                render(secondoffset, minuteoffset, houroffset, apoffset, weekoffset, dayoffset, monthoffset);

                requestAnimationFrame(update);
            }

            window.requestAnimationFrame = window.requestAnimationFrame
                || window.mozRequestAnimationFrame
                || window.webkitRequestAnimationFrame
                || window.msRequestAnimationFrame
                || function (callback) {
                    setTimeout(callback, 1000 / 60);
                };

            update();
        })();
    </script>
</body>

</html>


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK