目錄
知識點和求解參考圖
canvas畫時鐘效果的程式碼寫
首頁 web前端 H5教程 使用h5 canvas實現時鐘的動態效果

使用h5 canvas實現時鐘的動態效果

Jul 17, 2018 pm 02:37 PM
canvas html5

canvas 繪製好時鐘介面,使用定時器定時重繪整個canvas,就實現了模擬動態時鐘的效果。

困難在於:

  • 秒鐘刻度與時脈刻度的繪製

  • 整點文字沿著內邊圓形環繞

其中刻度的環繞並不難計算,文字的環繞就比較坑爹了,canvas繪製的文字是在繪製坐標之上的(文字基線和對齊方式影響),需要進行偏移的計算,使之文字中點正好落在圓上。
這一步相當淡疼,由於api中並沒有測量字高的辦法,而使用fontSize,其實也並不是字的準確高度,因此,y坐標二分之一行高向下偏移,使之垂直居中,卻總是不夠準確的。而x座標 二分之一行寬向左偏移,使之水平居中,則沒有這個問題,因為api提供了測量行寬的方法。

一切都是因為 ctx.measureText(text).width 存在,但 ctx.measureText(numText).height 不存在。列印測量結果,也只有一個寬度屬性。文檔中說canvas對於繪製文字的支援比較弱,從這一點上看 何止是弱。

直接設定基線和對齊方式為居中,似乎也存在一定誤差,看起來總不是那麼賞心悅目。下面的程式碼中兩種方式都寫了。

會走的時鐘預覽:

使用h5 canvas實現時鐘的動態效果

時間顯示可能略有誤差。

知識點和求解參考圖

主要知識點為圓的座標公式,和三角函數sin,cos計算。實際上,圓的座標公式使用的不多,引入求值反而可能複雜化。

下圖是計算刻度線座標和整點文字繪製座標的參考圖:

使用h5 canvas實現時鐘的動態效果

canvas畫時鐘效果的程式碼寫

下面是全部程式碼:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=1024, height=768,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
    <title>时钟</title></head><body><p style="margin: 50px">
    <canvas width="300" height="300"></canvas>
    <canvas width="200" height="200" style="background-color: #bbbbbb"></canvas>
    <canvas width="200" height="200"></canvas>
    <canvas width="200" height="200"></canvas></p><script>

    var clockHelper = function (canvas, config) {
        if (!config) {
            config = {}
        }        var ctx = canvas.getContext(&#39;2d&#39;);        var deColor = config.deColor ? config.deColor : &#39;#333333&#39;;        var deConfig = {
            ringWidth: config.ringWidth ? config.ringWidth : 6,//圆环宽度
            ringColor: config.ringColor ? config.ringColor : deColor,//圆环颜色
            hSaleL: config.hSaleL ? config.hSaleL : 8,//时刻度线长
            hScaleWidth: config.hScaleWidth ? config.hScaleWidth : 4,//时刻度线宽
            hScaleColor: config.hScaleColor ? config.hScaleColor : deColor,//时刻度颜色
            msSaleL: config.msSaleL ? config.msSaleL : 4,//分秒刻度线长
            msScaleWidth: config.msScaleWidth ? config.msScaleWidth : 2,//分秒刻度线宽
            msScaleColor: deColor,//分秒刻度颜色
            hFontSize: config.hFontSize ? config.hFontSize : 18,//整点文字大小
            hHandWidth: config.hHandWidth ? config.hHandWidth : 10,//时针宽度
            mHandWidth: config.mHandWidth ? config.mHandWidth : 5,//分针宽度
            sHandWidth: config.sHandWidth ? config.sHandWidth : 2,//秒针宽度

            hHandColor: config.hHandColor ? config.hHandColor : deColor,//时针颜色
            mHandColor: config.mHandColor ? config.mHandColor : deColor,//分针颜色
            sHandColor: config.sHandColor ? config.sHandColor : &#39;#bb3333&#39;,//秒针颜色
            handMode: [&#39;ms&#39;, &#39;s&#39;].indexOf("" + config.handMode) !== -1 ? config.handMode : &#39;s&#39;,//指针读秒模式,ms:毫秒,s:秒。
            clockFaceColor: config.clockFaceColor ? config.clockFaceColor : &#39;&#39;,
        };        var ox = canvas.width / 2;        var oy = canvas.height / 2;        var width = canvas.width;        var height = canvas.height;

        ctx.font = deConfig.hFontSize + "px 黑体";        //中线圆环半径
        var ringR = (width < height) ? (width / 2 - deConfig.ringWidth / 2) : (height / 2 - deConfig.ringWidth / 2);        //内圆环半径
        var ringInnerR = (width < height) ? (width / 2 - deConfig.ringWidth) : (height / 2 - deConfig.ringWidth);        var timer;        var timeSleep = 100;        var isStart = false;        function start() {
            if (isStart) {                return;
            }
            isStart = true;            if (deConfig.handMode == &#39;ms&#39;) {
                timeSleep = 100;
            } else {
                timeSleep = 1000;
            }

            ctx.clearRect(0, 0, width, height);
            draw();

            timer = setInterval(function () {
                if (isStart) {
                    ctx.clearRect(0, 0, width, height);
                    draw();
                }
            }, timeSleep);

        }        function stop() {
            isStart = false;
            clearInterval(timer)
        }        function draw() {

            beforeDraw();

            drawCircleFace();
            drawHands();

            afterDraw();

        }        function drawCircleFace() {

            ctx.fillStyle = deConfig.ringColor;
            ctx.strokeStyle = deConfig.ringColor;

            ctx.lineWidth = deConfig.ringWidth;
            ctx.beginPath();
            ctx.arc(ox, oy, ringR, 0, Math.PI * 2);
            ctx.stroke();            if (deConfig.clockFaceColor) {
                ctx.fillStyle = deConfig.clockFaceColor;
                ctx.fill();
            }            var x1 = ox;            var y1 = oy;            var x2 = ox;            var y2 = oy;            var radin = 0;
            ctx.lineWidth = deConfig.hScaleWidth;            // ctx.beginPath();
            for (var i = 1; i <= 60; i++) {
                radin = i * 6 * Math.PI / 180;
                x1 = ox + ringInnerR * Math.sin(radin);
                y1 = oy - ringInnerR * Math.cos(radin);                if (i % 5 === 0) {
                    ctx.lineWidth = deConfig.hScaleWidth;
                    x2 = ox + (ringInnerR - deConfig.hSaleL) * Math.sin(radin);
                    y2 = oy - (ringInnerR - deConfig.hSaleL) * Math.cos(radin);

                    ctx.fillStyle = deConfig.hScaleColor;                    var numText = i / 5 + "";                    var textWidth = ctx.measureText(numText).width;                    var x3 = ox + (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.sin(radin);                    var y3 = oy - (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.cos(radin);
                    ctx.textAlign = &#39;center&#39;;
                    ctx.textBaseline = &#39;middle&#39;;                    //不设置文字居中,基线居中,自己计算。貌似都有误差。因为旋转过程中,角度变化,且文字宽高不尽相同
                    // var x3 = ox + (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.sin(radin) - textWidth / 2;
                    // var y3 = oy - (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.cos(radin) + deConfig.hFontSize/ 2;
                    //x2,y2已经求过,化简为:
                    // var x3 = x2 - deConfig.hFontSize * Math.sin(radin) - textWidth / 2;
                    // var y3 = y2 + deConfig.hFontSize * Math.cos(radin) + textWidth / 2;
                    //文字x轴向左偏移一半文字宽,使之水平居中;向下偏移一半高度,使之垂直居中。
                    // 实际中发现,字高没法测(api无),而使用fontSize不准。但y轴加上字宽,位置倒是更对齐一些。

                    // var x3 = x2 + textWidth / 2;
                    // var y3 = y2 - deConfig.hFontSize / 2;

                    ctx.fillText(numText, x3, y3);

                } else {
                    ctx.lineWidth = deConfig.msScaleWidth;
                    x2 = ox + (ringInnerR - deConfig.msSaleL) * Math.sin(radin);
                    y2 = oy - (ringInnerR - deConfig.msSaleL) * Math.cos(radin);
                }


                ctx.beginPath();
                ctx.moveTo(x1, y1);
                ctx.lineTo(x2, y2);
                ctx.stroke();

            }

        }        //改变坐标中点,并旋转画布也许是更好的选择。
        function drawHands() {
            var date = new Date();            var h = date.getHours() % 12;            var m = date.getMinutes();            var s = date.getSeconds();            var ms = date.getMilliseconds();            // console.log(h + ":" + m + ":" + s);
            //    时针

            var hRadin = (h + m / 60 + s / 3600) * Math.PI * 2 / 12;            var mRadin = (m + s / 60) * Math.PI * 2 / 60;            var sRadin;            if (deConfig.handMode == &#39;ms&#39;) {
                sRadin = (s + ms / 1000) * Math.PI * 2 / 60;
            } else {
                sRadin = s * Math.PI * 2 / 60;
            }            var x = 0;            var y = 0;            var hDotR = deConfig.hHandWidth + 2;            var mDotR = 0.6 * hDotR            var sDotR = 0.5 * hDotR            //秒针半径
            var sHandR = ringInnerR - deConfig.hSaleL * 2
            //分针半径
            var mHandR = 0.8 * sHandR;            //时针半径
            var hHandR = 0.7 * mHandR;            //时针
            ctx.beginPath();
            ctx.lineWidth = deConfig.hHandWidth;
            ctx.strokeStyle = deConfig.hHandColor;
            ctx.strokeStyle = deConfig.hHandColor;
            ctx.moveTo(ox, oy);
            x = ox + hHandR * Math.cos(hRadin - Math.PI / 2);
            y = oy + hHandR * Math.sin(hRadin - Math.PI / 2);
            ctx.lineTo(x, y);
            ctx.stroke();            //针尖。直接圆型了(矩形指针来绘制针尖,计算复杂。。。)
            ctx.beginPath();
            ctx.lineWidth = 0;
            ctx.arc(x, y, deConfig.hHandWidth / 2, 0, 2 * Math.PI);
            ctx.fill();            //中心
            ctx.beginPath();            // ctx.lineWidth = hDotR;
            ctx.arc(ox, oy, hDotR / 2, 0, Math.PI * 2);
            ctx.fill();
            ctx.stroke();            //分针
            ctx.beginPath();
            ctx.lineWidth = deConfig.mHandWidth;
            ctx.strokeStyle = deConfig.mHandColor;
            ctx.fillStyle = deConfig.mHandColor;
            ctx.moveTo(ox, oy);
            x = ox + mHandR * Math.cos(mRadin - Math.PI / 2);
            y = oy + mHandR * Math.sin(mRadin - Math.PI / 2);
            ctx.lineTo(x, y);
            ctx.stroke();            //针尖。直接圆型了(矩形指针来绘制针尖,计算复杂。。。)
            ctx.beginPath();
            ctx.lineWidth = 0;
            ctx.arc(x, y, deConfig.mHandWidth / 2, 0, 2 * Math.PI);
            ctx.fill();            //中心
            ctx.beginPath();
            ctx.arc(ox, oy, mDotR / 2, 0, Math.PI * 2);
            ctx.stroke();            //秒针
            ctx.beginPath();
            ctx.strokeStyle = deConfig.sHandColor;
            ctx.fillStyle = deConfig.sHandColor;
            ctx.lineWidth = deConfig.sHandWidth;            //秒针有长短两线
            x = ox - sHandR / 4 * Math.cos(sRadin - Math.PI / 2);
            y = oy - sHandR / 4 * Math.sin(sRadin - Math.PI / 2);
            ctx.moveTo(x, y);
            x = ox + sHandR * Math.cos(sRadin - Math.PI / 2);
            y = oy + sHandR * Math.sin(sRadin - Math.PI / 2);
            ctx.lineTo(x, y);
            ctx.stroke();            //针尖。直接圆型了(矩形指针来绘制针尖,计算复杂。。。)
            ctx.beginPath();
            ctx.lineWidth = 0;
            ctx.arc(x, y, deConfig.sHandWidth / 2, 0, 2 * Math.PI);
            ctx.fill();            //中心
            ctx.beginPath();
            ctx.fillStyle = deColor;
            ctx.arc(ox, oy, sDotR, 0, Math.PI * 2);
            ctx.fill();
            ctx.stroke();

        }        function beforeDraw() {
            if (typeof exp.beforeDraw === &#39;function&#39;) {
                exp.beforeDraw(ctx, deConfig);
            }
        }        function afterDraw() {
            if (typeof exp.afterDraw === &#39;function&#39;) {
                exp.afterDraw(ctx, deConfig);
            }
        }        var exp = {
            start: start,
            stop: stop,
            beforeDraw: null,
            afterDraw: null,
        }        return exp;


    }    var clockCanvas1 = document.getElementsByTagName(&#39;canvas&#39;)[0];    var clockCanvas2 = document.getElementsByTagName(&#39;canvas&#39;)[1];    var clockCanvas3 = document.getElementsByTagName(&#39;canvas&#39;)[2];    var clockCanvas4 = document.getElementsByTagName(&#39;canvas&#39;)[3];    var clock = clockHelper(clockCanvas1, {mHandColor: &#39;#3333bb&#39;, sHandColor: &#39;#bb3333&#39;});
    clock.start();
    setTimeout(function () {
        clock.stop()
    }, 5000)
    setTimeout(function () {
        clock.start();
    }, 8000)

    clockHelper(clockCanvas2, {
        mHandColor: &#39;green&#39;,
        hHandWidth: 6,
        mHandWidth: 4,
        hFontSize: 14,
        hScaleWidth: 2,
        handMode: &#39;ms&#39;
    }).start();


    clockHelper(clockCanvas2, {
        mHandColor: &#39;green&#39;,
        hHandWidth: 6,
        mHandWidth: 4,
        hFontSize: 14,
        hScaleWidth: 2,
        handMode: &#39;ms&#39;
    }).start();


    clockHelper(clockCanvas3, {
        deColor: &#39;#bbbbbb&#39;,
        sHandColor: &#39;#bbbbbb&#39;,
        clockFaceColor: &#39;#112233&#39;,//钟面
        hHandWidth: 6,
        mHandWidth: 4,
        hFontSize: 14,
        hScaleWidth: 2,
        handMode: &#39;s&#39;
    }).start();    var clock4 = clockHelper(clockCanvas4, {
        deColor: &#39;#bbbbbb&#39;,
        sHandColor: &#39;#bbbbbb&#39;,        // clockFaceColor: &#39;#112233&#39;,
        hHandWidth: 6,
        mHandWidth: 4,
        hFontSize: 14,
        hScaleWidth: 2,
        handMode: &#39;s&#39;
    });

    clock4.afterDraw = function (ctx, deConfig) {
        var grd = ctx.createLinearGradient(0, 0, clockCanvas4.width, clockCanvas4.height);
        grd.addColorStop(0, "rgba(255,0,0,0.3)");
        grd.addColorStop(1, "rgba(0,0,255,0.5)");
        ctx.fillStyle = grd;
        ctx.arc(clockCanvas4.width/2,clockCanvas4.height/2,clockCanvas4.width/2,0,Math.PI*2);        // ctx.fillRect(0, 0, clockCanvas4.width, clockCanvas4.height);
        ctx.fill();

        ctx.fillText(&#39;时钟绘制完成后,自定义其他绘制&#39;,clockCanvas4.width/2,clockCanvas4.height - deConfig.hFontSize);
    };

    clock4.start();</script></body></html>
登入後複製

說明:

1、clockHelper第一個參數傳入畫布。第二個參數傳入時鐘介面的配置對象,包括指針、刻度的顏色、大小等,配置項和clockHelper中的deConfig預設對像是相對的,參考deConfig的屬性傳入參數即可。

2、clockHelper的封裝性略差,僅是基本能用型。但屬性不多,改造應該不難。

3、提供了時鐘介面繪製之前和之後的方法,可以在beforeDraw和afterDraw這兩個方法中執行自己的邏輯。但由於事先設計沒有留出足夠的空白像素,用途不大。只能進行一些簡單的再繪製。例如給鐘面添加色彩、漸層。

相關推薦:

H5 C3實作時鐘效果

使用Canvas製作時鐘動畫的方法

以上是使用h5 canvas實現時鐘的動態效果的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

HTML 中的表格邊框 HTML 中的表格邊框 Sep 04, 2024 pm 04:49 PM

HTML 表格邊框指南。在這裡,我們以 HTML 中的表格邊框為例,討論定義表格邊框的多種方法。

HTML 中的巢狀表 HTML 中的巢狀表 Sep 04, 2024 pm 04:49 PM

這是 HTML 中巢狀表的指南。這裡我們討論如何在表中建立表格以及對應的範例。

HTML 左邊距 HTML 左邊距 Sep 04, 2024 pm 04:48 PM

HTML 左邊距指南。在這裡,我們討論 HTML margin-left 的簡要概述及其範例及其程式碼實作。

HTML 表格佈局 HTML 表格佈局 Sep 04, 2024 pm 04:54 PM

HTML 表格佈局指南。在這裡,我們詳細討論 HTML 表格佈局的值以及範例和輸出。

HTML 輸入佔位符 HTML 輸入佔位符 Sep 04, 2024 pm 04:54 PM

HTML 輸入佔位符指南。在這裡,我們討論 HTML 輸入佔位符的範例以及程式碼和輸出。

HTML 有序列表 HTML 有序列表 Sep 04, 2024 pm 04:43 PM

HTML 有序列表指南。在這裡我們也分別討論了 HTML 有序列表和類型的介紹以及它們的範例

在 HTML 中移動文字 在 HTML 中移動文字 Sep 04, 2024 pm 04:45 PM

HTML 中的文字移動指南。在這裡我們討論一下marquee標籤如何使用語法和實作範例。

HTML onclick 按鈕 HTML onclick 按鈕 Sep 04, 2024 pm 04:49 PM

HTML onclick 按鈕指南。這裡我們分別討論它們的介紹、工作原理、範例以及各個事件中的onclick事件。

See all articles