使用h5 canvas实现时钟的动态效果

canvas 绘制好时钟界面,使用定时器定时重绘整个canvas,就实现了仿真动态时钟的效果。

难点在于:

  • 秒钟刻度和时钟刻度的绘制

  • 整点文字沿着内边圆形环绕

其中刻度的环绕并不难计算,文字的环绕就比较坑爹了,canvas绘制的文字是在绘制坐标之上的(文字基线和对齐方式影响),需要进行偏移的计算,使之文字中点正好落在圆上。
这一步相当淡疼,由于api中并没有测量字高的办法,而使用fontSize,其实也并不是字的准确高度,因此,y坐标+二分之一行高向下偏移,使之垂直居中,却总是不够准确的。而x坐标+二分之一行宽向左偏移,使之水平居中,则没有这个问题,因为api提供了测量行宽的方法。

一切都是因为 ctx.measureText(text).width 存在,但 ctx.measureText(numText).height 不存在。打印测量结果,也只有一个宽度属性。文档中说canvas对于绘制文字的支持比较弱,从这一点上看 何止是弱。

直接设置基线和对齐方式为居中,似乎也存在一定误差,看起来总不是那么赏心悦目。下面的代码中两种方式都写了。

会走的时钟预览:

这里写图片描述

时间显示可能略有误差。

知识点和求解参考图

主要知识点为圆的坐标公式,和三角函数sin,cos计算。实际上,圆的坐标公式使用的并不多,引入求值反而可能复杂化。

下图是计算刻度线坐标和整点文字绘制坐标的参考图:

这里写图片描述

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('2d');        var deColor = config.deColor ? config.deColor : '#333333';        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 : '#bb3333',//秒针颜色
            handMode: ['ms', 's'].indexOf("" + config.handMode) !== -1 ? config.handMode : 's',//指针读秒模式,ms:毫秒,s:秒。
            clockFaceColor: config.clockFaceColor ? config.clockFaceColor : '',
        };        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 == 'ms') {
                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 = 'center';
                    ctx.textBaseline = 'middle';                    //不设置文字居中,基线居中,自己计算。貌似都有误差。因为旋转过程中,角度变化,且文字宽高不尽相同
                    // 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 == 'ms') {
                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 === 'function') {
                exp.beforeDraw(ctx, deConfig);
            }
        }        function afterDraw() {
            if (typeof exp.afterDraw === 'function') {
                exp.afterDraw(ctx, deConfig);
            }
        }        var exp = {
            start: start,
            stop: stop,
            beforeDraw: null,
            afterDraw: null,
        }        return exp;


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

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


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


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

    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('时钟绘制完成后,自定义其他绘制',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实现时钟的动态效果的详细内容,更多请关注编程之家其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


HTML5和CSS3实现3D展示商品信息的代码
利用HTML5中的Canvas绘制笑脸的代码
Html5剪切板功能的实现
如何通过HTML5触摸事件实现移动端简易进度条
Html5移动端获奖无缝滚动动画实现
关于HTML5和CSS3实现机器猫的代码
HTML5使用DOM进行自定义控制
使用HTML5 Canvas绘制阴影效果的方法
使用PHP和HTML5 FormData实现无刷新文件上传
如何解决HTML5 虚拟键盘出现挡住输入框的问题
HTML5中div和section以及article的区别分析
html5和CSS 实现禁止IOS长按复制粘贴功能
html5 touch事件实现触屏页面上下滑动
canvas 模拟实现电子彩票刮刮乐的代码
HTML5 Plus 实现手机APP拍照或相册选择图片上传的功能
Android自定义环形LoadingView效果
HTML5 canvas绘制五角星的方法
html5使用html2canvas实现浏览器截图
使用Canvas处理图片的方法介绍
利用Canvas模仿百度贴吧客户端loading小球的方法