|
弹性是动态设计领域中一种常见的表达方式。不同于影视特效、动画 CG 等设计输出即为最终产物的生产环境,UI 动效始终面临着动效还原带来的种种问题,弹性动效的还原就是其中之一。当设计师完成弹性动效的设计,与工程师进行交接时,双方会发现参数无法对齐 —— 在设计工具中调节效果的参数与在工程开发环境下设定效果的参数无论是名称还是数量都存在差异。基于以上背景,我们通过研究一些常见原型设计工具的弹性模拟系统,深入弹性系统的动力学原理,与主流平台的弹性动效实现原理进行匹配,解决了从设计工具到工程实现的弹性动效还原问题。Part.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(张力)。Principle以友好易用被 UI 设计师、交互设计师甚至产品经理所钟情的 Principle,在定义弹性动效时,将其归类为一种特殊的曲线 —— Spring,与 Ease In、Ease Out 同级别。不同的是这条特殊的弹性「曲线」既不是通过 2 点坐标来定义,也不能调节相应的时长。Principle 中的 Spring 曲线通过修改 Friction 与 Tension 来调节弹性的表现,通过 Principle 提供的曲线可视化预览,我们可以容易的理解这两个参数的作用:Friction 的值越大,动效越“弹”;Tension 的值越大,动效结束的越“快”。其他同样使用 Friction 与 Tension 参数调节弹性表现的还有 ProtoPie Studio、Flinto 等原型工具,而其效果也和 Principle 中的表现一致。ProtoPie StudioFlintoPart.2在主流平台实现弹性动效iOS相对于将视觉风格从拟物改为扁平,iOS 7 对动效的调整方向刚好相反,采用了更加贴近真实的设计方案,这其中最重要的元素就是广泛应用自然的弹性动效。然而你可能会产生疑惑,因为并没有在 iOS 系统中见到“广泛”的弹性动效。这是因为 iOS 系统动效使用最多的,恰恰是一种没有弹性的弹性动效,即应用了弹性系统中的临界阻尼,其主要特征是:物体受弹力的作用最快的恢复到平衡位置。从 iOS 7 开始到目前为止,在系统内的任何一个场景:页面间的切换,弹窗的出现与消失,键盘的抬起与落下,文件夹的展开与收起 等等,都使用了处于临界阻尼状态的弹性动效。同时 Apple 也提供了使用这种动效的 API:UIView.animateWithDuration:usingSpringWithDamping:initialSpringVelocity+ (void)animateWithDurationNSTimeInterval)duration delayNSTimeInterval)delay usingSpringWithDampingCGFloat)dampingRatio initialSpringVelocityCGFloat)velocity optionsUIViewAnimationOptions)options animationsvoid (^)(void))animations completionvoid (^)(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 的 提供的新 APIUIViewPropertyAnimator 的参数中提到:The damping ratio for the spring is computed from the formula damping / (2 * sqrt (stiffness * mass)).—— UISpringTimingParameters全新的 UIViewPropertyAnimator API 相比 UIViewSpring 和 CASpringAnimation 更加强大,也同时兼容这两个旧 API 的参数定义格式,建议优先考虑使用新 API,具体使用方法不再赘述。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:刚度定义了用于衡量弹簧强度的弹簧常量。不在静止位置的坚硬弹簧可对所连接的对象施加更大的力。Web在 W3C 与 ecma 制定的 Web 标准中与动效相关的内容仅有:CSS animation,而其中并不包含定义弹性动效的方法。但 Web 领域最具魅力与价值的地方就在于各种丰富的、充满创造力的库,关于定义弹性动效,应用较为广泛的库有:animejs 等。在 animejs 中定义弹性动效需要 4 个参数:springPhysicsEasingeasing: 'spring(mass, stiffness, damping, velocity)'Part.3从设计到实现的差异设计工具将上述的信息归类整理,首先看各种原型设计工具之间是能够达成一致的,基本都支持 Friction 和 Tension 参数的输出,分别定义弹性的程度与效果的快慢,整理如下:开发平台而在开发平台上,弹性效果的实现则有共同点也有差异点,我们先看这张表:先看共同点:初始速度,该参数在所有 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 已经能够达成一致关系,或相同,或可以互相转换。差异尽管在设计工具和开发平台中,我们通过查阅概念定义与尝试验证得到了用于定义弹性强度与效果快慢的参数,但设计工具是使用 Friction 和 Tension 来表示,而开发平台则使用 damping 和 stiffness 来表示。为了寻找这些参数之间的关系,我们更深入的去了解了弹性系统的动力学原理。Part.4弹性系统原理什么是弹性系统[1]通过设定刚度(Stiffness)、质量(Mass)、阻尼(Damping)弹性体特征属性,为目标对象定义弹性,使对象表现出:被恢复力(F)驱动,受阻尼影响,从起始值向目标值(系统平衡位置) 运动的过程。此运动系统被称为阻尼谐振子系统,遵守胡克定律[2]。质量(Mass)弹性系统的受力对象,会对弹性系统产生惯性影响。质量越大,振荡的幅度越大,恢复到平衡位置的速度越慢。阻尼(Damping)是一个纯数,无真实的物理意义,用于描述系统在受到扰动后振荡及衰减的情形。阻尼越大,弹性运动的振荡次数越少、振荡幅度越小。阻尼比(Damping Ratio)[3]表示阻尼相对于临界阻尼的比值。· 无阻尼:阻尼比 -> 0,系统处于永远振荡的状态;· 欠阻尼:阻尼比 1,对象进行无振荡的减速运动;· 临界阻尼:阻尼比 = 1,对象以最短时间结束运动。刚度(Stiffness)[4]是物体抵抗施加的力而形变的程度。在弹性系统中,刚度越大,抵抗变形的能力越强,恢复到平衡位置的速度就越快。摩擦力(Friction)常见的一种造成弹性系统能量损耗的力,摩擦力的方向始终与对象运动的方向相反。摩擦力越大,弹性系统的能量损耗越快,振荡次数越少,振荡幅度越小。张力(Tension)[5]是由一伸展的弦对施力者所做的反作用力,张力越大,反作用力越强,弦恢复原状的速度就越快。Part.5结论与成果从弹性系统原理中我们认识到 Friction 与 Damping 的特征接近,Stiffness 与 Tension 的特征接近,再次进行假设并验证:开发平台中的 damping 是否等同于设计工具中的 friction;开发平台中的 stiffness 是否等同于设计工具中的 tension。最终通过将设计工具参数直接按照假设的对应关系进行验证,并结合之前的部分结论,最终得出:damping = frictionstiffness = tension[default]mass = 1dampingRatio = damping / (2 * sqrt (mass * stiffness))[default]velocity = 0应用根据结论,设计师在交付弹性动效时,可以按照下面的方法提供参数:Origami设定 Pop Animation 参数为Bounciness = 5Speed = 10使用 Bouncy Converter 得到Friction = 27.0487Tension = 299.61884则:damping = 27.05stiffness = 299.62mass = 1velocity = 0dampingRatio = 0.78For AndroidSpringAnimation(object, DynamicAnimation.[property]).apply {…spring.dampingRatio = 0.78fspring.stiffness = 299.62fsetStartVelocity(0f)}For iOSlet spring = CASpringAnimation(keyPath:[property])spring.stiffness = 299.62spring.damping = 27.05spring.mass = 1spring.initialVelocity = 0Principle设定 Spring 曲线参数为Tension = 381.47Friction = 20.17则:damping = 20.17stiffness = 381.47mass = 1velocity = 0dampingRatio = 0.52For AndroidSpringAnimation(object, DynamicAnimation.[property]).apply {…spring.dampingRatio = 0.52fspring.stiffness = 381.47fsetStartVelocity(0f)}For iOSlet spring = CASpringAnimation(keyPath:[property])spring.stiffness = 381.47spring.damping = 20.17spring.mass = 1spring.initialVelocity = 0UIViewSpring 的 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 nullepsilon = 0.001computeDuration = (tension, friction, velocity = 0, mass = 1) ->dampingRatio = computeDampingRatio(tension, friction)undampedFrequency = Math.sqrt(tension / mass) # This is basically duration extracted out of the envelope functionsif dampingRatio
Part.7总结在经历了复杂的研究与验证过程,我们知道了:设计工具中的参数是能够与开发平台的参数对齐的,只是换了种叫法 —— Friction 就是 Damping,Tension 就是 Stiffness;大胆假设有的时候是凭直觉,但直觉背后可能存在着一些潜在因素的影响——在没有定义 Mass 的时候,使用的是缺省值 1;虽然很艰难,但是我们终究还是在 Android 的全场景还原了弹性动效;开源的力量是伟大的,再次感谢开源社区。附录1. 谐振子系统:https://en.wikipedia.org/wiki/Harmonic_oscillator2.胡克定律:https://en.wikipedia.org/wiki/Hooke%27s_law3.阻尼比:https://en.wikipedia.org/wiki/Damping_ratio4.刚度:https://en.wikipedia.org/wiki/Stiffness5.张力:https://en.wikipedia.org/wiki/Tension_(physics)6.ReboundJS:http://facebook.github.io/rebound/7.FramerJS:https://github.com/koenbok/Framer推荐阅读感谢您的阅读,以上内容为头条UED团队原创首发,版权归今日头条UED所有。头条UED文章现已全面开启个人及机构转载,转载请注明内容出处,违者必究,感谢合作。申请转载授权后台留言"转载"或添加小编微信:827284979
|
|