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 相关文章推荐
jquery中的$(document).ready()与window.onload的区别
Nov 18 Javascript
jquery实现点击TreeView文本父节点展开/折叠子节点
Jan 10 Javascript
JavaScript中的onerror事件概述及使用
Apr 01 Javascript
Jquery ajax基础教程
Nov 20 Javascript
JS创建对象几种不同方法详解
Mar 01 Javascript
详解JavaScript实现设计模式中的适配器模式的方法
May 18 Javascript
动态生成的DOM不会触发onclick事件的原因及解决方法
Aug 06 Javascript
jQuery Layer弹出层传值到父页面的实现代码
Aug 17 jQuery
JavaScrip数组删除特定元素的几种方法总结
Sep 06 Javascript
在vue中动态添加class类进行显示隐藏实例
Nov 09 Javascript
JS可断点续传文件上传实现代码解析
Jul 30 Javascript
vue 把二维或多维数组转一维数组
Apr 24 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
PHP中的正则表达式函数介绍
2012/02/27 PHP
深入剖析PHP中printf()函数格式化使用
2016/05/23 PHP
php中__toString()方法用法示例
2016/12/07 PHP
一个对于js this关键字的问题
2007/01/09 Javascript
javascript onkeydown,onkeyup,onkeypress,onclick,ondblclick
2009/02/04 Javascript
Jquery+JSon 无刷新分页实现代码
2010/04/01 Javascript
javascript与CSS复习(二)
2010/06/29 Javascript
基于Jquery的$.cookie()实现跨越页面tabs导航实现代码
2011/03/03 Javascript
JS简单的图片放大缩小的两种方法
2013/11/11 Javascript
初识Node.js
2014/09/03 Javascript
自定义百度分享的分享按钮
2015/03/18 Javascript
Jquery实现遮罩层的简单实例(就是弹出DIV周围都灰色不能操作)
2016/07/14 Javascript
一道面试题引发的对javascript类型转换的思考
2017/03/06 Javascript
js原生实现移动端手指滑动轮播图效果的示例
2018/01/02 Javascript
vue 源码解析之虚拟Dom-render
2019/08/26 Javascript
基于vue中的scoped坑点解说
2020/09/04 Javascript
vue前端和Django后端如何查询一定时间段内的数据
2021/02/28 Vue.js
[53:15]Mineski vs iG 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
pandas or sql计算前后两行数据间的增值方法
2018/04/20 Python
Python使用百度翻译开发平台实现英文翻译为中文功能示例
2019/08/08 Python
基于python实现坦克大战游戏
2020/10/27 Python
使用python对excel表格处理的一些小功能
2021/01/25 Python
泰国最新活动和优惠:Megatix
2020/05/07 全球购物
Java程序开发中如何应用线程
2016/03/03 面试题
艺术设计专业个人求职信
2013/09/21 职场文书
2014年教师党员公开承诺书
2014/05/28 职场文书
学生会干部自我鉴定2014
2014/09/18 职场文书
党的群众路线对照检查材料范文
2014/09/24 职场文书
财务整改报告范文
2014/11/05 职场文书
2015年医院护理部工作总结
2015/04/23 职场文书
小学六年级班主任工作经验交流材料
2015/11/02 职场文书
护理自荐信
2019/05/14 职场文书
Python 文本滚动播放器的实现代码
2021/04/25 Python
浅谈JS的原型和原型链
2021/06/04 Javascript
Java Shutdown Hook场景使用及源码分析
2021/06/15 Java/Android
Logback 使用TurboFilter实现日志级别等内容的动态修改操作
2021/08/30 Java/Android