JavaScript牛刀小试,结合CSS3动画属性来作一个系统时间同步的时钟

JavaScript总算入门了,复杂的就先不来了,今儿牛刀小试,先来一个系统同步的时钟效果,只用到最简单的获取系统时间的函数。由于学习是须要正反馈的,不然总也看不到效果,不免失了深刻下去的兴趣。在以前的一篇专栏文章《利用CSS3的animation step属性实现wifi动画》的末尾,曾经作过一个时钟,当时鉴于JS空白,最终虽然也实现了时钟效果,但却没法作到与系统时间同步。今次,就来作一个可与当前时间彻底一致的时钟,并一步步实现从简陋到豪华的华丽变身。javascript

1. 基础版,先实现效果

为了不复杂的时钟图层太多的干扰,因此先来一个最最基础的版本,一个圆表明底盘,三个直线分别表明时针分针秒针,嗯,或者你能够称之为极简主义(MUJI风?Maybe)。既然是基础图形,那能够不用借助Ai,直接手动码出来就能够了。css

DOM部分(以800*600的画布为例,原点为(400,300))html

<!--圆形底盘-->
<circle class="base" cx="400" cy="300" r="200" /> 
<!--长度为180的秒针-->
<line class="pointer" id="second" x1="400" y1="300" x2="400" y2="120"/>
<!--长度为140的分针-->
<line class="pointer" id="minute" x1="400" y1="300" x2="400" y2="160"/>
<!--长度为80的时针-->
<line class="pointer" id="hour" x1="400" y1="300" x2="400" y2="220"/>
复制代码

CSS部分java

<style type="text/css">
/* 圆形底盘 */
.base{stroke:#000000;stroke-width:4; fill:#FFFFFF}
/* 指针公用描边样式 */
.pointer{opacity:0.5;fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
/* 描边粗为6的秒针 */
#second{stroke-width:6;}
/* 描边粗为12的分针 */
#minute{stroke-width:12;}
/* 描边粗为18的时针 */
#hour{stroke-width:18;}
</style>
复制代码

天然,这个最终的样子不是那么美,如今全部的指针都指向一个初始位置,即零时。app

基础时钟效果

2.获取当前时间在SVG图形中体现

在此篇文章以前,一直玩的都是SVG+CSS3动画,那这里要啰嗦一下了。SVG毕竟只是一种图形绘制方法,支持CSS控制样式,但和控制交互的JS并没有半点关联,既然要用JavaScript的函数及操做,因此,这里要作的第一步,就是把上一步建立的SVG图形像普通图片同样,整个一古脑儿的塞到html文件的body部分中。但这里SVG与普通图片相比,最大的优点是里面的标签元素能够被获取到并改变,往下看便知。在实际应用的过程当中,要把SVG文件和JavaScript以引入的形式使用,这里为了方便,暂时不作剥离。svg

<!DOCTYPE html>
<html lang="en">
<svg>
…… SVG部分
</svg>
</html>
复制代码

获取当前时间的函数很简单函数

<script>
var dt=new Date();
//获取小时
var hour=dt.getHours();
//获取分
var minute=dt.getMinutes();
//获取秒
var second=dt.getSeconds();
</script>
复制代码

下面要作的是,把返回的时间在时钟上正确的映射,也就是说把旋转的度数与时间一一对应。360度对应12个小时,60分钟,60秒,就相应得出1hour→30度,1minute→6度,1second→6度的关系。为了方便使用,我把以上JS改了一下,直接改为度数值。post

//小时对应的旋转度数
var hourDeg=dt.getHours()*30;
//分钟对应的旋转度数
var minuteDeg=dt.getMinutes()*6;
//秒对应的旋转度数
var secondDeg=dt.getSeconds()*6;
复制代码

CSS3里支持基本的旋转变形的属性,transform:rotate(相应度数),因此这里定义一个函数来给指针增长类样式,旋转的度数为上面获取到的当前时间对应的旋转度数。学习

//定义同步时间的函数
function syn(){
//给时针追加旋转变形的类样式
$("hour").style.cssText="transform:rotate("+hourDeg+"deg);";
//给分针追加旋转变形的类样式
$("minute").style.cssText="transform:rotate("+minuteDeg+"deg);";
//给秒针追加旋转变形的类样式
$("second").style.cssText="transform:rotate("+secondDeg+"deg);";
}
复制代码

这里还有一个和旋转变形关系极为密切的属性,旋转的原点transform-origin,由于是三种指针的共用属性值,因此我把旋转原点的定义transform-origin:400px 300px直接追加到了pointer类样式中。测试

为了测试是否好用,我让系统的时间暂时在窗口显示。

时钟时间与系统时间同步

当调用这个函数时,能够看到当前时间与SVG图形中的时针分针秒针是彻底对应的。

3.让时钟不停歇的动起来

其实上面的效果实现后,会一丢丢javascript的小伙伴已经知道了如何让时钟不停歇的动起来了。那就是定时器功能。只要设置每秒触发一次定时器就能够了。

//定义获取当前时间的函数
function timeId(){
var dt=new Date();
var hourDeg=dt.getHours()*30;
var minuteDeg=dt.getMinutes()*6;
var secondDeg=dt.getSeconds()*6;
$("hour").style.cssText="transform:rotate("+hourDeg+"deg);";
$("minute").style.cssText="transform:rotate("+minuteDeg+"deg);";
$("second").style.cssText="transform:rotate("+secondDeg+"deg);";
}
//定时器启动前先调用一次函数
timeId();
//定义每秒触发一次的定时器
setInterval(timeId, 1000);
复制代码

不停的时钟

这样的话,就获得了一枚丑丑的,可是能够与当前时间同步的时钟。 可是,这里有个小问题, 由于返回的系统时间是整数值,对于秒针而言,一格格的跳着走没什么问题,但对于分针尤为是时针而言,到了整点再跳一格,显然是与现实世界中的时钟不符的。 好比08:59:59在下一秒09:00:00时,时针会从8大幅度的跳向9。由于个人表盘没有指针,因此看上去并非那么明显。但从截取的GIF中能够看到明显的分针从42跳到43的效果。
以时针为例,解决思路很简单,只须要把分钟返回的数值换算成时针相应的度数,与时针对应的度数相加便可。60minutes→1hour→30度,即1minute→0.5度。每1分钟,时针增长0.5度。同理,每1秒钟,分针增长0.1度。因而我把原来定义的时针和分针的旋转变形角度,改为了下面这种

//时针度数修正,增长分针对应的度数
var hourDeg=dt.getHours()*30 + dt.getMinutes()*0.5;
//分针度数修正,增长秒针对应的度数
var minuteDeg=dt.getMinutes()*6+dt.getSeconds()*0.1;
复制代码

按接近整点截了一段动图,看一下效果

修正后的时钟效果
实现了平滑过渡。这种方法的实现,其实并无用到CSS3的动画属性,只用到了旋转变形属性。codepen附上所有源码 base template of SVG+javascript clock

4.扔掉定时器,使用animation属性

不要急不要急,虽然上面完成了一个彻底同步的时钟效果,但咱们知道CSS3的animation属性是很强大的,结合 @keyframes 动画规则定义,是能够实现旋转效果的。在上面的实现思路中,由于每一秒完成一次旋转变形,所以并无用到animation属性,下面就来试一试,若是不用定时器,如何实现这种效果。

先来看一下,若是是普通的旋转动画,不考虑动态的起始位置的话,定义的秒针的动画规则及animation属性的方法以下:

/*定义动画规则,旋转360度*/
@keyframes second{
0%{transform:rotate(0deg)}
100%{transform:rotate(360deg)}
}
/*定义秒针的旋转动画属性,360度须要时间60秒*/
.second{animation:second linear 60s infinite}
复制代码

这里只须要解决一个问题,就是 0%关键帧对应的初始旋转角度的问题 。0%的确认后,100%的只须要增长360度便可。问题难就难在javascript并不能把变量值传进去来改变CSS的属性,但好在能够经过javascrpt重写CSS属性值。这里实现的思路有些麻烦,我贴上后逐句解释。
以秒针为例,DOM结构中,增长类属性的定义
<line class="pointer second" id="second" x1="400" y1="300" x2="400" y2="120"/>

CSS样式中仅保留second动画属性的定义
.second{animation:second linear 60s infinite}

但动画规则@keyframes经过如下方式实现:

  • javascript建立一个新的CSS样式标签,<style type="text/css">……</style>
  • 把规则做为内容写入新的样式标签中
  • 把新的样式标签追加进去
//建立新的CSS style标签
var style = document.createElement("style");
style.type = "text/css";
//建立能够接受变量参数的动画规则 
//初始关键帧为分针对应的角度
//结束关键帧为分针对应的角度
var keyFrames = "@keyframes second {\ 0% {transform: rotate("+secondDeg+"deg);}\ 100%{transform:rotate("+(secondDeg+360)+"deg)}}";
//把动画规则的内容写入新建立的style标签中
style.innerHTML = keyFrames;
//把新的style标签做为svg的子元素追加进去
document.getElementsByTagName("svg")[0].appendChild(style);
复制代码

如今,只观察秒针,看看这种方法是否是可行。 f)

success!
经过这种方法,成功的把动态动画规则写入了CSS样式中。秒针的没有问题,那么分针和时针就是水到渠成的问题了。这里为了简化代码,封装了一个函数,把 动画规则的名称初始的度数 做为该函数的参数。

//建立一个新的CSS style标签
var style = document.createElement("style");
style.type = "text/css";
//定义一个新的style标签内容的函数,动画规则名称和初始度数做为参数
function newKeyFrames(name,degree){
var keyFrames = "@keyframes "+name+" {\ 0% {transform: rotate("+degree+"deg);}\ 100%{transform:rotate("+(degree+360)+"deg)}}";
//由于是新的style标签内容中不断增长新定义的动画规则,因此改为+=
style.innerHTML += keyFrames;
}

//时针的动画规则名称和初始旋转度数做为参数传入
newKeyFrames("hour",hourDeg);
//分针的动画规则名称和初始旋转度数做为参数传入
newKeyFrames("minute",minuteDeg);
//秒针的动画规则名称和初始旋转度数做为参数传入
newKeyFrames("second",secondDeg);
//把所有增长动画规则后的style样式标签追加到svg元素中
document.getElementsByTagName("svg")[0].appendChild(style);
复制代码

CSS部分,须要分别增长动画属性的类的定义。

/*秒针每旋转360度,须要60秒*/
.second{animation:second linear 60s  infinite;}
/*分针每旋转360度,须要3600秒*/
.minute{animation:minute linear 3600s  infinite;}
/*时针每旋转360度,须要216000秒*/
.hour{animation:hour linear 216000s  infinite;}
复制代码

DOM结构中记得把类样式附加上。

<line class="pointer second" id="second" />
<line class="pointer minute" id="minute" />
<line class="pointer hour" id="hour" />
复制代码

能够看一下最终的效果了

彷佛还有哪里有点问题,秒针的跳帧效果!

/*把线性linear去掉,改为steps(60) 即一个动画周期60秒跳帧为60*/
.second{animation:second steps(60) 60s  infinite;}
复制代码

无懈可击!codepen附上源码base template of SVG+javascript+CSS3 clock

显而易见的是,第二种方法看上去并无第一种那么简单,甚至能够说比较绕,那么为何这里用了不少篇幅来探索这种方法的可行性?是由于此次的案例恰好是个时钟,适合用定时器而已,而大多数状况下,咱们须要一种可接受变量做为动画规则参数的方法来解决问题,so。

5.以上是骨骼,如下才是灵魂

最难的部分已经完成,既然动画效果出来了,那么在此模板的基础上,能够作一些绚烂的效果了。(友情提示,建议在下面这些动效源码的基础上进行修改,有详细注释,能够直接用图层元素去替换)至于在哪一个模板的基础上修改?全凭我的喜爱,由于这个定时器是单线程的,不会吃内存,因此在这个案例中,暂时在这个模板的基础上替换。

<!DOCTYPE html>
<!--重点注意:!!!!Ai导出以前必定要把指针都放置初始零点的位置-->
<html lang="en">
<svg>
<style type="text/css"> /* Ai导出时生成的样式 每次使用时须要替换*/ .st0{ } …… /* 旋转原点的值根据不一样的底图须要从新定义 */ .pointer{transform-origin: } </style>
<!--在进行图层排序时,遵循底图-时针-分针-秒针-覆盖层(若是有的话)的规律-->
<g id="base">
	底图对应的路径
</g>
<g class="pointer" id="hour">
时针对应的路径
</g>
<g class="pointer" id="minute">
	分针对应的路径	
</g>
<g class="pointer" id="second">
	秒针对应的路径
</g>
<g id="overlay">
    覆盖层对应的路径
</g>
</svg>
<script> // 由于没有用jQuery,所以封装了一个最基本的函数 function $(id){ var idValue=document.getElementById(id); return idValue; } </script>
<script> //定义获取当前时间转换成旋转变形角度的函数 function timeId(){ var dt=new Date(); //时针度数修正,增长分针对应的度数 var hourDeg=dt.getHours()*30 + dt.getMinutes()*0.5; //分针度数修正,增长秒针对应的度数 var minuteDeg=dt.getMinutes()*6+dt.getSeconds()*0.1; var secondDeg=dt.getSeconds()*6; //给时针追加旋转变形类样式 $("hour").style.cssText="transform:rotate("+hourDeg+"deg);"; //给分针追加旋转变形类样式 $("minute").style.cssText="transform:rotate("+minuteDeg+"deg);"; //给秒针追加旋转变形类样式 $("second").style.cssText="transform:rotate("+secondDeg+"deg);"; } // 定时器触发前先执行一次函数 timeId(); // 设置每1000ms触发一次的定时器 setInterval(timeId, 1000); </script>
</html>
复制代码

喜欢clock?ok!好比这种不带刻度的简易风 codepen连接

如下均为各类无聊的炫技系列,懒得看可直接略过,无妨无妨。

光秃秃的底盘太单调?来个简易刻度怎样?codepen连接

不怕麻烦的全数字刻度 codepen连接

喜欢手表,说换就换,不就是替换一下的事情嘛codepen连接

太老土?那看看这种iWatch风格是否是你的菜

好了,不玩儿了,万变不离其宗。快过年了,最后来个中洋结合的,招财猫配金玉满堂金灿灿大福字的时钟。(我知道是猪年,可是招财猪,那个猪蹄子挥起来太有喜感,so)
关于招财猫的猫爪先后摆动能够参考我之前的一篇专栏,《无立体,不动画,CSS3 3D 动画属性入门》很基础的3D属性而已。只是由于是平面的,因此那个手臂的摆动会有些怪怪的。

祝各位新年鸿运当头,即便互联网寒冬再冷,也要笑着走下去。就酱。

相关文章
相关标签/搜索