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 相关文章推荐
AngularJS基础学习笔记之控制器
May 10 Javascript
深入分析Javascript事件代理
Jan 30 Javascript
JavaScript通过使用onerror设置默认图像显示代替alt
Mar 01 Javascript
Boostrap入门准备之border box
May 09 Javascript
ECMAScript6轮播图实践知识总结
Aug 17 Javascript
浅谈jQuery中hide和fadeOut的区别 show和fadeIn的区别
Aug 18 Javascript
整理关于Bootstrap排版的慕课笔记
Mar 29 Javascript
js匿名函数使用&amp;传参(实例)
Sep 08 Javascript
详解plotly.js 绘图库入门使用教程
Feb 23 Javascript
vue自定义一个v-model的实现代码
Jun 21 Javascript
JavaScript实现的拼图算法分析
Feb 13 Javascript
Vue 进阶之路(三)
Apr 18 Javascript
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
jQuery Validation插件remote验证方式的Bug解决
2010/07/01 Javascript
关于JS控制代码暂停的实现方法分享
2012/10/11 Javascript
js中的屏蔽的使用示例
2013/07/30 Javascript
使用javascript实现页面定时跳转总结篇
2013/09/21 Javascript
JavaScript AJAX之惰性载入函数
2014/08/27 Javascript
javascript获取文档坐标和视口坐标
2015/05/26 Javascript
JS模拟酷狗音乐播放器收缩折叠关闭效果代码
2015/10/29 Javascript
JS判断字符串变量是否含有某个字串的实现方法
2016/06/03 Javascript
Linux使用Node.js建立访问静态网页的服务实例详解
2017/03/21 Javascript
微信小程序中使用javascript 回调函数
2017/05/11 Javascript
vue2.0中click点击当前li实现动态切换class
2017/06/21 Javascript
Vue父子模版传值及组件传值的三种方法
2017/11/27 Javascript
redux中间件之redux-thunk的具体使用
2018/04/17 Javascript
angular6.0使用教程之父组件通过url传递id给子组件的方法
2018/06/30 Javascript
解决element-ui中下拉菜单子选项click事件不触发的问题
2018/08/22 Javascript
vue+mousemove实现鼠标拖动功能(拖动过快失效问题解决方法)
2018/08/24 Javascript
详解vue v-model
2020/08/31 Javascript
完美解决安装完tensorflow后pip无法使用的问题
2018/06/11 Python
Python pymongo模块常用操作分析
2018/09/01 Python
讲解Python3中NumPy数组寻找特定元素下标的两种方法
2019/08/04 Python
Python 仅获取响应头, 不获取实体的实例
2019/08/21 Python
python异步编程 使用yield from过程解析
2019/09/25 Python
python爬虫实现获取下一页代码
2020/03/13 Python
Django REST Swagger实现指定api参数
2020/07/07 Python
详解win10下pytorch-gpu安装以及CUDA详细安装过程
2021/01/28 Python
重新定义牛仔布,100美元以下:Warp + Weft
2018/07/25 全球购物
澳大利亚一站式数码相机商店:CameraPro
2020/03/09 全球购物
生产内勤岗位职责
2013/12/07 职场文书
初三政治教学反思
2014/01/30 职场文书
法制宣传标语集锦
2014/06/25 职场文书
关于诚信的活动方案
2014/08/18 职场文书
赔偿协议书
2015/01/27 职场文书
中秋节晚会开场白
2015/05/29 职场文书
「魔法少女伊莉雅」美游粘土人开订
2022/03/21 日漫
golang连接MySQl使用sqlx库
2022/04/14 Golang
深入理解 Golang 的字符串
2022/05/04 Golang