進入JS30的第二天,今天我們要利用Javascript中的方法以及CSS屬性,在頁面上做出一個會轉動的虛擬時鐘。[1]
實作連結一
實作連結二
首先我們需要取得時間這筆資料。而在Javascript中,可以使用new Date()這個方法,來取得目前我們電腦中的時間物件,並且利用getter來取得所需的詳細資料,例如年、月、日、時、分、秒等:
var time = new Date();
var currentHour = time.getHours();
var currentMinute = time.getMinutes();
var currentSecond = time.getSeconds();
console.log(time); //Thu Dec 21 2017 10:19:24 GMT+0800 (CST)
console.log(currentHour); //10
console.log(currentMiute); //19
console.log(currentSecond); //24
這邊我們只取得時鐘上需要的時針、分針與秒針所需的資料,在下方參考連結附上關於new Date()物件的MDN文件,各位讀者可以嘗試看看,將取得的時間物件,轉換成想要的資料形式,或者將頁面中的時間做修改,這些都是可以辦得到的。[2]
取得時間資料之後,我們就可以開始將時間,轉換成各指針在時鐘上的對應角度:
在作者提供的草稿當中,一開始三根指針都是指向9點鐘的方向,所以我們需要加上90度,將這些指針做歸零的動作,不然指針的位置將無法對應到正確的時間。此外還需加上分針與時針的偏移,因此我們得到各指針角度的內容如下:
var hourAngle = currentHour * 360/12 + currentMinute* 360/(12*60) + 90 ;
var minAngle = currentMinute * 360/60 + currentSecond *360/(60*60) + 90;
var secondAngle = currentSecond * 360/60 + 90;
完成目前時間下每個指針所需對應的旋轉角度後,再來就是讓各個指針旋轉。此時就要用到CSS中的transform()這個屬性,透過這個屬性,我們可以將指定的元素,做2D或是3D的變形,而在此章節中我們將只會用到2D變形。
首先,我們要先定位旋轉的原點,如果我們沒有設定的話,會因為原點的X軸以及Y軸座標default設定為50%,發現物體繞著自己的中心旋轉,因此我們需要設定transform-origin這個屬性,透過將X軸原點設定在元素最右邊,也就是寬度100%,Y軸原點則設定為最高度的一半,也就是50%,我們的三個指針便可以對著時鐘中心點旋轉:
//在hand element中 加入transform-origin屬性:
.hand {transform-origin: 100% 50%;}
指定旋轉的原點之後,我們便可以加上transform中的另一個屬性rotate(),讓指定的元素,繞著原點旋轉一定角度:
{transform:ratate(90deg)}
了解以上兩種transform的屬性設定之後,便可以建立一個函式,讓頁面開啟時執行該函式,並透過element.style.cssProperty,將我們指定的元素,加上我們想要的CSS屬性,如此一來各指針便能轉到對應的角度:
function getAngle() {
hourHand.style.transform = `rotate(${hourAngle}deg)`;
minHand.style.transform = `rotate(${minAngle}deg)`;
secHand.style.transform = `rotate(${secondAngle}deg)`;
};
getAngle();
可以看到在函式getAngle中,我們依序給時針、分針及秒針,加上transform屬性並給予其對應的rotate角度,並且在一開啟時便執行該函式。如此一來,當開啟頁面時,時鐘便會指出當下的時刻。
在transform屬性中還包含著許多功能,以下列出其他transform 2D變形的相關屬性:
這邊提供一個參考網站,可以直接在裡面修改各種2D變形的屬性,進而看到不同transform屬性在頁面上所產生的效果,可以對各個屬性有更進一步的認識。[3]
我們已經完成時鐘的初始設定,再來我們要讓各個指針隨著時間動起來。時鐘每個指針每秒會旋轉一定角度,因此最簡單的方式便是使用setInterval()這個方法。透過這個方法,我們可以指定某函式並指派其呼叫週期:
//每經過一定時間,執行一次函式。
setInterval(函式,時間/*毫秒*/)
當setInterval()方法存在時,他會持續呼叫所指定的函式,直到呼叫clearInterval()來終止或者關閉頁面。
而這邊有一個類似的方法,setTimeout()。這個方法可以讓我們設定多久時間後,執行一次指定的函式。
//經過指定時間後,只執行一次函式。
setTimeout(函式,時間/*毫秒*/)
了解setInterval()這個方法之後,我們便可以透過每秒呼叫一次函式,讓指針隨著時間轉動。我們在這邊使用的方法,是將各指針每秒所旋轉的角度,逐次加上,因此函式的內容如下:
function addAngle() {
hourAngle = hourAngle + 360 / (12*60*60);
minAngle = minAngle + 360 / (60*60);
secondAngle = secondAngle + 360 / 60;
hourHand.style.transform = `rotate(${hourAngle}deg)`;
minHand.style.transform = `rotate(${minAngle}deg)`;
secHand.style.transform = `rotate(${secondAngle}deg)`;
};
setInterval(addAngle, 1000);
完成以上的設定之後,當頁面開啟時,函式getAngle()被呼叫,並將指針旋轉至對應角度,然後隨著setInterval()每秒呼叫一次函式addAngle(),使指針逐次增加旋轉角度,如此一來我們的虛擬時鐘就像裝上了電池,動起來了!
如果我們想更進一步,讓指針的行為更生活化,例如持續旋轉或者產生抖呢?這時我們就要來認識一下transition這個CSS屬性。透過這個屬性,我們可以將元素CSS屬性的轉變過程轉為動畫,而不只是單一從原本的外觀直接變成另一種外觀。
Transition中總共包含以下4種子屬性:
此4種子屬性也可寫成單一行transition屬性:
transition: property duration timing-function delay;
前三個屬性我們都能從字面上了解其功能,但最後一個timing-function,似乎就比較難理解。
一般來說,會以貝茲曲線來解釋transition-timing-function,我們可以將其理解為時間對位置的關係,曲線中的X軸為過場時間,Y軸為過場動畫的位置,因此斜率就可以解釋成過場動畫速度。
此屬性中又提供了以下6種條件,大家可以透過參考網站,玩看看不同的條件下的過場行為 [4]:
Linear:
其貝茲曲線,可以看到為筆直的一條線,速度(斜率)上沒有任何的變化,因此過場動畫將以等速度開始到完成。
Ease:
其貝茲曲線一開始斜率無變化,然後斜率開始增加,最後斜率又漸趨平緩。因此過場動畫剛開始會短暫等速前進,然後加速,最後緩慢減速完成。
Ease-in:
其斜率由一開始平緩到後面急劇上升,所以過場動畫將一開始速度緩慢但後面劇烈加速來完成。
Ease-out:
此屬性為Ease-in的相反,斜率從剛開始急劇上升然後逐漸平緩,所以所以過場動畫將一開始劇烈加速但後面緩面減速來完成。
Ease-in-out:
此曲線前半段為Ease-in,後半段為Ease-out,過場動畫將會呈現先加速後減速的效果。
貝茲曲線(cubic-bezier):
透過直接給定貝茲曲線的設定值,來設定這條曲線的行為。
cubic-bezier (A1, A2, B1, B2);
x軸: 時間
y軸: 距離
利用貝茲曲線,我們可以設定想要的效果,甚至可以使這條曲線超出這個cubic-bezier區域,而達到過場動畫超出的起點與終點範圍的效果。這邊有提供一個範例,讀者可以點擊下方的連結,來看到透過調整貝茲曲線達到的特殊效果。[5]
模擬秒針的行為,在這邊我們提供兩個例子:
.second-hand {
transition: transform 1s;
transition-timing-function: linear;
};
.second-hand {
transition: transform 0.1s;
transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1);
}
在作者提供的原始Code當中,我們會發現秒針在59跳回0秒的時候,會出現一個神奇的情況。秒針會瞬間逆時針轉回0度再馬上轉回到目前的位置。這是因為作者在旋轉角度的寫法,是將當下的時間換算成角度,因此當秒針從59秒要跳回0秒的時候,transform的rotate角度會由接近360轉回接近0度,以致於逆時針旋轉的產生。而在我們的code當中,因為旋轉角度是用每秒增加的方式加上去,因此不會有此現象的產生,各位讀者如果是依照JS30作者的方法練習的話,可以嘗試參考我們文章中的設定內容,便可以消除秒針的在位置的小插曲。
在今天的時鐘課題當中,我們學到以下的技能:
透過理解javascript中new Date()這個方法,以及CSS transform以及transition屬性後,就可以做出我們想像中的時鐘。此外,利用這兩種CSS屬性,可以在頁面上做出更多令人驚豔的動畫效果喔!以上是JS30第二天的分享心得,歡迎大家交流討論。