熱評(píng) Tinker

不可多得的好文章,就這么點(diǎn)人可惜了,為作者點(diǎn)贊

彈性是動(dòng)態(tài)設(shè)計(jì)領(lǐng)域中一種常見的表達(dá)方式。不同于影視特效、動(dòng)畫 CG 等設(shè)計(jì)輸出即為最終產(chǎn)物的生產(chǎn)環(huán)境,UI 動(dòng)效始終面臨著動(dòng)效還原帶來的種種問題,彈性動(dòng)效的還原就是其中之一。

當(dāng)設(shè)計(jì)師完成彈性動(dòng)效的設(shè)計(jì),與工程師進(jìn)行交接時(shí),雙方會(huì)發(fā)現(xiàn)參數(shù)無法對(duì)齊——在設(shè)計(jì)工具中調(diào)節(jié)效果的參數(shù)與在工程開發(fā)環(huán)境下設(shè)定效果的參數(shù)無論是名稱還是數(shù)量都存在差異。

基于以上背景,我們通過研究一些常見原型設(shè)計(jì)工具的彈性模擬系統(tǒng),深入彈性系統(tǒng)的動(dòng)力學(xué)原理,與主流平臺(tái)的彈性動(dòng)效實(shí)現(xiàn)原理進(jìn)行匹配,解決了從設(shè)計(jì)工具到工程實(shí)現(xiàn)的彈性動(dòng)效還原問題。

1. 在原型工具中設(shè)計(jì)彈性動(dòng)效

1. Origami Studio

大多數(shù)情況下,我們會(huì)優(yōu)先選擇使用 Origami Studio(以下簡稱 Origami)進(jìn)行高保真原型設(shè)計(jì)。想要在 Origami 中實(shí)現(xiàn)彈性動(dòng)效,需要使用 Pop Animation Patch 來控制動(dòng)畫進(jìn)度(Progress),下面是 Origami 中一個(gè)簡單的彈性動(dòng)效片段示例:

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

可以通過修改 Pop Animation 中的 Bounciness 與 Speed 來調(diào)節(jié)彈性的表現(xiàn),這是兩個(gè)易于理解的參數(shù):Bounciness 的值越大,動(dòng)效越「彈」;Speed 的值越大,動(dòng)效結(jié)束的越「快」。

在 Origami 的 Patch 列表中,位于第 1 個(gè)的 Patch:Bouncy Converter 負(fù)責(zé)將 Pop Animation 的參數(shù) Bounciness 和 Speed 轉(zhuǎn)換為 Friction(摩擦)與 Tension(張力)。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

2. Principle

以友好易用被 UI 設(shè)計(jì)師、交互設(shè)計(jì)師甚至產(chǎn)品經(jīng)理所鐘情的 Principle,在定義彈性動(dòng)效時(shí),將其歸類為一種特殊的曲線 —— Spring,與 Ease In、Ease Out 同級(jí)別。不同的是這條特殊的彈性「曲線」既不是通過 2 點(diǎn)坐標(biāo)來定義,也不能調(diào)節(jié)相應(yīng)的時(shí)長。Principle 中的 Spring 曲線通過修改 Friction 與 Tension 來調(diào)節(jié)彈性的表現(xiàn),通過 Principle 提供的曲線可視化預(yù)覽,我們可以容易的理解這兩個(gè)參數(shù)的作用:Friction 的值越大,動(dòng)效越「彈」;Tension 的值越大,動(dòng)效結(jié)束的越「快」。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

3. 其他

同樣使用 Friction 與 Tension 參數(shù)調(diào)節(jié)彈性表現(xiàn)的還有 ProtoPie Studio、Flinto 等原型工具,而其效果也和 Principle 中的表現(xiàn)一致。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

△ ProtoPie Studio

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

△?Flinto

2. 在主流平臺(tái)實(shí)現(xiàn)彈性動(dòng)效

1. iOS

相對(duì)于將視覺風(fēng)格從擬物改為扁平,iOS 7 對(duì)動(dòng)效的調(diào)整方向剛好相反,采用了更加貼近真實(shí)的設(shè)計(jì)方案,這其中最重要的元素就是廣泛應(yīng)用自然的彈性動(dòng)效。然而你可能會(huì)產(chǎn)生疑惑,因?yàn)椴]有在 iOS 系統(tǒng)中見到「廣泛」的彈性動(dòng)效。這是因?yàn)?iOS 系統(tǒng)動(dòng)效使用最多的,恰恰是一種沒有彈性的彈性動(dòng)效,即應(yīng)用了彈性系統(tǒng)中的臨界阻尼,其主要特征是:物體受彈力的作用最快地恢復(fù)到平衡位置。

從 iOS 7 開始到目前為止,在系統(tǒng)內(nèi)的任何一個(gè)場(chǎng)景:頁面間的切換,彈窗的出現(xiàn)與消失,鍵盤的抬起與落下,文件夾的展開與收起 等等,都使用了處于臨界阻尼狀態(tài)的彈性動(dòng)效。

同時(shí) Apple 也提供了使用這種動(dòng)效的 API:

UIView.animateWithDuration:usingSpringWithDamping:initialSpringVelocity

+ (void)animateWithDuration:(NSTimeInterval)duration
 delay:(NSTimeInterval)delay
 usingSpringWithDamping:(CGFloat)dampingRatio
 initialSpringVelocity:(CGFloat)velocity
 options:(UIViewAnimationOptions)options
 animations:(void (^)(void))animations
 completion:(void (^)(BOOL finished))completion;

該 API 與普通的 UIView 動(dòng)畫相比有兩個(gè)特別的參數(shù):dampingRatio 和 velocity。文檔中對(duì)這兩個(gè)參數(shù)的解釋:該 API 與普通的 UIView 動(dòng)畫相比有兩個(gè)特別的參數(shù):dampingRatio 和 velocity。文檔中對(duì)這兩個(gè)參數(shù)的解釋:

  • dampingRatio:與彈性動(dòng)效靜止時(shí)的阻尼比。值為 1 時(shí)將得到平穩(wěn)減速?zèng)]有彈性的效果(即臨界阻尼狀態(tài)),值越接近 0 震蕩(彈性)程度越大。velocity:彈性動(dòng)效的初始速度。為了平滑地開始動(dòng)效,請(qǐng)把這個(gè)值與之前附著的視圖的速度匹配。

而在此方法中,控制效果表現(xiàn)快慢的是 duration 。

* 注:iOS 系統(tǒng)常用的臨界阻尼效果,參數(shù)為:duration: 0.5, dampingRatio: 1

到了 iOS 9,Apple 在 Core Animation 中增加了彈性動(dòng)效的 API:CASpringAnimation,這個(gè) API 與 iOS 7 提供的用 UIViewSpring 有什么區(qū)別呢?

從實(shí)現(xiàn)的效果來看,二者并無分別,因?yàn)?UIView Animation 本質(zhì)上是對(duì) Core Animation 的封裝,其意義在于面向開發(fā)者更加友好。以 CASpringAnimation 為例,它提供了 4 個(gè)參數(shù)來定義彈性動(dòng)效:damping、initialVelocity、mass 和 stiffness。這四個(gè)參數(shù)更接近彈性動(dòng)效的動(dòng)力學(xué)參數(shù),所以可以使用更貼近真實(shí)的調(diào)配方式來定義彈性效果的表現(xiàn)。

既然 UIView Animation 是對(duì) Core Animation 的封裝,那么同為彈性動(dòng)效的定義方式,UIViewSpring 與 CASpringAnimation 之間應(yīng)該可以互相轉(zhuǎn)換。

關(guān)于 UIViewSpring 與 CASpringAnimation 之間參數(shù)的關(guān)系在 iOS 10 的 提供的新 API

UIViewPropertyAnimator 的參數(shù)中提到:

The damping ratio for the spring is computed from the formula damping / (2 * sqrt (stiffness * mass)).

—— UISpringTimingParameters

全新的 UIViewPropertyAnimator API 相比 UIViewSpring 和 CASpringAnimation 更加強(qiáng)大,也同時(shí)兼容這兩個(gè)舊 API 的參數(shù)定義格式,建議優(yōu)先考慮使用新 API,具體使用方法不再贅述。

2. Android

在 2017 Google I/O 大會(huì)上,Android 平臺(tái)終于有了官方支持的彈性動(dòng)效實(shí)現(xiàn)方案 —— 基于物理的彈性動(dòng)效 SpringAnimation。雖然發(fā)布的晚,但是由于這個(gè) API 被包含在支持庫中,所以的兼容性得到了很好的保障(支持庫更新到 v28.0.0,能夠兼容到 API 14;最新的 API 已被包含在 Android X 中,兼容性得到了更好的保證)。

findViewById(R.id.imageView).also { img ->
 SpringAnimation(img, DynamicAnimation.TRANSLATION_X).apply {
 …
 spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
 spring.stiffness = SpringForce.STIFFNESS_LOW
 setStartVelocity(velocity)
 }
 }

SpringAnimation 提供了 3 個(gè)參數(shù)來定義彈性動(dòng)效:dampingRatio、stiffness 和 setStartVelocity。

文檔中對(duì)這 3 個(gè)參數(shù)進(jìn)行了細(xì)致的解釋與演示:

setStartVelocity:起始速度用于定義在動(dòng)畫開始時(shí)動(dòng)畫屬性更改的速度。

dampingRatio:阻尼比用于描述彈簧震動(dòng)逐漸衰減的狀況。通過使用阻尼比,您可以定義震動(dòng)從一次彈跳到下一次彈跳所衰減的速度有多快。以下列出了可使彈簧彈力衰減的四種不同方式:

  • 當(dāng)阻尼比大于 1 時(shí),會(huì)出現(xiàn)過阻尼現(xiàn)象。它會(huì)使對(duì)象快速地返回到靜止位置。
  • 當(dāng)阻尼比等于 1 時(shí),會(huì)出現(xiàn)臨界阻尼現(xiàn)象。這會(huì)使對(duì)象在最短時(shí)間內(nèi)返回到靜止位置。
  • 當(dāng)阻尼比小于 1 時(shí),會(huì)出現(xiàn)欠阻尼現(xiàn)象。這會(huì)使對(duì)象多次經(jīng)過并越過靜止位置,然后逐漸到達(dá)靜止位置。
  • 當(dāng)阻尼比等于零時(shí),便會(huì)出現(xiàn)無阻尼現(xiàn)象。這會(huì)使對(duì)象永遠(yuǎn)震動(dòng)下去。

stiffness:剛度定義了用于衡量彈簧強(qiáng)度的彈簧常量。不在靜止位置的堅(jiān)硬彈簧可對(duì)所連接的對(duì)象施加更大的力。

3. Web

在 W3C 與 ecma 制定的 Web 標(biāo)準(zhǔn)中與動(dòng)效相關(guān)的內(nèi)容僅有:CSS animation,而其中并不包含定義彈性動(dòng)效的方法。但 Web 領(lǐng)域最具魅力與價(jià)值的地方就在于各種豐富的、充滿創(chuàng)造力的庫,關(guān)于定義彈性動(dòng)效,應(yīng)用較為廣泛的庫有:animejs 等。在 animejs 中定義彈性動(dòng)效需要 4 個(gè)參數(shù):springPhysicsEasing

easing: 'spring(mass, stiffness, damping, velocity)'

3. 從設(shè)計(jì)到實(shí)現(xiàn)的差異

1. 設(shè)計(jì)工具

將上述的信息歸類整理,首先看各種原型設(shè)計(jì)工具之間是能夠達(dá)成一致的,基本都支持 Friction 和 Tension 參數(shù)的輸出,分別定義彈性的程度與效果的快慢,整理如下:

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

2. 開發(fā)平臺(tái)

而在開發(fā)平臺(tái)上,彈性效果的實(shí)現(xiàn)則有共同點(diǎn)也有差異點(diǎn),我們先看這張表:

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

先看共同點(diǎn):初始速度,該參數(shù)在所有 API 中的定義相同,是由外部因素對(duì)彈性動(dòng)效產(chǎn)生的影響參數(shù),其目的是保證對(duì)象從之前的運(yùn)動(dòng)狀態(tài)平滑的過渡到彈性動(dòng)效。值默認(rèn)為 0。

再看其他共同特征,如 iOS UIViewSpring 與 Android SpringAnimation 中定義彈性程度的參數(shù)都是 dampingRatio,且都沒有定義對(duì)象質(zhì)量的參數(shù)。根據(jù)從 UISpringTimingParameters 的參數(shù)描述中獲取到的公式,得出的 dampingRatio 與 damping 之間的關(guān)系:

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

在 Android SpringAnimation 中,已知 dampingRatio 和 stiffness,僅差 mass 的值即可得出 damping,而在 iOS CASpringAnimation 的參數(shù)描述中,mass 的默認(rèn)值為 1。這里我們進(jìn)行了大膽的假設(shè)在 Android SpringAnimation 中,mass 缺省值為 1。使用假設(shè)的 mass,定義 dampingRatio 與 stiffness 的值,帶入到公式求得 damping,再將求出的 damping,與定義的 stiffness、假設(shè)的 mass = 1 帶入到 iOS CASpringAnimation 中,進(jìn)行實(shí)現(xiàn)的效果對(duì)比。令人驚喜的是,兩端的效果表現(xiàn)完全一致,那么假設(shè)驗(yàn)證成立:在 Android SpringAnimation 中,mass 缺省值為 1(這個(gè)結(jié)論也與 Material Design 的設(shè)計(jì)原則相吻合)。以同樣的方法我們也可以假設(shè) iOS UIViewSpring 中 mass 也是使用缺省值 1,但在 iOS UIViewSpring 中定義效果快慢的參數(shù)并不是 stiffness,從值的類型來看也不屬于同一種,所以目前還無法驗(yàn)證這個(gè)假設(shè)。

除去 duration 未明確關(guān)系的 iOS UIViewSpring,其他的開發(fā)平臺(tái) API 已經(jīng)能夠達(dá)成一致關(guān)系,或相同,或可以互相轉(zhuǎn)換。

3. 差異

盡管在設(shè)計(jì)工具和開發(fā)平臺(tái)中,我們通過查閱概念定義與嘗試驗(yàn)證得到了用于定義彈性強(qiáng)度與效果快慢的參數(shù),但設(shè)計(jì)工具是使用 Friction 和 Tension 來表示,而開發(fā)平臺(tái)則使用 damping 和 stiffness 來表示。為了尋找這些參數(shù)之間的關(guān)系,我們更深入地去了解了彈性系統(tǒng)的動(dòng)力學(xué)原理。

4. 彈性系統(tǒng)原理

1. 什么是彈性系統(tǒng)[1]

通過設(shè)定剛度(Stiffness)、質(zhì)量(Mass)、阻尼(Damping)彈性體特征屬性,為目標(biāo)對(duì)象定義彈性,使對(duì)象表現(xiàn)出:被恢復(fù)力(F)驅(qū)動(dòng),受阻尼影響,從起始值向目標(biāo)值(系統(tǒng)平衡位置) 運(yùn)動(dòng)的過程。此運(yùn)動(dòng)系統(tǒng)被稱為阻尼諧振子系統(tǒng),遵守胡克定律[2]。

2. 質(zhì)量(Mass)

彈性系統(tǒng)的受力對(duì)象,會(huì)對(duì)彈性系統(tǒng)產(chǎn)生慣性影響。質(zhì)量越大,震蕩的幅度越大,恢復(fù)到平衡位置的速度越慢。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

3. 阻尼(Damping)

是一個(gè)純數(shù),無真實(shí)的物理意義,用于描述系統(tǒng)在受到擾動(dòng)后震蕩及衰減的情形。阻尼越大,彈性運(yùn)動(dòng)的震蕩次數(shù)越少、震蕩幅度越小。

4. 阻尼比(Damping Ratio)[3]

表示阻尼相對(duì)于臨界阻尼的比值。

  • 無阻尼:阻尼比 -> 0,系統(tǒng)處于永遠(yuǎn)震蕩的狀態(tài);
  • 欠阻尼:阻尼比 < 1,對(duì)象進(jìn)行指數(shù)遞減的震蕩運(yùn)動(dòng);
  • 過阻尼:阻尼比 > 1,對(duì)象進(jìn)行無震蕩的減速運(yùn)動(dòng);
  • 臨界阻尼:阻尼比 = 1,對(duì)象以最短時(shí)間結(jié)束運(yùn)動(dòng)。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

5. 剛度(Stiffness)[4]

是物體抵抗施加的力而形變的程度。在彈性系統(tǒng)中,剛度越大,抵抗變形的能力越強(qiáng),恢復(fù)到平衡位置的速度就越快。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

6. 摩擦力(Friction)

常見的一種造成彈性系統(tǒng)能量損耗的力,摩擦力的方向始終與對(duì)象運(yùn)動(dòng)的方向相反。摩擦力越大,彈性系統(tǒng)的能量損耗越快,震蕩次數(shù)越少,震蕩幅度越小。

7. 張力(Tension)[5]

是由一伸展的弦對(duì)施力者所做的反作用力,張力越大,反作用力越強(qiáng),弦恢復(fù)原狀的速度就越快。

結(jié)論與成果

從彈性系統(tǒng)原理中我們認(rèn)識(shí)到 Friction 與 Damping 的特征接近,Stiffness 與 Tension 的特征接近,再次進(jìn)行假設(shè)并驗(yàn)證:開發(fā)平臺(tái)中的 damping 是否等同于設(shè)計(jì)工具中的 friction;開發(fā)平臺(tái)中的 stiffness 是否等同于設(shè)計(jì)工具中的 tension。最終通過將設(shè)計(jì)工具參數(shù)直接按照假設(shè)的對(duì)應(yīng)關(guān)系進(jìn)行驗(yàn)證,并結(jié)合之前的部分結(jié)論,最終得出:

damping = friction
 stiffness = tension
 [default]mass = 1
 dampingRatio = damping / (2 * sqrt (mass * stiffness))
 [default]velocity = 0

1. 應(yīng)用

根據(jù)結(jié)論,設(shè)計(jì)師在交付彈性動(dòng)效時(shí),可以按照下面的方法提供參數(shù):

2. Origami

設(shè)定 Pop Animation 參數(shù)為

  • Bounciness = 5
  • Speed = 10

使用 Bouncy Converter 得到

  • Friction = 27.0487
  • Tension = 299.61884

則:

  • damping = 27.05
  • stiffness = 299.62
  • mass = 1
  • velocity = 0
  • dampingRatio = 0.78

For Android

SpringAnimation(object, DynamicAnimation.[property]).apply {
 …
 spring.dampingRatio = 0.78f
 spring.stiffness = 299.62f
 setStartVelocity(0f)
 }

For iOS

let spring = CASpringAnimation(keyPath:[property])
 spring.stiffness = 299.62
 spring.damping = 27.05
 spring.mass = 1
 spring.initialVelocity = 0

3. Principle

設(shè)定 Spring 曲線參數(shù)為

  • Tension = 381.47
  • Friction = 20.17

則:

  • damping = 20.17
  • stiffness = 381.47
  • mass = 1
  • velocity = 0
  • dampingRatio = 0.52

For Android

SpringAnimation(object, DynamicAnimation.[property]).apply {
 …
 spring.dampingRatio = 0.52f
 spring.stiffness = 381.47f
 setStartVelocity(0f)
 }

For iOS

let spring = CASpringAnimation(keyPath:[property])
 spring.stiffness = 381.47
 spring.damping = 20.17
 spring.mass = 1
 spring.initialVelocity = 0

4. UIViewSpring 的 duration 參數(shù)

上述的結(jié)論中并不包含關(guān)于 iOS UIViewSpring 的 duration 參數(shù)的相關(guān)信息,我們繼續(xù)進(jìn)行深入研究。

首先這次 Apple 并沒有像 dampingRatio 一樣在文檔中提供 duration 與其他參數(shù)直接的轉(zhuǎn)換關(guān)系,也僅有 iOS UIViewSpring API 唯一一個(gè)是以 duration 定義彈性動(dòng)效的效果快慢,從開發(fā)平臺(tái)能獲取到的信息就有限了。

在設(shè)計(jì)工具中,確實(shí)有能夠支持以 duration 和 dampingRatio 定義彈性動(dòng)效的,其中有我們熟悉的 Flinto 和以開源的 Framer.js 構(gòu)建的原型設(shè)計(jì)工具 Framer Studio。

Framer Studio 可以默認(rèn)使用 time 與 damping 定義彈性動(dòng)效,在彈性動(dòng)效的代碼區(qū)域,右鍵菜單可以看到 Copy Animation 中包含 2 個(gè)選項(xiàng),復(fù)制 Damping and Duration 的值,可以看到在 Framer Studio 中用于定義彈性動(dòng)效的默認(rèn)方法中:time 對(duì)應(yīng)的是 duration,damping 對(duì)應(yīng)的是 dampingRatio。

同時(shí) Framer Studio 也支持使用 Friction 與 Tension 來定義彈性動(dòng)效,在相應(yīng)的代碼區(qū)域仍舊可以右鍵 Copy Animation,也就是 Framer Studio 實(shí)現(xiàn)了 dampingRatio、duration 與 Friction、Tension 參數(shù)之間的互相轉(zhuǎn)換。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

得益于 Framer.js [7] 項(xiàng)目是開源的,我們?cè)?Github 上,找到了關(guān)于兩套參數(shù)互相轉(zhuǎn)換的參數(shù),其中具有關(guān)鍵意義的已知 Friction 與 Tension,轉(zhuǎn)換 duration 的方法詳情如下:

# Tries to compute the duration of a spring,
 # but can't for certain velocities and if dampingRatio >= 1
 # In those cases it will return null
 epsilon = 0.001
 computeDuration = (tension, friction, velocity = 0, mass = 1) ->
 dampingRatio = computeDampingRatio(tension, friction)
 undampedFrequency = Math.sqrt(tension / mass)
 # This is basically duration extracted out of the envelope functions
 if dampingRatio < 1
 a = Math.sqrt(1 - Math.pow(dampingRatio, 2))
 b = velocity / (a * undampedFrequency)
 c = dampingRatio / a
 d = - ((b - c) / epsilon)
 if d <= 0
 return null
 duration = Math.log(d) / (dampingRatio * undampedFrequency)
 else
 return null
 return duration
 
引用自:SpringCurveValueConverter.coffee host with ??Github

5. 轉(zhuǎn)換器

從 Framer.js 開源項(xiàng)目中獲取到了 dampingRatio、duration 與 Friction、Tension 互相轉(zhuǎn)換的方法,而從 Origami [6] 的開源項(xiàng)目中我們也獲取到了將 Bouncy Converter:將 Pop Animation 的 Bounciness 和 Speed 轉(zhuǎn)換為 Friction 與 Tension。由此為了方便使用,我們開發(fā)了一個(gè)簡單的 Web 轉(zhuǎn)換器,通過選擇各種原型工具或開發(fā)平臺(tái)的定義彈性動(dòng)效的參數(shù),能夠獲取到任意平臺(tái)的彈性動(dòng)效還原代碼參考。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

在圖中可以看到我們也提供了 CSS 版本的關(guān)鍵幀動(dòng)畫代碼參考,我們對(duì)關(guān)鍵幀的輸出進(jìn)行了平滑處理,在體積、性能與效果之間得到了良好的平衡關(guān)系。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

6. Origami Patch

強(qiáng)大的自定義能力是我們選擇優(yōu)先使用 Origami Studio 的一個(gè)原因,在已知公式的情況下,我們可以模擬出各個(gè)平臺(tái) API 定義彈性動(dòng)效的參數(shù)組合來設(shè)計(jì)動(dòng)效:

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

插值模擬

1. 自定義動(dòng)態(tài)插值器

在打通了彈性動(dòng)效參數(shù)的從設(shè)計(jì)工具到 iOS UIViewSpring、iOS CASpringAnimation、Android SpringAnimation、CSS Keyframe 之間的參數(shù)轉(zhuǎn)換后,彈性動(dòng)效的還原問題已經(jīng)能在絕大多數(shù)場(chǎng)景解決了,但由于 Android 的彈性動(dòng)效 API 需要引入支持庫,而在一些特殊情況下可能無法引入,這時(shí)候我們可以選擇一種降級(jí)方案:使用自定義插值器模擬彈性動(dòng)效。

在 Android 開發(fā)中,插值器其實(shí)就是設(shè)計(jì)師常說的動(dòng)效曲線,用于描述在定義時(shí)間內(nèi)值的變化規(guī)律。理論上你可以利用此特性定義出任何動(dòng)畫形式,而我們想要定義彈性動(dòng)效,首先需要的就是用于描述彈性運(yùn)動(dòng)的函數(shù)公式。

再次感嘆開源的偉大,在 Juraj Novák 的 Interpolator 項(xiàng)目中,提供了一個(gè)以 factor 為調(diào)整彈性程度參數(shù)的彈性運(yùn)動(dòng)描述函數(shù)。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

所以根據(jù)這個(gè)函數(shù)我們可以在 Android 中自定義插值器:

import android.view.animation.BaseInterpolator;
 public class SpringInterpolator extends BaseInterpolator{
 private float mFactor;

public SpringInterpolator() {
 this.mFactor = 0.5f;
 }

public SpringInterpolator(float mFactor) {
 this.mFactor = mFactor;
 }
 @Override
 public float getInterpolation(float input) {
 if (input == 0.0f || input == 1.0f)
 return input;
 else {
 float value = (float) (Math.pow(2, -10 * input) * 
Math.sin((input - mFactor / 4.0d) * (2.0d * Math.PI) / mFactor) + 1);
 return value;
 }
 }
 }

在 Android 的插值動(dòng)效中,描述動(dòng)效快慢的參數(shù)統(tǒng)一為 duration,我們可以利用來自 Framer.js 的轉(zhuǎn)換方法得出一個(gè) duration 參數(shù)。但為了還原效果,我們還需要知道 factor 與 Friction 和 Tension 之間的關(guān)系。遺憾的是我們并沒有查閱到相關(guān)資料,也沒能夠通過假設(shè)證明,最終我們選擇了使用數(shù)學(xué)辦法進(jìn)行從 Friction、Tension 到 factor、duration 的轉(zhuǎn)換。
首先我們將 iOS UIViewSpring 中的 duration 直接賦予到 Android SpringInterpolator 動(dòng)畫,用于描述效果的快慢。

由于彈性運(yùn)動(dòng)是一個(gè)衰減過程,所以我們可以把運(yùn)動(dòng)過程中達(dá)到的最大值當(dāng)作是與彈性程度相關(guān)的量。在遵循胡克定律的條件下,當(dāng)兩個(gè)彈性運(yùn)動(dòng)的最大值相同,且運(yùn)動(dòng)快慢也相同的時(shí)候,可以近似地認(rèn)為二者的彈性效果是相同的。于是在已知 Friction 和 Tension 的情況下,求出當(dāng)前彈性動(dòng)效的最大值,再對(duì) Android

SpringInterpolator 進(jìn)行不同 factor 的最大值的遍歷與匹配,最終得出一個(gè)近似效果對(duì)應(yīng)的 factor 值。由于需要大量的重復(fù)計(jì)算,且沒有通用公式,此方法僅被應(yīng)用在 Web 轉(zhuǎn)換器中。

2. 自定義 XML 插值器

在一些極端情況下,Android 無法使用動(dòng)態(tài)方法定義動(dòng)畫,必須使用 XML 時(shí),我們也可以利用 XML 定義插值器,但此時(shí)的靈活性就更低了,因?yàn)樾枰褟椥孕Ч嫵鰜怼?/p>

插值器是定義在點(diǎn) (0, 0) 到點(diǎn)(1, 1) 區(qū)域內(nèi)的一段連續(xù)曲線,Android 支持使用路徑信息的形式來描述插值器,那么關(guān)鍵點(diǎn)就在于如何輸出符合效果的路徑信息。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

使用工具(如 D3.js)按照彈性系統(tǒng)的運(yùn)動(dòng)學(xué)公式生成 SVG,將起點(diǎn)和終點(diǎn)限制在 1*1 px 的畫布內(nèi),同時(shí)注意調(diào)整坐標(biāo)軸,畫布坐標(biāo)軸以左上角為原點(diǎn) (0, 0),插值坐標(biāo)軸卻是以左下角為原點(diǎn) (0, 0)。

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

把輸出的路徑信息從SVG中拷貝到自定義 XML 插值器的 pathData 中,我們就得到了一個(gè)效果固定的自定義 XML 彈性插值器:

總結(jié)

在經(jīng)歷了復(fù)雜的研究與驗(yàn)證過程,我們知道了:設(shè)計(jì)工具中的參數(shù)是能夠與開發(fā)平臺(tái)的參數(shù)對(duì)齊的,只是換了種叫法 —— Friction 就是 Damping,Tension 就是 Stiffness;大膽假設(shè)有的時(shí)候是憑直覺,但直覺背后可能存在著一些潛在因素的影響——在沒有定義 Mass 的時(shí)候,使用的是缺省值 1;雖然很艱難,但是我們終究還是在 Android 的全場(chǎng)景還原了彈性動(dòng)效;開源的力量是偉大的,再次感謝開源社區(qū)。

拓展閱讀:

附錄

歡迎關(guān)注作者微信公眾號(hào):「今日頭條UED」

大廠硬核干貨!深入分析彈性動(dòng)效的應(yīng)用及原理

收藏 113
點(diǎn)贊 16

復(fù)制本文鏈接 文章為作者獨(dú)立觀點(diǎn)不代表優(yōu)設(shè)網(wǎng)立場(chǎng),未經(jīng)允許不得轉(zhuǎn)載。