OneClock目前的三個(gè)表盤中用戶最喜歡的是翻頁(yè)時(shí)鐘。翻頁(yè)效果是表盤的核心,也是我花時(shí)間調(diào)試最久的細(xì)節(jié)。經(jīng)過(guò)7次的產(chǎn)品迭代,終于調(diào)整到了一個(gè)合適的效果。
實(shí)現(xiàn)這個(gè)動(dòng)效的方法只需用到CABasicAnimation中的以X軸旋轉(zhuǎn)即可。CABasicAnimation實(shí)際上還有很多參數(shù),比如大小、透明度、背景顏色等,用這個(gè)方法做很多動(dòng)畫都是可以實(shí)現(xiàn)的。
制作翻頁(yè)時(shí)鐘的效果,實(shí)際上需要解決3個(gè)問(wèn)題:
1.只要時(shí)間刻度變動(dòng)就提前翻頁(yè);
2.真實(shí)演示出翻頁(yè)過(guò)程的遮蓋和層級(jí)效果;
3.正確顯示數(shù)字;
實(shí)現(xiàn)過(guò)程
將翻頁(yè)的過(guò)程通過(guò)側(cè)面角度,解析成下圖中所示的樣子。翻頁(yè)的效果是從“上半頁(yè)”翻轉(zhuǎn)到“下半頁(yè)”,為了讓過(guò)程顯得舒緩,我將翻頁(yè)的過(guò)程設(shè)定為1.0秒。翻頁(yè)過(guò)程耗時(shí)1.0秒,因此在真實(shí)時(shí)間到達(dá)某一秒鐘時(shí),需要判斷下一秒是否需要翻頁(yè),如果翻頁(yè),就立刻執(zhí)行翻頁(yè)的過(guò)程。
翻頁(yè)過(guò)程不需要改變最底層的X層和Y層,它們繼續(xù)靜態(tài)顯示即可。以8點(diǎn)13分59秒為例,當(dāng)判斷下一秒為8點(diǎn)14分時(shí),將立刻生成A層、B層、C層,分別在下圖示意的位置。
A層、B層所顯示的數(shù)字為當(dāng)前的13,C層所顯示的數(shù)字為下一秒的時(shí)間14,當(dāng)然因?yàn)榉较虻膯?wèn)題C層翻轉(zhuǎn)之后實(shí)際上是“下半層”。
一旦開始旋轉(zhuǎn),A層、B層、C層覆蓋X層和Y層,此時(shí)的X層和Y層可以提前賦予他們下一秒的時(shí)間14。
隨著不斷翻轉(zhuǎn),0.5秒后,上半層露出下一個(gè)時(shí)間14,下半層仍然顯示的是當(dāng)前時(shí)間13。
0.5秒到1.0秒之間,C層會(huì)逐步在下半部顯示,能夠清晰地看到下一個(gè)數(shù)字為14。翻頁(yè)1.0秒之后,時(shí)間剛好到達(dá)此刻的時(shí)間8點(diǎn)14分,A層、B層、C層的使命完成,立刻消失隱藏。X層和Y層提前1秒顯示了正確的時(shí)間,翻頁(yè)1.0秒后的時(shí)間剛好是我們所見到的正確時(shí)間8點(diǎn)14分。
如此,翻頁(yè)過(guò)程結(jié)束。
小時(shí)的翻頁(yè)和分鐘的翻頁(yè)實(shí)際上是一樣的,只是小時(shí)翻頁(yè)的過(guò)程伴隨著分鐘同時(shí)翻頁(yè),例如8點(diǎn)59分59秒的下一秒是9點(diǎn)00分,因此兩個(gè)翻頁(yè)將同時(shí)進(jìn)行,而基本原理是一樣的。
相關(guān)代碼
在我的代碼實(shí)現(xiàn)中,「翻頁(yè)時(shí)鐘」和「檢測(cè)時(shí)間」兩個(gè)函數(shù)是分開獨(dú)立的,因此時(shí)鐘、分鐘甚至秒鐘都可以單獨(dú)執(zhí)行翻頁(yè)。
翻頁(yè)函數(shù):
func?rotation(A:UIView,B:UIView,C:UIView){
????A.alpha?=?1
????B.alpha?=?1
????C.alpha?=?1
????rotationFirst(view:?B)
????//本文中提到的B,顯示13
????rotationSecond(view:?C)
????//本文中提到的C,顯示14
????self.perform(#selector(self.initializeABC),with:?nil,?afterDelay:?0.9)?//最后為了過(guò)度順利,提前0.1秒讓A/B/C小時(shí)
????//initializeABC函數(shù)設(shè)置A/B/C隱藏
}
func?rotationFirst(view:UIView){
????//舊值標(biāo)簽,先出來(lái)?let?animation?=?CABasicAnimation(keyPath:?"transform.rotation.x")
????animation.fromValue?=?(-10/360)*Double.pi
????animation.toValue?=?(-355/360)*Double.pi
????animation.duration?=?1.0
????animation.repeatCount?=?0
????animation.delegate?=?selfas??CAAnimationDelegate
????view.layer.add(animation,?forKey:?"rotationSecond")
????view.alpha?=?1
}
func?rotationSecond(view:UIView)?{
????//新值標(biāo)簽,后?let?animation?=?CABasicAnimation(keyPath:?"transform.rotation.x")
????animation.fromValue?=?(355/360)?*?Double.pi
????animation.toValue?=?(10/360)?*?Double.pi
????animation.duration?=?1.0
????animation.repeatCount?=?0
????animation.delegate?=?self?as??CAAnimationDelegate
????view.layer.add(animation,?forKey:?"rotationFirst")
????view.alpha?=?1
}