js Canvas实现圆形时钟教程


Posted in Javascript onSeptember 19, 2016

阅读本文需要一点关于canvas基本用法的基础,本文实例为大家分享了HTML5 Canvas实现圆形时钟简易教程

第一步:新建一个最简单的html文件,并且在<body>标签中定义元素canvas。

canvas.html

<html>
 <head>
 <title>Canvas clock tutorial</title>
 </head>
 <body>
 <canvas id="clock" width="400" height="400"></canvas>
 </body>
</html>

 在这一步完成后,用浏览器打开canvas.html,你会发现什么都看不到,这是因为我们没有在canvas上绘制任何东西,同时也没有为其定义边界。

在canvas .html中为<canvas>添加css样式属性:

<html>
 <head>
 <title>Canvas clock tutorial</title>
 <style type="text/css">
  canvas { border: 1px solid black; }
 </style>
 </head>
 <body>
 <canvas id="clock" width="400" height="400"></canvas>
 </body>
</html>

这样,我们就能看到<canvas>的轮廓。

当没有设置宽度和高度的时候,canvas会初始化宽度为300像素和高度为150像素。该元素可以使用CSS来定义大小,但在绘制时图像会伸缩以适应它的框架尺寸:如果CSS的尺寸与初始画布的比例不一致,它会出现扭曲。

注意: 如果你绘制出来的图像是扭曲的, 尝试在<canvas>的属性中明确规定宽和高,而不是使用CSS。 

第二步:新建实现绘制圆形时钟逻辑的draw.js文件,进行初始化工作。

毫无疑问,要实现时钟,就需要获取系统时间。 

在js语法中,可以利用Date()来实时获取时间。
 var currentTime = new Date();

随后,要掌握的是canvas绘制圆形的函数:
 arc(x, y, radius, startAngle, endAngle, anticlockwise)该方法表示画一个以(x,y)为圆心的、以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认顺时针(true))来生成。
 其中stratAngle和endAngle的单位并不是我们所熟悉的角度单位,而是弧度单位。一个完整的圆跨弧度2π。 

在canvas的坐标系中,是以x轴正方向所在的方向为0弧度。时钟指针按顺时针方向转,以2π为一个周期,因此如下图所示,时钟指针,是从(-1/2)*π位置开始走的。 

js Canvas实现圆形时钟教程

当前时间的弧度计算方式如下:

//将一个时钟周期12等分,对12求余是因为Date().getHours将返回24小时制的小时。
hour = (currentTime.getHours() % 12 ) * (2 * Math.PI /12); 
//MINUTE 一圈60等分
minute = (currentTIme.getMinutes) * (2* Math.PI / 60); 
//SECOND 一圈60等分
second = (currentTime.getSeconds) * (2 * Math.PI / 60);

由于在canvas中时钟圆从(-1/2)*π开始走,因此我们还需要给它们加上(-1/2)*π的起始偏移量。

初步得到draw.js:

function draw() {
 //canvas绘画的前提工作
 var canvas = document.getElementById('clock');
 var currentTime = new Date();
 var hour = (currentTime.getHours()%12) * Math.PI/6;
 var minute = currentTime.getMinutes() * Math.PI/30;
 var second = currentTime.getSeconds() * Math.PI/30;
 hour = hour - Math.PI * (1/2);
 minute = minute - Math.PI * (1/2);
 second = second - Math.PI * (1/2);
 if (canvas.getContext){
 var ctx = canvas.getContext('2d');
 ctx.beginPath();
 ctx.arc(200,200,50,Math.PI*(-1/2),hour,false);
 ctx.moveTo(200,100);
 ctx.arc(200,200,100,Math.PI*(-1/2),minute,false);
 ctx.moveTo(200,50);
 ctx.arc(200,200,150,Math.PI*(-1/2),second,false);
 ctx.stroke();
 }
}

同时在canvas.html中 加入draw.js的引用。 

<html>
 <head>
 <title>Canvas clock tutorial</title>
 <style type="text/css">
  canvas { border: 1px solid black; }
 </style>
 <script src="draw.js" type="text/javascript"></script>
 </head>
 <body onload="draw();">
 <canvas id="clock" width="400" height="400"></canvas>
 </body>
</html>

在完成了第二步之后,我们可以看到一个当前时间的圆形时钟轮廓。那么接下来,就是让它动起来! 

第三步:使用requestAnimationFrame()方法让时钟动起来。

requestAnimationFrame()的执行频率是1秒60帧,用户可以在requestAnimationFrame定义事件,使其在每一帧中重复、迭代完成相应的事件。

在我们这个案例中,每一帧里面requestAnimationFrame要做的事情很简单,为当前时间的三个参数(时针、分针、秒针)添加增量,然后重新绘制时钟。

根据一秒60帧的频率,那么second在1帧里面的增量是:2*π / 60 / 60 = π / 1800;

minute在一帧里面的增量是second增量的60分之一;hour在一帧里面的增量是minute的12分之1。

另外:当hour、minute、second任一一个变量到达3/2*π后,意味着完成了一个周期,就要重新初始化为(-1/2)*π,否则会覆盖绘制成一个圆。

由此得到:requestAnimationFrame()函数中,hour,minute,second的更新过程如下: 

var s = Math.PI / 1800;
  var m = s / 60;
  var h = m / 12;
  second = second + s;
  minute = minute + m;
  hour = hour + h;
  if(second >= Math.PI * (3/2)){
  second = Math.PI * (-1/2);
  }
  if(minute >= Math.PI * (3/2)){
  minute = Math.PI * (-1/2);
  }
  if(second >= Math.PI * (3/2)){
  second = Math.PI * (-1/2);
  }</span>

 更新draw.js的完整代码如下:

function draw() {
 var canvas = document.getElementById('clock');
 var currentTime = new Date();
 var hour = (currentTime.getHours()%12) * Math.PI/6;
 var minute = currentTime.getMinutes() * Math.PI/30;
 var second = currentTime.getSeconds() * Math.PI/30;
 hour = hour - Math.PI * (1/2);
 minute = minute - Math.PI * (1/2);
 second = second - Math.PI * (1/2);
 if (canvas.getContext){
 var ctx = canvas.getContext('2d');
 var s = Math.PI / 1800;
 var m = s / 60;
 var h = m / 12;
 var rotate = requestAnimationFrame(step);
 function step(){
  second = second + s;
  minute = minute + m;
  hour = hour + h;
  if(second >= Math.PI * (3/2)){
  second = Math.PI * (-1/2);
  }
  if(minute >= Math.PI * (3/2)){
  minute = Math.PI * (-1/2);
  }
  if(second >= Math.PI * (3/2)){
  second = Math.PI * (-1/2);
  }
  ctx.clearRect(0, 0, 400 , 400);
  ctx.beginPath();
  ctx.arc(200,200,50,Math.PI*(-1/2),hour,false);
  ctx.moveTo(200,100);
  ctx.arc(200,200,100,Math.PI*(-1/2),minute,false);
  ctx.moveTo(200,50);
  ctx.arc(200,200,150,Math.PI*(-1/2),second,false);
  ctx.stroke();
  requestAnimationFrame(step);
 }
 }
}

到这步为止,我们已经得到了一个会动的圆形时钟,接下来,我们为其添加上指针。 

第四步*:添加时针、分针、秒针。 

JS为我们提供了Math.cos() 、Math.sin() 计算指针到达的位置,它们所接收参数的单位也都是弧度。

function draw() {
 var canvas = document.getElementById('clock');
 var currentTime = new Date();
 var hour = (currentTime.getHours()%12) * Math.PI/6;
 var minute = currentTime.getMinutes() * Math.PI/30;
 var second = currentTime.getSeconds() * Math.PI/30;
 hour = hour - Math.PI * (1/2);
 minute = minute - Math.PI * (1/2);
 second = second - Math.PI * (1/2);
 if (canvas.getContext){
 var ctx = canvas.getContext('2d');
 var s = Math.PI / 1800;
 var m = s / 60;
 var h = m / 12;
 var rotate = requestAnimationFrame(step);
 function step(){
  second = second + s;
  minute = minute + m;
  hour = hour + h;
  if(second >= Math.PI * (3/2)){
  second = Math.PI * (-1/2);
  }
  if(minute >= Math.PI * (3/2)){
  minute = Math.PI * (-1/2);
  }
  if(second >= Math.PI * (3/2)){
  second = Math.PI * (-1/2);
  }
  //秒针的终点
  xs = 150 * Math.cos(second) + 200;
  ys = 150 * Math.sin(second) + 200;
  //分针的终点
  xm = 100 * Math.cos(minute) + 200;
  ym = 100 * Math.sin(minute) + 200;
  //时针的终点
  xh = 50 * Math.cos(hour) + 200;
  yh = 50 * Math.sin(hour) + 200;

  ctx.clearRect(0, 0, 400 , 400);
  ctx.beginPath();
  //绘制指针
  ctx.moveTo(200,200);
  ctx.lineTo(xs,ys);
  ctx.moveTo(200,200);
  ctx.lineTo(xm,ym);
  ctx.moveTo(200,200);
  ctx.lineTo(xh,yh);
  //绘制圆形轮廓
  ctx.moveTo(200,150);
  ctx.arc(200,200,50,Math.PI*(-1/2),hour,false);
  ctx.moveTo(200,100);
  ctx.arc(200,200,100,Math.PI*(-1/2),minute,false);
  ctx.moveTo(200,50);
  ctx.arc(200,200,150,Math.PI*(-1/2),second,false);
  ctx.stroke();
  requestAnimationFrame(step);
 }
 }
}

嗯嗯,虽然指针是画出来了,都是我略感后悔,因为感觉太丑了。从审美学角度而言,带指针时钟就应该是个全圆轮廓的时钟。不知道读者有没有同感。我决定在第五步中把第四步回退了。 

第五步:个性化、美化、自定义你的时钟。

html5的canvas画布,提供了多种样式属性,如线条颜色、线条粗细等等。

为了提高代码的可重用性,我们将画圆的代码抽象成一个函数。

function draw() {
 var canvas = document.getElementById('clock');
 var currentTime = new Date();
 var hour = (currentTime.getHours()%12) * Math.PI/6;
 var minute = currentTime.getMinutes() * Math.PI/30;
 var second = currentTime.getSeconds() * Math.PI/30;
 hour = hour - Math.PI * (1/2);
 minute = minute - Math.PI * (1/2);
 second = second - Math.PI * (1/2);
 if (canvas.getContext){
 var ctx = canvas.getContext('2d');
 var s = Math.PI / 1800;
 var m = s / 60;
 var h = m / 12;
 var rotate = requestAnimationFrame(step);
 function step(){
  second = second + s;
  minute = minute + m;
  hour = hour + h;
  if(second >= Math.PI * (3/2)){
  second = Math.PI * (-1/2);
  }
  if(minute >= Math.PI * (3/2)){
  minute = Math.PI * (-1/2);
  }
  if(second >= Math.PI * (3/2)){
  second = Math.PI * (-1/2);
  }

  ctx.clearRect(0, 0, 400 , 400);
  ctx.beginPath();
  //绘制圆形轮廓
  drawCircle(ctx, 100, hour, '#99ff00');
  drawCircle(ctx, 120, minute, '#99ff66');
  drawCircle(ctx, 140, second, '#66cc66');
  requestAnimationFrame(step);
 }
 }
}

function drawCircle(ctx, radius ,endAngle, color){
 ctx.beginPath();
 ctx.moveTo(200,200-radius);
 ctx.strokeStyle = color;
 ctx.lineWidth = 20;
 ctx.arc(200,200,radius,Math.PI*(-1/2),endAngle,false);
 ctx.stroke();
}

第六步(后续添加):为时钟添加数字指数。

具体做法为多增加一个画布canvas元素。既然我们已经能从Date()中获取时间,那么显示数值的来源不成问题。读者唯一需要知道的就是利用setInterval(thingstodo(),interval)方法来实现每秒更新一次指数。 

修改后的canvas.html

<html>
 <head>
 <title>Canvas clock tutorial</title>
 <style type="text/css">
  canvas { border: 1px solid black; }
  #display {position: absolute; top:8; left:8;}
 </style>
 <script src="draw.js" type="text/javascript"></script>
 </head>
 <body onload="draw();">
 <canvas id="clock" width="400" height="400"></canvas>
 <canvas id="display" width="400" height="400"></canvas>
 </body>
</html>

修改后的draw.js

function draw() {
 var canvas = document.getElementById('clock');
 var displayCanvas = document.getElementById('display');
 var currentTime = new Date();
 var hour = (currentTime.getHours()%12) * Math.PI/6;
 var minute = currentTime.getMinutes() * Math.PI/30;
 var second = currentTime.getSeconds() * Math.PI/30;
 hour = hour - Math.PI * (1/2);
 minute = minute - Math.PI * (1/2);
 second = second - Math.PI * (1/2);
 if (canvas.getContext){
 var ctx = canvas.getContext('2d');
 var ctxD = displayCanvas.getContext('2d');
 showDisplay(ctxD, currentTime);
 var s = Math.PI / 1800;
 var m = s / 60;
 var h = m / 12;
 var rotate = requestAnimationFrame(step);
 function step(){
  second = second + s;
  minute = minute + m;
  hour = hour + h;
  if(second >= Math.PI * (3/2)){
  second = Math.PI * (-1/2);
  }
  if(minute >= Math.PI * (3/2)){
  minute = Math.PI * (-1/2);
  }
  if(second >= Math.PI * (3/2)){
  second = Math.PI * (-1/2);
  }

  ctx.clearRect(0, 0, 400 , 400);
  ctx.beginPath();
  //绘制圆形轮廓
  drawCircle(ctx, 100, hour, '#99ff00');
  drawCircle(ctx, 120, minute, '#99ff66');
  drawCircle(ctx, 140, second, '#66cc66');
  requestAnimationFrame(step);
 }
 }
}

function drawCircle(ctx, radius ,endAngle, color){
 ctx.beginPath();
 ctx.moveTo(200,200-radius);
 ctx.strokeStyle = color;
 ctx.lineWidth = 20;
 ctx.arc(200,200,radius,Math.PI*(-1/2),endAngle,false);
 ctx.stroke();
}

function showDisplay(ctx, date){
 var h = date.getHours(),m = date.getMinutes(),s = date.getSeconds();
 //计时文字样式
 ctx.font = "13px Sans-Serif";
 ctx.textAlign = "center";
 ctx.strokeText(h+":"+m+":"+s,200,200);
 var timmer = setInterval(function(){
 s++;
 if(s>=60){
  m++;
  s=0;
 }
 if(m>=60){
  h++;
  m=0;
 }
 if(h>=24){
  h=0;
 }
 ctx.clearRect(0,0,400,400);
 ctx.strokeText(h+":"+m+":"+s,200,200);
 },1000);
}

最终成果图如下:

js Canvas实现圆形时钟教程

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
baidu博客的编辑友情链接的新的层窗口!经典~支持【FF】
Feb 09 Javascript
jquery.lazyload  实现图片延迟加载jquery插件
Feb 06 Javascript
javascript getElementsByClassName实现代码
Oct 11 Javascript
Jquery 模板数据绑定插件的使用方法详解
Jul 08 Javascript
js操作数组函数实例小结
Dec 10 Javascript
AngularJs中 ng-repeat指令中实现含有自定义指令的动态html的方法
Jan 19 Javascript
Vue开发过程中遇到的疑惑知识点总结
Jan 20 Javascript
Three.js利用顶点绘制立方体的方法详解
Sep 27 Javascript
Vue中mintui的field实现blur和focus事件的方法
Aug 25 Javascript
原生js实现的移动端可拖动进度条插件功能详解
Aug 15 Javascript
微信JSSDK实现打开摄像头拍照再将相片保存到服务器
Nov 15 Javascript
一篇超完整的Vue新手入门指导教程
Nov 18 Vue.js
Bootstrap模态框调用功能实现方法
Sep 19 #Javascript
javascript实现的上下无缝滚动效果
Sep 19 #Javascript
Angular ng-class详解及实例代码
Sep 19 #Javascript
javascript实现的左右无缝滚动效果
Sep 19 #Javascript
javascript实现图片左右滚动效果【可自动滚动,有左右按钮】
Sep 19 #Javascript
BootStrap入门教程(三)之响应式原理
Sep 19 #Javascript
HTML5 实现的一个俄罗斯方块实例代码
Sep 19 #Javascript
You might like
推荐Discuz!5的PHP代码高亮显示与实现可运行代码
2007/03/15 PHP
php生成缩略图示例代码分享(使用gd库实现)
2014/01/20 PHP
PHP信号量基本用法实例详解
2016/02/12 PHP
JS中剪贴板兼容性、判断复制成功或失败
2021/03/09 Javascript
jquery监控数据是否变化(修正版)
2011/04/12 Javascript
一个JQuery操作Table的代码分享
2012/03/30 Javascript
JS添加删除一组文本框并对输入信息加以验证判断其正确性
2013/04/11 Javascript
JavaScript设置IFrame高度自适应(兼容各主流浏览器)
2013/06/05 Javascript
图片上传判断及预览脚本的效果实例
2013/08/07 Javascript
$.each与$().each的区别示例介绍
2014/03/20 Javascript
jQuery拖拽插件gridster使用指南
2015/04/21 Javascript
通过npm引用的vue组件使用详解
2017/03/02 Javascript
详解node.js平台下Express的session与cookie模块包的配置
2017/04/26 Javascript
小程序ios音频播放没声音问题的解决
2018/07/11 Javascript
layui禁用侧边导航栏点击事件的解决方法
2019/09/25 Javascript
vuex+axios+element-ui实现页面请求loading操作示例
2020/02/02 Javascript
解决elementUI 切换tab后 el_table 固定列下方多了一条线问题
2020/07/19 Javascript
js实现限定区域范围拖拉拽效果
2020/11/20 Javascript
[44:01]2018DOTA2亚洲邀请赛3月30日 小组赛B组 EG VS paiN
2018/03/31 DOTA
浅谈Scrapy网络爬虫框架的工作原理和数据采集
2019/02/07 Python
python 实现兔子生兔子示例
2019/11/21 Python
Python hashlib常见摘要算法详解
2020/01/13 Python
Python集成开发工具Pycharm的安装和使用详解
2020/03/18 Python
详细分析Python collections工具库
2020/07/16 Python
世界上最好的威士忌和烈性酒购买网站:The Whisky Exchange
2016/11/20 全球购物
英国女性化妆品收纳和家具网站:Beautify
2019/12/07 全球购物
俄罗斯天然和有机产品、健康生活网上商店:Fitomarket.ru
2020/10/09 全球购物
汇科协同Java笔试题
2012/03/31 面试题
环境科学专业个人求职信
2013/09/26 职场文书
入党申请人的自我鉴定
2013/12/01 职场文书
拓展培训心得体会
2014/01/04 职场文书
小学生操行评语
2014/04/22 职场文书
我的祖国演讲稿
2014/05/04 职场文书
先进个人主要事迹范文
2015/11/04 职场文书
Java 常见的限流算法详细分析并实现
2022/04/07 Java/Android
MySQL 原理与优化之原数据锁的应用
2022/08/14 MySQL