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