第四個(gè)案例,加載效果
首先我們需要梳理一下加載的過(guò)程,用靜態(tài)界面標(biāo)識(shí)出來(lái),所謂磨刀不誤砍柴工。
思路整理:
- 點(diǎn)擊按鈕
- 按鈕變色
- 文字放大并縮小
- 按鈕變化樣式縮小
- 按鈕開(kāi)始加載
- 加載完成,按鈕變化樣式還原
- 結(jié)束圖標(biāo)出現(xiàn)
思路清楚后,接下來(lái)就是用 Framer 逐一的實(shí)現(xiàn)了。
點(diǎn)擊按鈕
我們先把需要的元素用 Framer 畫(huà)出來(lái),有按鈕,按鈕上的文字,最后出現(xiàn)符號(hào)。把所有動(dòng)畫(huà)寫(xiě)完以后,點(diǎn)擊按鈕觸發(fā)動(dòng)畫(huà)。
# Define variables
width = 240 * 2
height = 48 * 2
green = '#3BE0A5'
grey = '#CECBD4'
# 設(shè)置背景色
Screen.backgroundColor = "#fafafa"
# 設(shè)置默認(rèn)動(dòng)畫(huà)參數(shù)
Framer.Defaults.Animation =
curve: Bezier.easeIn
time: .3
# 畫(huà)按鈕
button = new Layer
x: Align.center
y: Align.center
height: 48 * 2
width: 240 * 2
borderColor: green
borderWidth: 2
borderRadius: 100
backgroundColor: null
# 記錄初始 x 值
buttonOriginX = button.x
# 畫(huà)文字
text = new TextLayer
parent: button
text: 'SUBMIT'
fontSize: 32
fontFamily: 'Roboto'
color: green
x: Align.center
y: Align.center
originY: 1
# 畫(huà)符號(hào)
symbol = new TextLayer
parent: button
text: '?'
fontSize: 50
fontFamily: 'Roboto'
color: '#fff'
x: Align.center
y: Align.center
opacity: 0
注意:
- 這里記錄一下按鈕的初始 x 位置,因?yàn)樽儞Q長(zhǎng)寬是以(0,0)為固定點(diǎn)的。按鈕縮小時(shí),需要同時(shí)移動(dòng) x 坐標(biāo)讓按鈕視覺(jué)上看上出是以 (center, center) 為固定點(diǎn)。
按鈕變色
按鈕變色給用戶(hù)點(diǎn)擊提供反饋,同時(shí)邊框消失。
# 按鈕變色
buttonChangeColor = new Animation button,
borderWidth: 0
backgroundColor: green
options:
time: .2
文字放大并縮小
按鈕變色的同時(shí)文字放大,提供 Anticipation, 放大后文字再縮小消失。
# 文字放大并縮小
textScaleUp = new Animation text,
scale: 1.1
options:
time: .2
delay: .1
textScaleDown = new Animation text,
scale: 0
opacity: 0
options:
time: .2
按鈕變換樣式縮小
按鈕變成原型,背景色消失,邊框變粗且變成灰色。
# 按鈕變換樣式縮小
buttonChangeStyle = new Animation button,
borderWidth: 8
borderColor: grey
backgroundColor: 'rgba(59,224,165,0)'
width: button.height
borderRadius: '50%'
x: buttonOriginX + (width - button.height) / 2
option:
time:.4
delay: .2
注意:
-
x: (width - button.height) / 2
記錄的是中心偏移的距離,即 x 需要增加的距離
按鈕開(kāi)始加載
這是最復(fù)雜的一步,這也是我喜歡 Framer 的原因。當(dāng)你用 AE 實(shí)現(xiàn)一個(gè)看似很簡(jiǎn)單的功能,開(kāi)發(fā)可能需要很長(zhǎng)的時(shí)間才能實(shí)現(xiàn)。這時(shí)作為設(shè)計(jì)師,你就需要權(quán)衡項(xiàng)目進(jìn)度能否允許復(fù)雜動(dòng)效的開(kāi)發(fā)。
使用了 Svg 的 stroke-dashoffset, stroke-dasharray 來(lái)模擬實(shí)現(xiàn)圓的加載。同時(shí)使用 Utils.modulate() 映射一個(gè)特定的 Bezire 函數(shù)效果。推薦一個(gè)調(diào)節(jié) Bizire 曲線的好網(wǎng)站,cubic-bezire.
# 按鈕開(kāi)始加載
# svg 中即將用到的屬性
radius = (96-8) / 2
border = 8
viewBox = (radius * 2) + border
# buttonBorder 用 svg 繪制
buttonBorder = new Layer
x: Align.center
y: Align.center
width: viewBox
height: viewBox
rotation: -90
backgroundColor: null
buttonBorder.pathLength = 2 * Math.PI * radius
buttonBorder.html = "
<svg viewBox='#{-border/2} #{-border/2} #{viewBox} #{viewBox}'>
<circle fill='none'
stroke='#{green}'
stroke-width = '#{border}'
stroke-dasharray = '#{buttonBorder.pathLength}'
stroke-dashoffset = '#{buttonBorder.pathLength}'
cx = '#{radius}'
cy = '#{radius}'
r = '#{radius}'>
</svg>"
buttonBorder.path = document.querySelector('svg circle')
# buttonBorder 的運(yùn)動(dòng)
proxy = new Layer
x: 1
visible: false
proxy.on 'change:x', ->
offset = Utils.modulate(@.x, [0, 200], [buttonBorder.pathLength, 0])
buttonBorder.path.setAttribute 'stroke-dashoffset', offset
buttonBorderAnimation = new Animation proxy,
x: 200
options:
time: 1.5
# 自定義 Bezire 函數(shù)
curve: Bezier(.9,.1,.83,.67)
注意:
- 實(shí)際做的時(shí)候,我花了很長(zhǎng)事件把兩個(gè)圓環(huán)對(duì)齊,一開(kāi)始不理解為什么對(duì)不齊。后來(lái)發(fā)現(xiàn),F(xiàn)ramer 在添加 border 時(shí),border 是包含在總 width 內(nèi)的,即 border 寬度不影響該元素的總寬度。但是 svg 矢量畫(huà)圓的時(shí)候,是以 border 的中點(diǎn)開(kāi)始畫(huà)的。所以相比原圖層,會(huì)偏移 border / 2 的寬度。所以 Svg 圖像的原點(diǎn)需要偏移 (- borderWidth/2, borderWidth/2)進(jìn)行對(duì)齊。
加載完成,按鈕變化樣式還原
按鈕邊框消失,寬度和圓角還原,背景色出現(xiàn)。
# 加載完成,按鈕變化樣式還原
buttonChangeBig = new Animation button,
borderWidth: 0
width: width
x: buttonOriginX
borderRadius: 100
backgroundColor: green
options:
delay: .1
time: .3
curve: Bezier.easeInOut
注意:
- 同理,x 坐標(biāo)還原到初始狀態(tài)
結(jié)束圖標(biāo)出現(xiàn)
結(jié)束圖標(biāo)出現(xiàn),opacity 和 scale 從 0 到 1,同時(shí)加一個(gè) Spring 曲線增加趣味性。
# 結(jié)束圖標(biāo)出現(xiàn)
symbolAppear = new Animation symbol,
opacity: 1
scale: 1
options:
delay: .1
time: .2
curve: Spring(damping: .6)
動(dòng)畫(huà)執(zhí)行
將之前的動(dòng)畫(huà)思路,用代碼按順序執(zhí)行。
# 點(diǎn)擊按鈕事件,動(dòng)畫(huà)開(kāi)始
button.on Events.Click, ->
# 背景色變化
buttonChangeColor.start()
# 文字變顏色,并放大
text.color = '#fff'
textScaleUp.start()
# 文字放大結(jié)束后,文字變小,按鈕縮小
textScaleUp.on Events.AnimationEnd, ->
textScaleDown.start()
buttonChangeStyle.start()
# 按鈕縮小結(jié)束后,邊框開(kāi)始加載
buttonChangeStyle.on Events.AnimationEnd, ->
buttonBorderAnimation.start()
# 邊框加載結(jié)束后,按鈕開(kāi)始變大
buttonBorderAnimation.on Events.AnimationEnd, ->
buttonBorder.destroy()
buttonChangeBig.start()
# 按鈕變大結(jié)束后,符號(hào)出現(xiàn)
buttonChangeBig.on Events.AnimationEnd, ->
symbolAppear.start()
這是一個(gè)不斷調(diào)試的過(guò)程,每一個(gè)動(dòng)畫(huà)的 time, delay, curve 三個(gè)參數(shù)都需要反復(fù)調(diào)整直到產(chǎn)生一個(gè)滿意的視覺(jué)效果。我會(huì)先憑經(jīng)驗(yàn)輸入一個(gè)大概的值,在根據(jù)實(shí)際情況進(jìn)行微調(diào)。你可以調(diào)的時(shí)候畫(huà)一個(gè) timeline 作為草稿。
小結(jié)
四個(gè)案例后,筆者對(duì)一般的效果已經(jīng)有了一定了解。但是深感自己在 Web Animation 底層知識(shí)上的薄弱。比如:動(dòng)畫(huà)創(chuàng)意,動(dòng)效節(jié)奏,Svg, Canvas 的 JS 實(shí)現(xiàn)。所以筆者接下來(lái)會(huì)多了解 Web Animation 方面的理論知識(shí),為做更好的動(dòng)效打下基礎(chǔ)。我們下期見(jiàn)。
Reference
Framer 線上演示:https://framer.cloud/jvcnt
完整代碼
# Coded by Joey in April, 2017
# 導(dǎo)入 Roboto 字體
Utils.insertCSS("@import 'https://fonts.googleapis.com/css?family=Roboto:300,400,700';")
# Define variables
width = 240 * 2
height = 48 * 2
green = '#3BE0A5'
grey = '#CECBD4'
# 設(shè)置背景色
Screen.backgroundColor = "#fafafa"
# 設(shè)置默認(rèn)動(dòng)畫(huà)參數(shù)
Framer.Defaults.Animation =
curve: Bezier.easeIn
time: .3
# 畫(huà)按鈕
button = new Layer
x: Align.center
y: Align.center
height: 48 * 2
width: 240 * 2
borderColor: green
borderWidth: 2
borderRadius: 100
backgroundColor: null
# 記錄初始 x 值
buttonOriginX = button.x
# 畫(huà)文字
text = new TextLayer
parent: button
text: 'SUBMIT'
fontSize: 32
fontFamily: 'Roboto'
color: green
x: Align.center
y: Align.center
originY: 1
# 畫(huà)符號(hào)
symbol = new TextLayer
parent: button
text: '?'
fontSize: 50
fontFamily: 'Roboto'
color: '#fff'
x: Align.center
y: Align.center
opacity: 0
scale: 0
# 按鈕變色
buttonChangeColor = new Animation button,
borderWidth: 0
backgroundColor: green
options:
time: .2
# 文字放大并縮小
textScaleUp = new Animation text,
scale: 1.1
options:
time: .2
delay: .1
textScaleDown = new Animation text,
scale: 0
opacity: 0
options:
time: .2
# 按鈕變換樣式縮小
buttonChangeStyle = new Animation button,
borderWidth: 8
borderColor: grey
backgroundColor: 'rgba(59,224,165,0)'
width: button.height
borderRadius: '50%'
x: buttonOriginX + (width - button.height) / 2
option:
time:.4
delay: .2
# 按鈕開(kāi)始加載
# svg 中即將用到的屬性
radius = (96-8) / 2
border = 8
viewBox = (radius * 2) + border
# buttonBorder 用 svg 繪制
buttonBorder = new Layer
x: Align.center
y: Align.center
width: viewBox
height: viewBox
rotation: -90
backgroundColor: null
buttonBorder.pathLength = 2 * Math.PI * radius
buttonBorder.html = "
<svg viewBox='#{-border/2} #{-border/2} #{viewBox} #{viewBox}'>
<circle fill='none'
stroke='#{green}'
stroke-width = '#{border}'
stroke-dasharray = '#{buttonBorder.pathLength}'
stroke-dashoffset = '#{buttonBorder.pathLength}'
cx = '#{radius}'
cy = '#{radius}'
r = '#{radius}'>
</svg>"
buttonBorder.path = document.querySelector('svg circle')
# buttonBorder 的運(yùn)動(dòng)
proxy = new Layer
x: 1
visible: false
proxy.on 'change:x', ->
offset = Utils.modulate(@.x, [0, 200], [buttonBorder.pathLength, 0])
buttonBorder.path.setAttribute 'stroke-dashoffset', offset
buttonBorderAnimation = new Animation proxy,
x: 200
options:
time: 1.5
# 自定義 Bezire 函數(shù)
curve: Bezier(.9,.1,.83,.67)
# 加載完成,按鈕變化樣式還原
buttonChangeBig = new Animation button,
borderWidth: 0
width: width
x: buttonOriginX
borderRadius: 100
backgroundColor: green
options:
delay: .1
time: .3
curve: Bezier.easeInOut
# 結(jié)束圖標(biāo)出現(xiàn)
symbolAppear = new Animation symbol,
opacity: 1
scale: 1
options:
delay: .1
time: .2
curve: Spring(damping: .6)
# 點(diǎn)擊按鈕事件,動(dòng)畫(huà)開(kāi)始
button.on Events.Click, ->
# 背景色變化
buttonChangeColor.start()
# 文字變顏色,并放大
text.color = '#fff'
textScaleUp.start()
# 文字放大結(jié)束后,文字變小,按鈕縮小
textScaleUp.on Events.AnimationEnd, ->
textScaleDown.start()
buttonChangeStyle.start()
# 按鈕縮小結(jié)束后,邊框開(kāi)始加載
buttonChangeStyle.on Events.AnimationEnd, ->
buttonBorderAnimation.start()
# 邊框加載結(jié)束后,按鈕開(kāi)始變大
buttonBorderAnimation.on Events.AnimationEnd, ->
buttonBorder.destroy()
buttonChangeBig.start()
# 按鈕變大結(jié)束后,符號(hào)出現(xiàn)
buttonChangeBig.on Events.AnimationEnd, ->
symbolAppear.start()