為Apple Watch設(shè)計APP的時候,首要的準(zhǔn)則是讓它保持簡單。Uber 為 Apple Watch設(shè)計的APP是簡單的最好的例子,原因在于它只有3個頁面:
1.給Uber發(fā)送需求
2.等待回復(fù)
3.讓你知道司機(jī)的信息和狀態(tài)
說到動畫,這個 APP每個界面都有不同的加載動畫,這些動畫原型都非常有趣,下面將用 framer實(shí)現(xiàn)這些動畫。
你自己可以跟著我一步步用 framer實(shí)現(xiàn)這個原型,下圖是將要實(shí)現(xiàn)的效果:
要開始,先下載 Sketch文件(在附件中),在開始前我已經(jīng)用 Sketch自帶的 Apple Watch GUI 在不同畫板之間創(chuàng)建了我們需要的界面,當(dāng)我們把 Sketch文件導(dǎo)入 framer的時候,只有被編組的界面元素才會被 framer識別到,這里要特別注意圖層的組織。
在這個例子中,作為第一個屏幕,需要把加載的圓形和 “Request”按鈕分別編組,因?yàn)橹挥羞@兩個元素需要添加交互和動畫,現(xiàn)在,我們的Sketch文件已經(jīng)準(zhǔn)備好了,讓我們導(dǎo)入到framer。
我們要做的第一件事是清除代碼編輯器,并將您的設(shè)備切換為蘋果手表。工具欄的右上角去Device ?- ??Apple Watch 42mm?-? 選擇其中一個選項(xiàng)。
保持Sketch處于打開狀態(tài),在 framer中點(diǎn)擊工具欄上的導(dǎo)入按鈕或者用快捷鍵 Command+I喚出導(dǎo)入對話框,確認(rèn)對話框中的 Sketch列表后面的 import按鈕處于激活狀態(tài),點(diǎn)擊導(dǎo)入按鈕導(dǎo)入。
framer會自動為導(dǎo)入的原型添加注釋和代碼行,我們可以重命名導(dǎo)入文件的名稱使它變簡短,例如 這里重命名為Uber。
# This imports all the layers for "uber-watch" into
uber= framer.importer.load "imported/uber-watch"
我們應(yīng)該在右側(cè)的預(yù)覽窗口中看到request界面。我們也可以在中間的檢查面板中查看導(dǎo)入的其他的兩個界面(以淡一點(diǎn)的顏色顯示),因?yàn)樗鼈兪遣豢梢姷摹?/p>
要創(chuàng)建加載脈動,我們將創(chuàng)建兩個動畫對象。第一個動畫用到 loadingCircle圖層scale 屬性增大10%。如果要訪問我們導(dǎo)入的任何圖層對象,我們只需輸入導(dǎo)入時的文件變量,后面加點(diǎn),然后加圖層組名。按照這種方式,我們將使用uber.loadingcircle指定動畫的層。定義過度時間為1.5秒的脈沖動畫。
pulseUp = new Animation
layer: uber.loadingCircle
properties:
scale: 1.1
time: 1.5
第二個動畫是把放大的 loadingCircle圖層,恢復(fù)到原來的狀態(tài),同樣是 1.5秒。
pulseDown = new Animation
layer: uber.loadingCircle
properties:
scale: 1.0
time: 1.5
下面將這兩種動畫串聯(lián)起來使得第一種狀態(tài)結(jié)束時自動開始下一種狀態(tài)。
為了串聯(lián)動畫,我們會監(jiān)聽 animationend事件,這個事件的意思當(dāng)一種狀態(tài)結(jié)束時會被調(diào)用。要監(jiān)聽事件,我們將制定動畫對象上面的 “on”關(guān)鍵字來監(jiān)聽這個事件。
當(dāng)pulseUp動畫結(jié)束時,我們希望 pulseDown動畫開始,用動畫對象的 start()函數(shù),因?yàn)檫@是一個函數(shù),所以要在后面加括號。
pulseUp.on Events.AnimationEnd, ->
pulseDown.start()
反過來,當(dāng) pulseDown動畫結(jié)束時,我們希望 pulseUp開始。
pulseDown.on Events.AnimationEnd, ->
pulseUp.start()
需要做的只有開始動畫了。
pulseUp.start()
我們現(xiàn)在應(yīng)該可以看到脈沖動畫了。
為了切換到下一個界面,要給 requestButton圖層創(chuàng)建一個點(diǎn)擊事件。
requestButton.on Events.Click, ->
當(dāng)導(dǎo)入Sketch文件,僅僅第一個畫板界面能看到,為了顯示出 Requesting界面,需要設(shè)置它的 visible屬性為 true,另外為了隱藏 Request界面,需要設(shè)置它的 visible屬性為 false。
uber.requestButton.on Events.Click, ->
uber.request.visible = false
uber.requesting.visible = true
如果仔細(xì)觀察原型,你會發(fā)現(xiàn)過度不平滑,因?yàn)閮H僅是界面瞬間的隱藏和顯示。我們可以做的更好,在 requesting完全顯示之前, 通過設(shè)置 Request界面的透明度的過度動畫為 0.3,然后設(shè)置 requesting的透明度過度到 1。與其創(chuàng)建另一個動畫對象,并告訴它開始,倒不如我們可以直接在事件里面通過圖層名稱.animate啟動的圖層的動畫,然后指定過度動畫的屬性。
uber.requestButton.on Events.Click, ->
uber.requesting.opacity = .3
uber.request.visible = false
uber.requesting.visible = true
uber.requesting.animate
properties:
opacity: 1
現(xiàn)在原型的效果如下:
在真實(shí)的Apple Watch中,點(diǎn)擊 request按鈕后就是展示加載界面,最后才是顯示下一個界面。
在Requesting界面中一致播放脈動加載條的動畫直到有司機(jī)確認(rèn),但是這里有一個回到 request界面的取消按鈕。這里我們不處理取消按鈕的事件,但是你可以自己練習(xí)著完成它。
為了創(chuàng)建這個脈動加載條,先設(shè)置加載條的開始位置,這個加載條就是在 Sketch文件中命名為 requestingLoad的圖層組,這里設(shè)置開始位置通過 scaleX屬性為 0.1且它的不透明度為 0。
uber.requestingLoad.scaleX = .1
uber.requestingLoad.opacity = 0
為加載條創(chuàng)建動畫對象,我們想要它從初始狀態(tài)過度到原始的狀態(tài),且伴隨不透明度的增加。這里要通過 repeat選項(xiàng),重復(fù)動畫幾次。
requestingPulse= new Animation
layer: uber.requestingLoad
properties:
opacity: .8
scaleX: 1
repeat: 5
現(xiàn)在回到第一屏的 request按鈕的點(diǎn)擊事件,切換到過度界面
uber.requestButton.on Events.Click, ->
uber.requesting.opacity = .3
uber.request.visible = false
uber.requesting.visible = true
uber.requesting.animate
properties:
opacity: 1
requestingPulse.start()
現(xiàn)在來添加一個延遲直到想我們希望看到的一樣。
requestingPulse= new Animation
layer: uber.requestingLoad
properties:
opacity: .8
scaleX: 1
delay: .4
time: .9
repeat: 5
這里Uber的 requesting界面的效果如下:
為了過度到下一個界面,需要設(shè)定脈沖加載動畫的重復(fù)次數(shù)。每一次 requestingpulse動畫結(jié)束的時候觸發(fā)的animationend 事件,是添加邏輯最好的地方。
首先創(chuàng)建一個 requestingCount變量,它的作用是記錄脈沖次數(shù),設(shè)置初始值為0。我們把它放在animationend事件代碼塊外面,而不是在里面,因?yàn)槿绻阉锩鎸⒚看沃刂脼?,結(jié)果是一個無限循環(huán)。
requestingCount = 0
在AnimationEnd事件中,它被調(diào)用一次增加計數(shù) 1次,這里我們設(shè)置變量等于它本身加 1即可。
requestingPulse.on Events.AnimationEnd, ->
requestingCount = requestingCount + 1
一種簡單的寫法是在變量后面帶兩個加號,效果和上面是一樣的。
requestingPulse.on Events.AnimationEnd, ->
requestingCount++
在計算次數(shù)等于希望的動畫重復(fù)播放次數(shù),添加切換到下一個界面的邏輯,這里我們設(shè)定希望動畫重復(fù)的次數(shù)為 3。
requestingPulse.on Events.AnimationEnd, ->
requestingCount++
if requestingCount is 3
當(dāng)計算次數(shù)為 3的時候,我們要做和 Request界面切換到 Requesting界面一樣的切換效果來切換到下一個界面。首先設(shè)置下一個界面( enroute)的不透明度為 0.3, requesting圖層的 visible的屬性為 false, enroute界面的 visible的屬性為 true,在讓透明度的過度更平滑一些。
requestingPulse.on Events.AnimationEnd, ->
requestingCount++
if requestingCount is 3
uber.enroute.opacity = .3
uber.requesting.visible = false
uber.enroute.visible = true
uber.enroute.animate
properties:
opacity: 1
代替計算次數(shù)的做法,你也可以選用在 requesting界面經(jīng)過幾秒之后自動切換到下一個界面(提示:使用 Utils.delay 來做)。記住這是一個原型,所以它不必是完美的,所有選用那種方式更快或更好才是關(guān)鍵。
效果如下:
對于Uber的erneute界面,在Sketch文件中命名為 enRouteProgress進(jìn)度條是有旋轉(zhuǎn)動畫的。創(chuàng)建旋轉(zhuǎn)動畫設(shè)置旋轉(zhuǎn)角度為360度, 給旋轉(zhuǎn)動畫添加AnimationEnd事件監(jiān)聽,使旋轉(zhuǎn)結(jié)束繼續(xù)開始下一次旋轉(zhuǎn),重復(fù)次數(shù)可以多一些,也可以設(shè)定旋轉(zhuǎn)一圈所用的時間。
enRouteRotate = new Animation
layer: uber.enRouteProgress
properties:
rotation: 360
repeat: 30
time: 2
可以在前面提到的切換到第三個屏幕的的時候開始播放這個動畫。
requestingPulse.on Events.AnimationEnd, ->
requestingCount++
if requestingCount is 3
uber.enroute.opacity = .3
uber.requesting.visible = false
uber.enroute.visible = true
uber.enroute.animate
properties:
opacity: 1
enRouteRotate.start()
如果你觀察原型,會發(fā)現(xiàn)不是很平滑。
這是因?yàn)閯赢嬊€默認(rèn)是 “ease-in-out”曲線,我們設(shè)置動畫的曲線為 linear。
enRouteRotate= new Animation
layer: uber.enRouteProgress
properties:
rotation: 360
repeat: 30
curve: "linear"
time: 2
現(xiàn)在看起來已經(jīng)平滑多了:
這個屏幕如果可以滑動應(yīng)該還有更多內(nèi)容。如果你觀察 Sketch中的這個畫板,你會看到這樣的結(jié)構(gòu),可以滾動的內(nèi)容被加上了蒙板。
添加滾動事件是通過在 Sketch中命名為 content的組上創(chuàng)建 ScrollComponent 的wrap 函數(shù)來完成的。
scroll= ScrollComponent.wrap uber.content
這里禁用水平方向滾動
scroll.scrollHorizontal = false
現(xiàn)在原型中的第三個界面應(yīng)該能滾動了。
為了讓這個 framer原型更具真實(shí),現(xiàn)在來做汽車的移動。
創(chuàng)建一個小汽車在一條路徑上通過轉(zhuǎn)彎到另一條路徑??梢酝ㄟ^改變 x, y的坐標(biāo)實(shí)現(xiàn)移動,通過轉(zhuǎn)動角度來變化路徑,讓它看起來像沿著路徑運(yùn)動。
moveCarDown= new Animation
layer: uber.delorean
properties:
x: 130
y: 95
time: 20
moveCarLeft= new Animation
layer: uber.delorean
properties:
rotation: -100
x: 125
y: 125
time: 10
通過監(jiān)聽moveCarDown動畫的結(jié)束事件來開始 moveCarLeft動畫建立一個動畫序列。
moveCarDown.on Events.AnimationEnd, ->
moveCarLeft.start()
這里需要在開始 moveCarDown 動畫開始的時候,開始 enRouteRotate動畫
requestingPulse.on Events.AnimationEnd, ->
requestingCount++
if requestingCount is 3
uber.enroute.opacity = .3
uber.requesting.visible = false
uber.enroute.visible = true
uber.enroute.animate
properties:
opacity: 1
enRouteRotate.start()
moveCarDown.start()
小汽車已經(jīng)沿著路徑移動了,為了展示,只制作了簡短的 gif動畫避免gif過大,效果如下:
最后,在5秒之后更新標(biāo)簽 ““En Route”為“Arriving Now” ,這里用延時函數(shù) delay 函數(shù)實(shí)現(xiàn),它的意思是在操作執(zhí)行之前等待幾秒。
moveCarDown.start()
Utils.delay 5, ->
在5秒之后,隱藏當(dāng)前的標(biāo)簽顯示需要展示的標(biāo)簽,這里都用到了 visible屬性。
Utils.delay 5, ->
uber.enRouteTitle.visible = false
uber.arrivingNowTitle.visible = true
恭喜您,我們在 framer中已經(jīng)實(shí)現(xiàn)he Uber Apple Watch app的效果了。