JavaScript總算入門了,複雜的就先不來了,今兒牛刀小試,先來一個系統同步的時鐘效果,只用到最簡單的獲取系統時間的函數。由於學習是須要正反饋的,不然總也看不到效果,不免失了深刻下去的興趣。在以前的一篇專欄文章《利用CSS3的animation step屬性實現wifi動畫》的末尾,曾經作過一個時鐘,當時鑑於JS空白,最終雖然也實現了時鐘效果,但卻沒法作到與系統時間同步。今次,就來作一個可與當前時間徹底一致的時鐘,並一步步實現從簡陋到豪華的華麗變身。javascript
爲了不復雜的時鐘圖層太多的干擾,因此先來一個最最基礎的版本,一個圓表明底盤,三個直線分別表明時針分針秒針,嗯,或者你能夠稱之爲極簡主義(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
在此篇文章以前,一直玩的都是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圖形中的時針分針秒針是徹底對應的。
其實上面的效果實現後,會一丟丟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不要急不要急,雖然上面完成了一個徹底同步的時鐘效果,但咱們知道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經過如下方式實現:
//建立新的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 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。
最難的部分已經完成,既然動畫效果出來了,那麼在此模板的基礎上,能夠作一些絢爛的效果了。(友情提示,建議在下面這些動效源碼的基礎上進行修改,有詳細註釋,能夠直接用圖層元素去替換)至於在哪一個模板的基礎上修改?全憑我的喜愛,由於這個定時器是單線程的,不會吃內存,因此在這個案例中,暫時在這個模板的基礎上替換。
<!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屬性而已。只是由於是平面的,因此那個手臂的擺動會有些怪怪的。
祝各位新年鴻運當頭,即便互聯網寒冬再冷,也要笑着走下去。就醬。