彈性是動態設計領域中一種常見的表達方式。不同於影視特效、動畫 CG 等設計輸出即為最終產物的生產環境,UI 動效始終面臨着動效還原帶來的種種問題,彈性動效的還原就是其中之一。
當設計師完成彈性動效的設計,與工程師進行交接時,雙方會發現參數無法對齊——在設計工具中調節效果的參數與在工程開發環境下設定效果的參數無論是名稱還是數量都存在差異。
基於以上背景,我們通過研究一些常見原型設計工具的彈性模擬系統,深入彈性系統的動力學原理,與主流平台的彈性動效實現原理進行匹配,解決了從設計工具到工程實現的彈性動效還原問題。
1. 在原型工具中設計彈性動效
1. Origami Studio
大多數情況下,我們會優先選擇使用 Origami Studio(以下簡稱 Origami)進行高保真原型設計。想要在 Origami 中實現彈性動效,需要使用 Pop Animation Patch 來控制動畫進度(Progress),下面是 Origami 中一個簡單的彈性動效片段示例:
可以通過修改 Pop Animation 中的 Bounciness 與 Speed 來調節彈性的表現,這是兩個易於理解的參數:Bounciness 的值越大,動效越「彈」;Speed 的值越大,動效結束的越「快」。
在 Origami 的 Patch 列表中,位於第 1 個的 Patch:Bouncy Converter 負責將 Pop Animation 的參數 Bounciness 和 Speed 轉換為 Friction(摩擦)與 Tension(張力)。
2. Principle
以友好易用被 UI 設計師、交互設計師甚至產品經理所鍾情的 Principle,在定義彈性動效時,將其歸類為一種特殊的曲線 —— Spring,與 Ease In、Ease Out 同級別。不同的是這條特殊的彈性「曲線」既不是通過 2 點坐標來定義,也不能調節相應的時長。Principle 中的 Spring 曲線通過修改 Friction 與 Tension 來調節彈性的表現,通過 Principle 提供的曲線可視化預覽,我們可以容易的理解這兩個參數的作用:Friction 的值越大,動效越「彈」;Tension 的值越大,動效結束的越「快」。
3. 其他
同樣使用 Friction 與 Tension 參數調節彈性表現的還有 ProtoPie Studio、Flinto 等原型工具,而其效果也和 Principle 中的表現一致。
△ ProtoPie Studio
△ Flinto
2. 在主流平台實現彈性動效
1. iOS
相對於將視覺風格從擬物改為扁平,iOS 7 對動效的調整方向剛好相反,採用了更加貼近真實的設計方案,這其中最重要的元素就是廣泛應用自然的彈性動效。然而你可能會產生疑惑,因為並沒有在 iOS 系統中見到「廣泛」的彈性動效。這是因為 iOS 系統動效使用最多的,恰恰是一種沒有彈性的彈性動效,即應用了彈性系統中的臨界阻尼,其主要特徵是:物體受彈力的作用最快地恢復到平衡位置。
從 iOS 7 開始到目前為止,在系統內的任何一個場景:頁面間的切換,彈窗的出現與消失,鍵盤的抬起與落下,文件夾的展開與收起 等等,都使用了處於臨界阻尼狀態的彈性動效。
同時 Apple 也提供了使用這種動效的 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 動畫相比有兩個特別的參數:dampingRatio 和 velocity。文檔中對這兩個參數的解釋:該 API 與普通的 UIView 動畫相比有兩個特別的參數:dampingRatio 和 velocity。文檔中對這兩個參數的解釋:
- dampingRatio:與彈性動效靜止時的阻尼比。值為 1 時將得到平穩減速沒有彈性的效果(即臨界阻尼狀態),值越接近 0 震蕩(彈性)程度越大。velocity:彈性動效的初始速度。為了平滑地開始動效,請把這個值與之前附着的視圖的速度匹配。
而在此方法中,控制效果表現快慢的是 duration 。
* 注:iOS 系統常用的臨界阻尼效果,參數為:duration: 0.5, dampingRatio: 1
到了 iOS 9,Apple 在 Core Animation 中增加了彈性動效的 API:CASpringAnimation,這個 API 與 iOS 7 提供的用 UIViewSpring 有什麼區別呢?
從實現的效果來看,二者並無分別,因為 UIView Animation 本質上是對 Core Animation 的封裝,其意義在於面向開發者更加友好。以 CASpringAnimation 為例,它提供了 4 個參數來定義彈性動效:damping、initialVelocity、mass 和 stiffness。這四個參數更接近彈性動效的動力學參數,所以可以使用更貼近真實的調配方式來定義彈性效果的表現。
既然 UIView Animation 是對 Core Animation 的封裝,那麼同為彈性動效的定義方式,UIViewSpring 與 CASpringAnimation 之間應該可以互相轉換。
關於 UIViewSpring 與 CASpringAnimation 之間參數的關係在 iOS 10 的 提供的新 API
UIViewPropertyAnimator 的參數中提到:
The damping ratio for the spring is computed from the formula damping / (2 * sqrt (stiffness * mass)).
—— UISpringTimingParameters
全新的 UIViewPropertyAnimator API 相比 UIViewSpring 和 CASpringAnimation 更加強大,也同時兼容這兩箇舊 API 的參數定義格式,建議優先考慮使用新 API,具體使用方法不再贅述。
2. Android
在 2017 Google I/O 大會上,Android 平台終於有了官方支持的彈性動效實現方案 —— 基於物理的彈性動效 SpringAnimation。雖然發布的晚,但是由於這個 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 個參數來定義彈性動效:dampingRatio、stiffness 和 setStartVelocity。
文檔中對這 3 個參數進行了細緻的解釋與演示:
setStartVelocity:起始速度用於定義在動畫開始時動畫屬性更改的速度。
dampingRatio:阻尼比用於描述彈簧震動逐漸衰減的狀況。通過使用阻尼比,您可以定義震動從一次彈跳到下一次彈跳所衰減的速度有多快。以下列出了可使彈簧彈力衰減的四種不同方式:
- 當阻尼比大於 1 時,會出現過阻尼現象。它會使對象快速地返回到靜止位置。
- 當阻尼比等於 1 時,會出現臨界阻尼現象。這會使對象在最短時間內返回到靜止位置。
- 當阻尼比小於 1 時,會出現欠阻尼現象。這會使對象多次經過並越過靜止位置,然後逐漸到達靜止位置。
- 當阻尼比等於零時,便會出現無阻尼現象。這會使對象永遠震動下去。
stiffness:剛度定義了用於衡量彈簧強度的彈簧常量。不在靜止位置的堅硬彈簧可對所連接的對象施加更大的力。
3. Web
在 W3C 與 ecma 制定的 Web 標準中與動效相關的內容僅有:CSS animation,而其中並不包含定義彈性動效的方法。但 Web 領域最具魅力與價值的地方就在於各種豐富的、充滿創造力的庫,關於定義彈性動效,應用較為廣泛的庫有:animejs 等。在 animejs 中定義彈性動效需要 4 個參數:springPhysicsEasing
easing: 'spring(mass, stiffness, damping, velocity)'
3. 從設計到實現的差異
1. 設計工具
將上述的信息歸類整理,首先看各種原型設計工具之間是能夠達成一致的,基本都支持 Friction 和 Tension 參數的輸出,分別定義彈性的程度與效果的快慢,整理如下:
2. 開發平台
而在開發平台上,彈性效果的實現則有共同點也有差異點,我們先看這張表:
先看共同點:初始速度,該參數在所有 API 中的定義相同,是由外部因素對彈性動效產生的影響參數,其目的是保證對象從之前的運動狀態平滑的過渡到彈性動效。值默認為 0。
再看其他共同特徵,如 iOS UIViewSpring 與 Android SpringAnimation 中定義彈性程度的參數都是 dampingRatio,且都沒有定義對象質量的參數。根據從 UISpringTimingParameters 的參數描述中獲取到的公式,得出的 dampingRatio 與 damping 之間的關係:
在 Android SpringAnimation 中,已知 dampingRatio 和 stiffness,僅差 mass 的值即可得出 damping,而在 iOS CASpringAnimation 的參數描述中,mass 的默認值為 1。這裏我們進行了大膽的假設在 Android SpringAnimation 中,mass 缺省值為 1。使用假設的 mass,定義 dampingRatio 與 stiffness 的值,帶入到公式求得 damping,再將求出的 damping,與定義的 stiffness、假設的 mass = 1 帶入到 iOS CASpringAnimation 中,進行實現的效果對比。令人驚喜的是,兩端的效果表現完全一致,那麼假設驗證成立:在 Android SpringAnimation 中,mass 缺省值為 1(這個結論也與 Material Design 的設計原則相吻合)。以同樣的方法我們也可以假設 iOS UIViewSpring 中 mass 也是使用缺省值 1,但在 iOS UIViewSpring 中定義效果快慢的參數並不是 stiffness,從值的類型來看也不屬於同一種,所以目前還無法驗證這個假設。
除去 duration 未明確關係的 iOS UIViewSpring,其他的開發平台 API 已經能夠達成一致關係,或相同,或可以互相轉換。
3. 差異
儘管在設計工具和開發平台中,我們通過查閱概念定義與嘗試驗證得到了用於定義彈性強度與效果快慢的參數,但設計工具是使用 Friction 和 Tension 來表示,而開發平台則使用 damping 和 stiffness 來表示。為了尋找這些參數之間的關係,我們更深入地去了解了彈性系統的動力學原理。
4. 彈性系統原理
1. 什麼是彈性系統[1]
通過設定剛度(Stiffness)、質量(Mass)、阻尼(Damping)彈性體特徵屬性,為目標對象定義彈性,使對象表現出:被恢復力(F)驅動,受阻尼影響,從起始值向目標值(系統平衡位置) 運動的過程。此運動系統被稱為阻尼諧振子系統,遵守胡克定律[2]。
2. 質量(Mass)
彈性系統的受力對象,會對彈性系統產生慣性影響。質量越大,震蕩的幅度越大,恢復到平衡位置的速度越慢。
3. 阻尼(Damping)
是一個純數,無真實的物理意義,用於描述系統在受到擾動后震蕩及衰減的情形。阻尼越大,彈性運動的震蕩次數越少、震蕩幅度越小。
4. 阻尼比(Damping Ratio)[3]
表示阻尼相對於臨界阻尼的比值。
- 無阻尼:阻尼比 -> 0,系統處於永遠震蕩的狀態;
- 欠阻尼:阻尼比 < 1,對象進行指數遞減的震蕩運動;
- 過阻尼:阻尼比 > 1,對象進行無震蕩的減速運動;
- 臨界阻尼:阻尼比 = 1,對象以最短時間結束運動。
5. 剛度(Stiffness)[4]
是物體抵抗施加的力而形變的程度。在彈性系統中,剛度越大,抵抗變形的能力越強,恢復到平衡位置的速度就越快。
6. 摩擦力(Friction)
常見的一種造成彈性系統能量損耗的力,摩擦力的方向始終與對象運動的方向相反。摩擦力越大,彈性系統的能量損耗越快,震蕩次數越少,震蕩幅度越小。
7. 張力(Tension)[5]
是由一伸展的弦對施力者所做的反作用力,張力越大,反作用力越強,弦恢復原狀的速度就越快。
結論與成果
從彈性系統原理中我們認識到 Friction 與 Damping 的特徵接近,Stiffness 與 Tension 的特徵接近,再次進行假設並驗證:開發平台中的 damping 是否等同於設計工具中的 friction;開發平台中的 stiffness 是否等同於設計工具中的 tension。最終通過將設計工具參數直接按照假設的對應關係進行驗證,並結合之前的部分結論,最終得出:
damping = friction stiffness = tension [default]mass = 1 dampingRatio = damping / (2 * sqrt (mass * stiffness)) [default]velocity = 0
1. 應用
根據結論,設計師在交付彈性動效時,可以按照下面的方法提供參數:
2. Origami
設定 Pop Animation 參數為
- 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
設定 Spring 曲線參數為
- 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 參數
上述的結論中並不包含關於 iOS UIViewSpring 的 duration 參數的相關信息,我們繼續進行深入研究。
首先這次 Apple 並沒有像 dampingRatio 一樣在文檔中提供 duration 與其他參數直接的轉換關係,也僅有 iOS UIViewSpring API 唯一一個是以 duration 定義彈性動效的效果快慢,從開發平台能獲取到的信息就有限了。
在設計工具中,確實有能夠支持以 duration 和 dampingRatio 定義彈性動效的,其中有我們熟悉的 Flinto 和以開源的 Framer.js 構建的原型設計工具 Framer Studio。
Framer Studio 可以默認使用 time 與 damping 定義彈性動效,在彈性動效的代碼區域,右鍵菜單可以看到 Copy Animation 中包含 2 個選項,複製 Damping and Duration 的值,可以看到在 Framer Studio 中用於定義彈性動效的默認方法中:time 對應的是 duration,damping 對應的是 dampingRatio。
同時 Framer Studio 也支持使用 Friction 與 Tension 來定義彈性動效,在相應的代碼區域仍舊可以右鍵 Copy Animation,也就是 Framer Studio 實現了 dampingRatio、duration 與 Friction、Tension 參數之間的互相轉換。
得益於 Framer.js [7] 項目是開源的,我們在 Github 上,找到了關於兩套參數互相轉換的參數,其中具有關鍵意義的已知 Friction 與 Tension,轉換 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. 轉換器
從 Framer.js 開源項目中獲取到了 dampingRatio、duration 與 Friction、Tension 互相轉換的方法,而從 Origami [6] 的開源項目中我們也獲取到了將 Bouncy Converter:將 Pop Animation 的 Bounciness 和 Speed 轉換為 Friction 與 Tension。由此為了方便使用,我們開發了一個簡單的 Web 轉換器,通過選擇各種原型工具或開發平台的定義彈性動效的參數,能夠獲取到任意平台的彈性動效還原代碼參考。
在圖中可以看到我們也提供了 CSS 版本的關鍵幀動畫代碼參考,我們對關鍵幀的輸出進行了平滑處理,在體積、性能與效果之間得到了良好的平衡關係。
6. Origami Patch
強大的自定義能力是我們選擇優先使用 Origami Studio 的一個原因,在已知公式的情況下,我們可以模擬出各個平台 API 定義彈性動效的參數組合來設計動效:
插值模擬
1. 自定義動態插值器
在打通了彈性動效參數的從設計工具到 iOS UIViewSpring、iOS CASpringAnimation、Android SpringAnimation、CSS Keyframe 之間的參數轉換后,彈性動效的還原問題已經能在絕大多數場景解決了,但由於 Android 的彈性動效 API 需要引入支持庫,而在一些特殊情況下可能無法引入,這時候我們可以選擇一種降級方案:使用自定義插值器模擬彈性動效。
在 Android 開發中,插值器其實就是設計師常說的動效曲線,用於描述在定義時間內值的變化規律。理論上你可以利用此特性定義出任何動畫形式,而我們想要定義彈性動效,首先需要的就是用於描述彈性運動的函數公式。
再次感嘆開源的偉大,在 Juraj Novák 的 Interpolator 項目中,提供了一個以 factor 為調整彈性程度參數的彈性運動描述函數。
所以根據這個函數我們可以在 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 的插值動效中,描述動效快慢的參數統一為 duration,我們可以利用來自 Framer.js 的轉換方法得出一個 duration 參數。但為了還原效果,我們還需要知道 factor 與 Friction 和 Tension 之間的關係。遺憾的是我們並沒有查閱到相關資料,也沒能夠通過假設證明,最終我們選擇了使用數學辦法進行從 Friction、Tension 到 factor、duration 的轉換。
首先我們將 iOS UIViewSpring 中的 duration 直接賦予到 Android SpringInterpolator 動畫,用於描述效果的快慢。
由於彈性運動是一個衰減過程,所以我們可以把運動過程中達到的最大值當作是與彈性程度相關的量。在遵循胡克定律的條件下,當兩個彈性運動的最大值相同,且運動快慢也相同的時候,可以近似地認為二者的彈性效果是相同的。於是在已知 Friction 和 Tension 的情況下,求出當前彈性動效的最大值,再對 Android
SpringInterpolator 進行不同 factor 的最大值的遍歷與匹配,最終得出一個近似效果對應的 factor 值。由於需要大量的重複計算,且沒有通用公式,此方法僅被應用在 Web 轉換器中。
2. 自定義 XML 插值器
在一些極端情況下,Android 無法使用動態方法定義動畫,必須使用 XML 時,我們也可以利用 XML 定義插值器,但此時的靈活性就更低了,因為需要把彈性效果畫出來。
插值器是定義在點 (0, 0) 到點(1, 1) 區域內的一段連續曲線,Android 支持使用路徑信息的形式來描述插值器,那麼關鍵點就在於如何輸出符合效果的路徑信息。
使用工具(如 D3.js)按照彈性系統的運動學公式生成 SVG,將起點和終點限制在 1*1 px 的畫布內,同時注意調整坐標軸,畫布坐標軸以左上角為原點 (0, 0),插值坐標軸卻是以左下角為原點 (0, 0)。
把輸出的路徑信息從SVG中拷貝到自定義 XML 插值器的 pathData 中,我們就得到了一個效果固定的自定義 XML 彈性插值器:
總結
在經歷了複雜的研究與驗證過程,我們知道了:設計工具中的參數是能夠與開發平台的參數對齊的,只是換了種叫法 —— Friction 就是 Damping,Tension 就是 Stiffness;大膽假設有的時候是憑直覺,但直覺背後可能存在着一些潛在因素的影響——在沒有定義 Mass 的時候,使用的是缺省值 1;雖然很艱難,但是我們終究還是在 Android 的全場景還原了彈性動效;開源的力量是偉大的,再次感謝開源社區。
拓展閱讀:
歷時2個月,整理了這篇動效落地輸出指南!
想讓你的動效完美實現?了解動效落地背後的原理,可以幫助我們在設計的前期階段,就了解應該如何做設計才能更容易的對接和落地。
閱讀文章 >>
附錄
- 諧振子系統:https://en.wikipedia.org/wiki/Harmonic_oscillator
- 胡克定律:https://en.wikipedia.org/wiki/Hooke%27s_law
- 阻尼比:https://en.wikipedia.org/wiki/Damping_ratio
- 剛度:https://en.wikipedia.org/wiki/Stiffness
- 張力:https://en.wikipedia.org/wiki/Tension_(physics)
- ReboundJS:http://facebook.github.io/rebound/
- FramerJS:https://github.com/koenbok/Framer
歡迎關注作者微信公眾號:「今日頭條UED」
未经-摩登3注册-摩登3测速官网-允许不得转载:摩登3注册-摩登3测速官网 » 無極5平台_大廠硬核乾貨!深入分析彈性動效的應用及原理