0

0

使用Canvas制作时钟动画的方法

一个新手

一个新手

发布时间:2017-10-16 09:51:02

|

1913人浏览过

|

来源于php中文网

原创

  复习javascript到canvas的知识点,看到一个使用canvas绘制的静态时钟例子,便想将其变成动态显示系统时间的时钟动画。另外再配上数字显示的时钟,一个小的时钟模块的诞生了!目前的界面还比较粗糙,只有简单的界面和动画效果。

  这个时钟包括两个部分,动画圆盘时钟和数字时钟。首先是使用超时调用setTimeout()方法做一个循环动画的效果以显示时间。先看数字时钟的代码,比较简单,圆盘时钟的做法也是模仿数字时钟做的。


var ntimeoutId = setTimeout(ntimeOut,0);function ntimeOut() {
    clearTimeout(ntimeoutId);    
    var now = new Date();    
    var hours = now.getHours().toString(),
        minutes = now.getMinutes().toString(),
        seconds = now.getSeconds().toString();    
        var time = hours+" : "+minutes+" : "+seconds;    
        var timep = document.getElementById("time");
    timep.innerHTML = time;
    ntimeoutId = setTimeout(ntimeOut, 1000);
}

  1、首先是设定一个超时调用以首次调用方法来显示系统当前时间,时间之所以设置为0,是为了没有延迟地显示时间。

  2、每次循环之前清除前一次的超时调用(为什么要这样做,我也还不清楚???可能是为了内存性能相关,注释了以后程序也能正常执行。)

  3、取得当前系统时间,并按照一定的字符串格式保存。

  4、为了动态显示在页面中,在Html页面中定义了一个空的p元素,以存放该时间字符串。使用DOM操作将其添加到p元素中即可动态显示。

  5、通过不停循环超时调用,就可以动态显示数字时钟了, 同过开发者工具也可以看到p元素里的动态变化。

  接下来就是制作圆盘时钟动画,圆盘和数值的刻画都比较简单,只要使用context.arc()方法和context.fillText()方法即可。下面是其源代码:


     context.beginPath();
        context.restore();
        context.translate(0,0);
        context.clearRect(0,0,300,300);        //绘制时钟内外边框
        context.arc(150,150,149,0,2 * Math.PI,false);
        context.moveTo(295,150);
        context.arc(150,150,145,0,2 * Math.PI,false);

        context.font = "bold 18px Arial";
        context.textAlign = "center";        //绘制时钟表盘数值
        context.fillText("12",150,25);
        context.fillText("3",285,150);
        context.fillText("6",150,290);
        context.fillText("9",15,150);
        context.fillText("1",215,45);
        context.fillText("2",265,95);
        context.fillText("4",265,225);
        context.fillText("7",95,275);
        context.fillText("5",215,275);
        context.fillText("8",35,225);
        context.fillText("10",35,95);
        context.fillText("11",75,45);

        context.stroke();
        context.closePath();

  接下来就是指针的绘制了,指针的绘制中参考高程中的做法,使用变换操作context.translate()方法改变原点,再绘制路径成为指针会方便很多。另一个绘制指针的难点是弧度的计算,当然这就是数学问题了。下面先看源代码:


     //绘制指针        context.save();
        context.translate(150,150);        //时针
        context.moveTo(0,0);
        hour(hours);        function hour(thour) {
            context.save();            
            var newhour = 0;            
            if(thour>12) {
                newhour = thour-12;
            } else {
                newhour = thour;
            }
            context.rotate((2*Math.PI/12)*newhour);
            context.lineTo(0,-80);
            context.restore();
        }        //分针
        context.moveTo(0,0);
        minute(minutes);        function minute(tminute) {
            context.save();
            context.rotate((2*Math.PI/12)*tminute/5);
            context.lineTo(0,-110);
            context.restore();
        }        //秒针
        context.moveTo(0,0);
        second(seconds);        function second(tsecond) {
            context.save();
            context.fillStyle = "#fff";
            context.rotate((2*Math.PI/12)*tsecond/5);
            context.lineTo(0,-120);
            context.restore();
        }
        
        context.stroke();

  在绘制指针中,每种指针都使用了函数来改变每次指针绘制的弧度来实现指针转动的动画效果。对于时针,则将二十四小时制转化为十二小时制,每次转动30°即可。分针和秒针则是转为0到11进行转动,简单的数学问题,相信大家都是比我厉害的,当时我还纠结了一阵子。在每个函数中都有使用context.save()方法和context.restore()方法,是为了保存和复原初始化时的路径,不然指针都要跑偏啦。

  所有工作基本准备就绪了,接下来只要将钟盘和指针都放在超时调用中即可,但仍然有些问题需要注意的,比如说钟盘和指针的原点设置不同,要注意使用保存和复原来还原初始化时候的路径,不然钟盘和指针都要跑偏了。另外要注意的是,使用Canvas制作动画,每次的动画循环都是要清空画布重新绘制,不然指针一直转转转,转成一朵花的样子。

  完整的圆盘时钟代码如下:


//显示指针时间var drawing = document.getElementById("drawing");if(drawing.getContext) {    
var context = drawing.getContext("2d");    
var rtimeoutId = setTimeout(roudClock,0);    
function roudClock() {
        clearTimeout(rtimeoutId);

        context.beginPath();
        context.restore();
        context.translate(0,0);
        context.clearRect(0,0,300,300);        //绘制时钟内外边框
                context.arc(150,150,149,0,2 * Math.PI,false);
        context.moveTo(295,150);
        context.arc(150,150,145,0,2 * Math.PI,false);

        context.font = "bold 18px Arial";
        context.textAlign = "center";        //绘制时钟表盘数值
                context.fillText("12",150,25);
        context.fillText("3",285,150);
        context.fillText("6",150,290);
        context.fillText("9",15,150);
        context.fillText("1",215,45);
        context.fillText("2",265,95);
        context.fillText("4",265,225);
        context.fillText("7",95,275);
        context.fillText("5",215,275);
        context.fillText("8",35,225);
        context.fillText("10",35,95);
        context.fillText("11",75,45);

        context.stroke();
        context.closePath();        
        var now = new Date();        
        var hours = now.getHours(),
            minutes = now.getMinutes(),
            seconds = now.getSeconds();        //绘制指针        
            context.save();
        context.translate(150,150);        //时针
                context.moveTo(0,0);
        hour(hours);        
        function hour(thour) {
            context.save();            
            var newhour = 0;            
            if(thour>12) {
                newhour = thour-12;
            } else {
                newhour = thour;
            }
            context.rotate((2*Math.PI/12)*newhour);
                        context.lineTo(0,-80);
            context.restore();
        }        //分针
           context.moveTo(0,0);
        minute(minutes);        
        function minute(tminute) {
            context.save();
            context.rotate((2*Math.PI/12)*tminute/5);
            context.lineTo(0,-110);
            context.restore();
        }        //秒针
                context.moveTo(0,0);
        second(seconds);        
        function second(tsecond) {
            context.save();
            context.fillStyle = "#fff";
            context.rotate((2*Math.PI/12)*tsecond/5);
            context.lineTo(0,-120);
            context.restore();
        }
        
        context.stroke();
        context.restore();
        context.translate(0,0);
        context.save();
        rtimeoutId = setTimeout(roudClock,1000);
    }    
}

  最后总结我在这次Demo的练习中遇到的几点问题:

  1、画布重绘问题

Swiper制作网易智造大气幻灯片
Swiper制作网易智造大气幻灯片

这是使用 Swiper 插件制作的「网易智造」首页宽幅大气幻灯片效果,幻灯片共 6 幅,响应式设计,切换方式位淡入淡出,鼠标移入幻灯片区域显示左右箭头,移出隐藏箭头,同时设置了自动播放、延迟加载图片。

下载

  在指针动画循环的时候,前一个路径都没有方法清除,造成每一次循环都留下印记。尝试了小范围地使用clearRect()方法,结果发现只能在范围内清除了表盘和数字的内容,指针依然会留下痕迹。后来通过搜索,得到的答案是使用Canvas制作动画一定是要进行重绘的,在新绘制内容前要清空画布内容,每一次的动画变化都要清空一次。重绘的方法可以参考一下链接,我使用的是clearRect()方法清空整个画布。

  2、save()方法和restore()方法的使用

  因为我的钟盘和指针的原点设定不同,所以在进行重绘后,钟盘的原点会改变为指针的原点,因此要利用save()方法和restore()方法改变。除了这个地方也有其他一些地方需要用到,适合操作以后改变了设定,但后续操作需要用原设定的情况。注意的是这两个方法只会保存和恢复设定,而不是内容。

  3、translate()方法的使用

  translate()方法是属于变换操作中的,可改变画布的原点,默认画布的原点在画布的左上角。使用这个方法可以更轻易地绘制指针的路径,当然还有一些其他需要用的地方,我还没有接触到。

  4、setTimeout()方法的使用

  使用超时调用是比间歇调用更好的方法,使用超时调用可以模拟间歇调用。通过在超时调用传入的函数中再添加超时调用就可以很好地模仿循环。还可以在函数中根据一些条件限定循环的次数和时间。

  5、对应当前时间,弧度的使用方法

  这就是数学问题,我在这个问题纠结了好一会儿,没有拐过弯来,明白其中的原理就不是技术上的难题了。

  6、对于时间重复取得和重复使用方法的问题

  对于代码中还存在有重复代码的情况,我还没有想到更好的方法减少冗余。例如在获取时间上,在两个超时调用中均有重复定义,但若是把他们放在全局中,则没有了动画效果,只是显示加载完成后的那个静态时间。另外的是指针的函数设定中有重复的部分,是否可以合为一个函数方法再进行调用呢。

  欢迎大家提出想法,对于不足的地方提出建议,一起来交流。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

31

2026.01.28

包子漫画在线官方入口大全
包子漫画在线官方入口大全

本合集汇总了包子漫画2026最新官方在线观看入口,涵盖备用域名、正版无广告链接及多端适配地址,助你畅享12700+高清漫画资源。阅读专题下面的文章了解更多详细内容。

9

2026.01.28

ao3中文版官网地址大全
ao3中文版官网地址大全

AO3最新中文版官网入口合集,汇总2026年主站及国内优化镜像链接,支持简体中文界面、无广告阅读与多设备同步。阅读专题下面的文章了解更多详细内容。

32

2026.01.28

php怎么写接口教程
php怎么写接口教程

本合集涵盖PHP接口开发基础、RESTful API设计、数据交互与安全处理等实用教程,助你快速掌握PHP接口编写技巧。阅读专题下面的文章了解更多详细内容。

1

2026.01.28

php中文乱码如何解决
php中文乱码如何解决

本文整理了php中文乱码如何解决及解决方法,阅读节专题下面的文章了解更多详细内容。

3

2026.01.28

Java 消息队列与异步架构实战
Java 消息队列与异步架构实战

本专题系统讲解 Java 在消息队列与异步系统架构中的核心应用,涵盖消息队列基本原理、Kafka 与 RabbitMQ 的使用场景对比、生产者与消费者模型、消息可靠性与顺序性保障、重复消费与幂等处理,以及在高并发系统中的异步解耦设计。通过实战案例,帮助学习者掌握 使用 Java 构建高吞吐、高可靠异步消息系统的完整思路。

8

2026.01.28

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

23

2026.01.27

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

122

2026.01.26

edge浏览器怎样设置主页 edge浏览器自定义设置教程
edge浏览器怎样设置主页 edge浏览器自定义设置教程

在Edge浏览器中设置主页,请依次点击右上角“...”图标 > 设置 > 开始、主页和新建标签页。在“Microsoft Edge 启动时”选择“打开以下页面”,点击“添加新页面”并输入网址。若要使用主页按钮,需在“外观”设置中开启“显示主页按钮”并设定网址。

51

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Canvas 绘制时钟
Canvas 绘制时钟

共7课时 | 1.5万人学习

HTML5新特性基础视频教程
HTML5新特性基础视频教程

共18课时 | 3.2万人学习

HTML5 Canvas 动画实战教程
HTML5 Canvas 动画实战教程

共28课时 | 6.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号