Wave 是一个基于弹簧的动画引擎,适用于 iOS、iPadOS 和 macOS。它可以轻松创建流畅、交互式和可中断的动画,带来绝佳的体验。
Wave 没有外部依赖项,并且可以轻松地集成到现有的基于 UIKit、SwiftUI 或 AppKit 的项目和应用程序中。
Wave 的核心特性是所有动画都是可重定向的,这意味着您可以在动画进行过程中更改动画的目标值,动画会平滑地重定向到新的值。
请看这些 iOS 画中画功能的演示。左侧屏幕使用标准的 UIKit 动画创建,右侧屏幕使用 Wave 创建。
虽然两者都是“可中断的”,但基于 Wave 的实现能更好地处理中断,并流畅地弧线过渡到新的目标。相比之下,UIKit 动画感觉生硬且顿挫。
从本质上讲,重定向是在目标改变时仍保持动画速度的过程,而 Wave 可以自动实现这一点。
将 Wave 添加到您应用程序的 Package.swift
文件中,或在 Xcode 中选择 File -> Add Packages
.package(url: "https://github.com/jtrivedi/Wave")
如果您克隆了仓库,您可以运行示例应用程序,其中包含一些交互式演示,以帮助您理解 Wave 的功能。
注意:要在 ProMotion 设备上启用高帧率动画(即 120 fps 动画),您需要在您的 Info.plist
文件中添加一个键值对。将键 CADisableMinimumFrameDuration
设置为 true
。如果没有此条目,动画将被限制为 60 fps。
有一个完整的 Wave 文档站点,提供完整的 API 和使用文档。
根据您的需求,您可以通过两种方式与 Wave 交互:基于块的动画和基于属性的动画。
最简单的入门方式是使用 Wave 基于块的 API,这些 API 类似于 UIView.animateWithDuration()
API。
此 API 允许您动画化几个常见的 UIView 和 CALayer 属性,例如 frame
、center
、scale
、backgroundColor
等。
对于这些受支持的属性,Wave 将在底层创建、管理和执行所需的弹簧动画。
例如,将上述 PiP 视图动画化到其最终目的地非常简单。
if panGestureRecognizer.state == .ended {
// Create a spring with some bounciness. `response` affects the animation's duration.
let animatedSpring = Spring(dampingRatio: 0.68, response: 0.80)
// Get the gesture's lift-off velocity, and pass it into the Wave animation.
let gestureVelocity = panGestureRecognizer.velocity(in: view)
Wave.animate(withSpring: animatedSpring, gestureVelocity: gestureVelocity) {
// Update animatable properties on the view's `animator` property, _not_ the view itself.
pipView.animator.center = pipViewDestination // Some target CGPoint that you calculate.
pipView.animator.scale = CGPoint(x: 1.1, y: 1.1)
}
}
请注意,在任何时候,您都可以将视图的 center
属性重定向到其他位置,它都会平滑地进行动画。
基于块的 API 当前支持动画化以下属性。对于其他属性,您可以使用下面的基于属性的动画 API。
frame
bounds
center
origin
alpha
backgroundColor
cornerRadius
scale
translation
shadowColor/radius/offset/opacity
borderColor/borderWidth
即将推出的属性
rotation
虽然基于块的 API 通常最方便,但您可能希望动画化基于块的 API 尚不支持的内容(例如 rotation)。或者,您可能需要获取中间弹簧值并自行驱动动画的灵活性(例如进度值)。
例如,要绘制 PiP 演示的橙色路径,我们需要知道从视图的初始中心到其目标中心之间每个 CGPoint
的值。
// When the gesture ends, create a `CGPoint` animator from the PiP view's initial center, to its target.
// The `valueChanged` callback provides the intermediate locations of the callback, allowing us to draw the path.
let positionAnimator = SpringAnimator<CGPoint>(spring: animatedSpring)
positionAnimator.value = pipView.center // The presentation value
positionAnimator.target = pipViewDestination // The target value
positionAnimator.velocity = gestureVelocity
positionAnimator.valueChanged = { [weak self] location in
self?.drawPathPoint(at: location)
}
positionAnimator.start()
基于块和基于属性的 API 都支持完成代码块。如果动画完全完成,则完成代码块的 finished
标志将为 true。
但是,如果动画的目标在进行过程中被更改(“重定向”),则 finished
将为 false,而 retargeted
将为 true。
Wave.animate(withSpring: Spring.defaultAnimated) {
myView.animator.backgroundColor = .systemBlue
} completion: { finished, retargeted in
print(finished, retargeted)
}
探索提供的示例应用程序是开始使用 Wave 的绝佳方式。
只需打开 Wave-Sample
Xcode 项目并点击“Run”。画中画演示的完整源代码也在此处提供!
特别感谢 Ben Oztalay,感谢他帮助设计 Wave 的底层物理原理!