另一个动画库
专为手势驱动的动画设计。快速、简单且可扩展!
该库完全使用Swift 3.1编写,采用面向协议的设计,广泛使用泛型。
可以将其视为Facebook的pop的Swift优化版本。它更贴合Swift并且运行更快。
快速:
- 使用SIMD类型和指令进行计算
- 通过Swift泛型获得更好的编译器优化
简单:
- 自带曲线(基本)、弹簧和衰减动画支持
- 提供动画常见可动画属性的简单API。(查看Extensions文件夹获取支持的属性列表)
- 分配动画值时保证了类型安全
- 可观察的,包括值、速度和目标值
- 内置链式运算符,易于对值的变化做出反应
- 通过手势提供了速度插值
可扩展:
- 支持自定义属性
- 支持自定义可动画类型
- 支持自定义动画
安装
pod "YetAnotherAnimationLibrary"
使用方法
动画
// Spring animation
view.yaal.center.animateTo(CGPoint(x:50, y:100))
view.yaal.alpha.animateTo(0.5, stiffness: 300, damping: 20)
// Curve(Basic) animation
view.yaal.frame.animateTo(CGRect(x:0, y:0, width:50, height:50), duration:0.5, curve: .linear)
// Decay Animation
view.yaal.center.decay(initialVelocity:CGPoint(x:100, y:0))
观察变化
// observe value changes
view.yaal.center.value.changes.addListener { oldVelocity, newVelocity in
print(oldVelocity, newVelocity)
}
// observe velocity changes
view.yaal.center.velocity.changes.addListener { oldVelocity, newVelocity in
print(oldVelocity, newVelocity)
}
链式反应
// when scale changes, also change its alpha
// for example if view's scale animates from 1 to 0.5. its alpha will animate to 0.5 as well
view.yaal.scale.value => view.yaal.alpha
// equvalent to the following
// view.yaal.scale.value.changes.addListener { _, newScale in
// view.yaal.alpha.animateTo(newScale)
// }
// optionally you can provide a mapping function in between.
// For example, the following code makes the view more transparent the faster it is moving
view.yaal.center.velocity => { 1 - $0.magnitude / 1000 } => view.yaal.alpha
// equvalent to the following
// view.yaal.center.velocity.changes.addListener { _, newVelocity in
// view.yaal.alpha.animateTo(1 - newVelocity.magnitude / 1000)
// }
设置值(通知监听者)
// this sets the value directly (not animate to). Change listeners are called.
// Velocity listeners will receive a series of smoothed velocity values.
view.yaal.center.setTo(gestureRecognizer.location(in:nil))
高级用法
响应变化
“Animate”在观察动画值和相应反应方面非常高效。通过观察值可以实现一些出色的效果。
例如,下面是一个简单的通过观察中心值速度实现的2D旋转动画。
override func viewDidLoad() {
// ...
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tap(gr:))))
squareView.yaal.center.velocity => { $0.x / 1000 } => squareView.yaal.rotation
}
func tap(gr: UITapGestureRecognizer) {
squareView.yaal.center.animateTo(gr.location(in: view))
}
在调用《setTo(_:)》时,“Animate”还提供了平滑的速度插值。这在使用用户手势时特别有用。
例如,以下示例在拖动时执行3D旋转动画。
override func viewDidLoad() {
// ...
squareView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(pan(gr:))))
squareView.yaal.perspective.setTo(-1.0 / 500.0)
squareView.yaal.center.velocity => { $0.x / 1000 } => squareView.yaal.rotationY
squareView.yaal.center.velocity => { -$0.y / 1000 } => squareView.yaal.rotationX
}
func pan(gr: UIPanGestureRecognizer) {
squareView.yaal.center.setTo(gr.location(in: view))
}
自定义属性
要动画自定义属性,只需通过调用 SpringAnimation(getter:setter:)
创建一个动画对象。使用动画对象来动画化和设置值。Animate 提供了 4 种类型的动画。
- 弹簧动画
- 曲线动画
- 衰减动画
- 混合动画(包含所有三种类型的动画)
class Foo {
var volumn: Float = 0.0
lazy var volumnAnimation: SpringAnimation<Float>
= SpringAnimation(getter: { [weak self] in self?.volumn },
setter: { [weak self] in self?.volumn = $0 })
}
volumnAnimation.animateTo(0.5)
如果您的类继承自 NSObject,那么使用内置的动画库会更简单。
yaal.animationFor
通过扩展 & extension Foo {
public var volumnAnimation: MixAnimation<CGRect> {
return yaal.animationFor(key: "volumn",
getter: { [weak self] in self?.volumn },
setter: { [weak self] in self?.volumn = $0 })
}
}
yaal.register
& yaal.animationFor
通过 // or register ahead of time
yaal.register(key: "volumn",
getter: { [weak self] in self?.volumn },
setter: { [weak self] in self?.volumn = $0 })
// and retrieve the animation object through the same key.
yaal.animationFor(key: "volumn")!.animateTo(0.5)
// NOTE: that this method have limited type safety. You can basically pass any animatable type into `animateTo()`
// There is nothing to stop you from doing the following. but they will crash at run time
yaal.animationFor(key: "volumn")!.animateTo(CGSize.zero)
yaal.animationFor(key: "volumn")!.animateTo(CGRect.zero)
自定义的可动画类型
自定义可动画类型也受支持。只需使类型符合 VectorConvertable
。
// the following makes IndexPath animatable
extension IndexPath: VectorConvertible {
public typealias Vector = Vector2
public init(vector: Vector) {
self.init(item: Int(vector.x), section: Int(vector.y))
}
public var vector: Vector {
return [Double(item), Double(section)]
}
}
// Can now be used like this
let indexAnimation = SpringAnimation(getter: { self.indexPath },
setter: { self.indexPath = $0 })
indexAnimation.animateTo(IndexPath(item:0, section:0))
// Note that everything is type safe. incorrect type won't be allowed to compile
自定义动画
只需子类化 Animation
并重写 update(dt:TimeInterval)
方法。如果您的动画需要 getter & setter 支持,请改子类化 ValueAnimation
。查看内置动画的示例。