如何在连续的 repeatForever 中链接 Facebook pop 动画?
How to chain Facebook pop Animations in a continuous repeatForever?
情况:
我创建了一个脉动动画并且它有效。
使用 repeatForever
属性,我可以根据需要使其连续。
但有一个问题仍然存在,我在做了很多研究后仍无法解决。
解释:
唯一的问题是我不知道如何再添加一个动画,使图像在再次跳动之前恢复到原来的大小。
我的挑战在于,如果我在另一个之后声明它,它将不会被调用,因为它不会包含在 repeatForever
循环中。
因此,我现在的动画并不流畅,因为一旦完成,图像会立即恢复到原来的大小,然后再重复。
会发生什么:
1) 图像跳动到当前大小的 1.2 thx 到动画。
THIS IS NOT SMOOTH:
2) Animation finishes at 1.2 and Image instantly "warps" back to 1.0
3) 动画重复。图像再次跳动到 1.2。
问题: 如何更改我的 pulsatingAnimation 以便我的图像在再次脉动之前以平滑的方式恢复到原始大小?
代码:
import Foundation
import pop
class AnimationEngine {
func pulsate(object: UIImageView) {
let pulsateAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
pulsateAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateAnim.springBounciness = 18
pulsateAnim.dynamicsFriction = 20
pulsateAnim.springSpeed = 1.0
pulsateAnim.repeatForever = true
// pulsateAnim.autoreverses = true
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
}
}
我不知道Facebook pop
,所以我只讲一下如何实现这个功能的分析和逻辑(脉冲效应)
正如我在评论中所写,你想要做的似乎正是在 SpriteKit
中多次发生的事情,你要制作特定的动画 (SKAction
),必须构建一个一系列动作。
只是为了做一个SpriteKit
例子,这是我做一个特定的脉冲效果:
let titleForward = runTitleForward()
let wait = SKAction.waitForDuration(5.0)
let titleBackward = runTitleBackward()
let titleAnim = SKAction.repeatActionForever((SKAction.sequence([titleForward, wait, titleBackward,wait ])))
title.runAction(titleAnim, withKey:"titleAnimation")
func runTitleForward()-> SKAction {
let atlascTexturesArray = self.myManager.atlascTexturesDictionary["title-anim"]
let texturesAction = SKAction.animateWithTextures(atlascTexturesArray!,timePerFrame: 0.09)
return texturesAction
}
func runTitleBackward()-> SKAction {
let texturesAction = runTitleForward()
let texturesReverseAction = SKAction.reversedAction(texturesAction)
return texturesReverseAction()
}
希望你能帮助在UIKit
做同样的事情。
更新:(我没有测试..)
func delayAnimation(duration:NSTimeInterval)-> CABasicAnimation {
let animation : CABasicAnimation = CABasicAnimation(keyPath: "opacity");
animation.delegate = self
animation.fromValue = 1
animation.toValue = 1 // fake animation, just to obtain duration delay..
animation.duration = duration
return animation
}
func pulseElements () -> (POPSpringAnimation,POPSpringAnimation) {
// zoom in
let pulsateInAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
pulsateInAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateInAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateInAnim.springBounciness = 18
pulsateInAnim.dynamicsFriction = 20
pulsateInAnim.springSpeed = 1.0
// zoom out
let pulsateOutAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
...
return (pulsateInAnim,pulsateOutAnim)
}
func pulseAnimation() {
let (pulsateInAnim,pulsateOutAnim) = pulseElements()
let wait = delayAnimation(1.0)
var pulseAnimationGroup: CAAnimationGroup = CAAnimationGroup()
pulseAnimationGroup.animations = [pulsateInAnim, wait, pulsateOutAnim, wait]
pulseAnimationGroup.duration = 0.5
pulseAnimationGroup.repeatCount = Float.infinity
object.layer.addAnimation(pulseAnimationGroup, forKey: layerScaleSpringAnimation)
}
我是一个巨大的 ReactiveCocoa 书呆子所以我围绕 POPAnimation
构建了一个 class 这样我就可以以更实用的方式处理动画。
import Foundation
import ReactiveCocoa
import pop
import Result
/**
`UIView` wrapper for performing reactive-based pop animations.
*/
public struct ReactivePOPView<View: UIView> {
/// View object
private weak var view: View?
/// `SignalProducer` object when the wrapper is about to deallocate.
private let willDealloc: SignalProducer<Void, NoError>
/// Wrapper on the view property.
private var animator: View? { return self.view }
/**
Create an animation wrapper with a view.
- parameter view: `UIView` object.
*/
public init(_ view: View) {
self.view = view
self.willDealloc = view
.willDeallocSignalProducer()
}
}
/**
Binds a `Signal` that emits an animation object to a `ReactivePOPView`.
- parameter view: `ReactivePOPView` object.
- parameter signal: `Signal` object.
- returns: `Disposable` object.
*/
public func <~ <T, Animation: POPAnimation>(view: ReactivePOPView<T>, signal: Signal<Animation, NoError>) -> Disposable {
let disposable = CompositeDisposable()
let viewDisposable = view.willDealloc.startWithCompleted {
disposable.dispose()
}
disposable.addDisposable(viewDisposable)
let signalDisposable = signal.observe(Observer(next: {
view.animator?.pop_addAnimation([=10=], forKey: [=10=].name ?? "")
}, completed: {
disposable.dispose()
}))
disposable.addDisposable(signalDisposable)
return disposable
}
/**
Binds a `SignalProducer` that emits an animation object to a `ReactivePOPView`.
- parameter view: `ReactivePOPView` object.
- parameter producer: `SignalProducer` object.
- returns: `Disposable` object.
*/
public func <~ <T, Animation: POPAnimation>(view: ReactivePOPView<T>, producer: SignalProducer<Animation, NoError>) -> Disposable {
var disposable: Disposable!
producer.startWithSignal { signal, signalDisposable in
view <~ signal
disposable = signalDisposable
view.willDealloc.startWithCompleted {
signalDisposable.dispose()
}
}
return disposable
}
/**
Bind a reactive animation property to a `ReactivePOPView` property.
- parameter destinationProperty: `ReactivePOPView` property.
- parameter sourceProperty: Animation property.
- returns: `Disposable` object.
*/
public func <~ <T, P: PropertyType where P.Value == POPAnimation>(destinationProperty: ReactivePOPView<T>, sourceProperty: P) -> Disposable {
return destinationProperty <~ sourceProperty.producer
}
使用此 class,您可以执行以下操作:
ReactivePOPView<UIButton>(self.loginButton) <~ self.someSignal.signal
.flatMap(.Concat) { _ in SignalProducer<POPBasicAnimation, NoError> in
let animation ...
// construct animation here
return SignalProducer(value: animation)
}
.repeat(0)
如果您需要,我还有一个包装器 class 用于约束。
我找到了解决方案。
解决方案:
func pulsate(object: UIImageView) {
let pulsateAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
let returnToSizeAnim = POPBasicAnimation(propertyNamed: kPOPLayerScaleXY)
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
pulsateAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateAnim.springBounciness = 30
pulsateAnim.dynamicsFriction = 20
pulsateAnim.springSpeed = 1.0
pulsateAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(returnToSizeAnim, forKey: "layerScaleSpringAnimation")
}
returnToSizeAnim.toValue = NSValue(CGSize: CGSizeMake(1.0, 1.0))
returnToSizeAnim.duration = 0.5
returnToSizeAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
}
}
func pulsate2(object: UIImageView) {
let pulsateAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
let returnToSizeAnim = POPBasicAnimation(propertyNamed: kPOPLayerScaleXY)
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
pulsateAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateAnim.springBounciness = 30
pulsateAnim.dynamicsFriction = 20
pulsateAnim.springSpeed = 1.0
pulsateAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(returnToSizeAnim, forKey: "layerScaleSpringAnimation")
}
returnToSizeAnim.toValue = NSValue(CGSize: CGSizeMake(1.0, 1.0))
returnToSizeAnim.duration = 0.5
returnToSizeAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
}
}
N.B.:
我需要为每个要使用它的对象声明一个脉动函数。如果我不这样做,我第二次调用该函数时,它将无法正常工作,因为动画实例已经 运行.
情况:
我创建了一个脉动动画并且它有效。
使用 repeatForever
属性,我可以根据需要使其连续。
但有一个问题仍然存在,我在做了很多研究后仍无法解决。
解释:
唯一的问题是我不知道如何再添加一个动画,使图像在再次跳动之前恢复到原来的大小。
我的挑战在于,如果我在另一个之后声明它,它将不会被调用,因为它不会包含在 repeatForever
循环中。
因此,我现在的动画并不流畅,因为一旦完成,图像会立即恢复到原来的大小,然后再重复。
会发生什么:
1) 图像跳动到当前大小的 1.2 thx 到动画。
THIS IS NOT SMOOTH:
2) Animation finishes at 1.2 and Image instantly "warps" back to 1.0
3) 动画重复。图像再次跳动到 1.2。
问题: 如何更改我的 pulsatingAnimation 以便我的图像在再次脉动之前以平滑的方式恢复到原始大小?
代码:
import Foundation
import pop
class AnimationEngine {
func pulsate(object: UIImageView) {
let pulsateAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
pulsateAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateAnim.springBounciness = 18
pulsateAnim.dynamicsFriction = 20
pulsateAnim.springSpeed = 1.0
pulsateAnim.repeatForever = true
// pulsateAnim.autoreverses = true
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
}
}
我不知道Facebook pop
,所以我只讲一下如何实现这个功能的分析和逻辑(脉冲效应)
正如我在评论中所写,你想要做的似乎正是在 SpriteKit
中多次发生的事情,你要制作特定的动画 (SKAction
),必须构建一个一系列动作。
只是为了做一个SpriteKit
例子,这是我做一个特定的脉冲效果:
let titleForward = runTitleForward()
let wait = SKAction.waitForDuration(5.0)
let titleBackward = runTitleBackward()
let titleAnim = SKAction.repeatActionForever((SKAction.sequence([titleForward, wait, titleBackward,wait ])))
title.runAction(titleAnim, withKey:"titleAnimation")
func runTitleForward()-> SKAction {
let atlascTexturesArray = self.myManager.atlascTexturesDictionary["title-anim"]
let texturesAction = SKAction.animateWithTextures(atlascTexturesArray!,timePerFrame: 0.09)
return texturesAction
}
func runTitleBackward()-> SKAction {
let texturesAction = runTitleForward()
let texturesReverseAction = SKAction.reversedAction(texturesAction)
return texturesReverseAction()
}
希望你能帮助在UIKit
做同样的事情。
更新:(我没有测试..)
func delayAnimation(duration:NSTimeInterval)-> CABasicAnimation {
let animation : CABasicAnimation = CABasicAnimation(keyPath: "opacity");
animation.delegate = self
animation.fromValue = 1
animation.toValue = 1 // fake animation, just to obtain duration delay..
animation.duration = duration
return animation
}
func pulseElements () -> (POPSpringAnimation,POPSpringAnimation) {
// zoom in
let pulsateInAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
pulsateInAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateInAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateInAnim.springBounciness = 18
pulsateInAnim.dynamicsFriction = 20
pulsateInAnim.springSpeed = 1.0
// zoom out
let pulsateOutAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
...
return (pulsateInAnim,pulsateOutAnim)
}
func pulseAnimation() {
let (pulsateInAnim,pulsateOutAnim) = pulseElements()
let wait = delayAnimation(1.0)
var pulseAnimationGroup: CAAnimationGroup = CAAnimationGroup()
pulseAnimationGroup.animations = [pulsateInAnim, wait, pulsateOutAnim, wait]
pulseAnimationGroup.duration = 0.5
pulseAnimationGroup.repeatCount = Float.infinity
object.layer.addAnimation(pulseAnimationGroup, forKey: layerScaleSpringAnimation)
}
我是一个巨大的 ReactiveCocoa 书呆子所以我围绕 POPAnimation
构建了一个 class 这样我就可以以更实用的方式处理动画。
import Foundation
import ReactiveCocoa
import pop
import Result
/**
`UIView` wrapper for performing reactive-based pop animations.
*/
public struct ReactivePOPView<View: UIView> {
/// View object
private weak var view: View?
/// `SignalProducer` object when the wrapper is about to deallocate.
private let willDealloc: SignalProducer<Void, NoError>
/// Wrapper on the view property.
private var animator: View? { return self.view }
/**
Create an animation wrapper with a view.
- parameter view: `UIView` object.
*/
public init(_ view: View) {
self.view = view
self.willDealloc = view
.willDeallocSignalProducer()
}
}
/**
Binds a `Signal` that emits an animation object to a `ReactivePOPView`.
- parameter view: `ReactivePOPView` object.
- parameter signal: `Signal` object.
- returns: `Disposable` object.
*/
public func <~ <T, Animation: POPAnimation>(view: ReactivePOPView<T>, signal: Signal<Animation, NoError>) -> Disposable {
let disposable = CompositeDisposable()
let viewDisposable = view.willDealloc.startWithCompleted {
disposable.dispose()
}
disposable.addDisposable(viewDisposable)
let signalDisposable = signal.observe(Observer(next: {
view.animator?.pop_addAnimation([=10=], forKey: [=10=].name ?? "")
}, completed: {
disposable.dispose()
}))
disposable.addDisposable(signalDisposable)
return disposable
}
/**
Binds a `SignalProducer` that emits an animation object to a `ReactivePOPView`.
- parameter view: `ReactivePOPView` object.
- parameter producer: `SignalProducer` object.
- returns: `Disposable` object.
*/
public func <~ <T, Animation: POPAnimation>(view: ReactivePOPView<T>, producer: SignalProducer<Animation, NoError>) -> Disposable {
var disposable: Disposable!
producer.startWithSignal { signal, signalDisposable in
view <~ signal
disposable = signalDisposable
view.willDealloc.startWithCompleted {
signalDisposable.dispose()
}
}
return disposable
}
/**
Bind a reactive animation property to a `ReactivePOPView` property.
- parameter destinationProperty: `ReactivePOPView` property.
- parameter sourceProperty: Animation property.
- returns: `Disposable` object.
*/
public func <~ <T, P: PropertyType where P.Value == POPAnimation>(destinationProperty: ReactivePOPView<T>, sourceProperty: P) -> Disposable {
return destinationProperty <~ sourceProperty.producer
}
使用此 class,您可以执行以下操作:
ReactivePOPView<UIButton>(self.loginButton) <~ self.someSignal.signal
.flatMap(.Concat) { _ in SignalProducer<POPBasicAnimation, NoError> in
let animation ...
// construct animation here
return SignalProducer(value: animation)
}
.repeat(0)
如果您需要,我还有一个包装器 class 用于约束。
我找到了解决方案。
解决方案:
func pulsate(object: UIImageView) {
let pulsateAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
let returnToSizeAnim = POPBasicAnimation(propertyNamed: kPOPLayerScaleXY)
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
pulsateAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateAnim.springBounciness = 30
pulsateAnim.dynamicsFriction = 20
pulsateAnim.springSpeed = 1.0
pulsateAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(returnToSizeAnim, forKey: "layerScaleSpringAnimation")
}
returnToSizeAnim.toValue = NSValue(CGSize: CGSizeMake(1.0, 1.0))
returnToSizeAnim.duration = 0.5
returnToSizeAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
}
}
func pulsate2(object: UIImageView) {
let pulsateAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
let returnToSizeAnim = POPBasicAnimation(propertyNamed: kPOPLayerScaleXY)
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
pulsateAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateAnim.springBounciness = 30
pulsateAnim.dynamicsFriction = 20
pulsateAnim.springSpeed = 1.0
pulsateAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(returnToSizeAnim, forKey: "layerScaleSpringAnimation")
}
returnToSizeAnim.toValue = NSValue(CGSize: CGSizeMake(1.0, 1.0))
returnToSizeAnim.duration = 0.5
returnToSizeAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
}
}
N.B.:
我需要为每个要使用它的对象声明一个脉动函数。如果我不这样做,我第二次调用该函数时,它将无法正常工作,因为动画实例已经 运行.