在 CAAnimation 期间修改值
Modify a value during a CAAnimation
假设你有一个简单的动画
let e : CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd")
e.duration = 2.0
e.fromValue = 0
e.toValue = 1.0
e.repeatCount = .greatestFiniteMagnitude
self.someLayer.add(e, forKey: nil)
一层
private lazy var someLayer: CAShapeLayer
很容易读取每个动画步骤的 strokeEnd 值。
只需更改为自定义图层
private lazy var someLayer: CrazyLayer
然后
class CrazyLayer: CAShapeLayer {
override func draw(in ctx: CGContext) {
print("Yo \(presentation()!.strokeEnd)")
super.draw(in: ctx)
}
}
实际上能够在每一步设置图层的 属性 值会非常方便。
示例:
class CrazyLayer: CAShapeLayer {
override func draw(in ctx: CGContext) {
print("Yo \(presentation()!.strokeEnd)")
strokeStart = presentation()!.strokeEnd - 0.3
super.draw(in: ctx)
}
}
当然你不能那样做 -
attempting to modify read-only layer
也许你可以有一个自定义动画 属性
class LoopyLayer: CAShapeLayer {
@NSManaged var trick: CGFloat
override class func needsDisplay(forKey key: String) -> Bool {
if key == "trick" { return true }
return super.needsDisplay(forKey: key)
}
... somehow for each frame
strokeEnd = trick * something
backgroundColor = alpha: trick
}
如何"manually"设置每帧动画属性的值?
是否有一种方法可以简单地提供(覆盖?)一个计算每帧值的函数?如何通过计算其他属性或其他自定义设置某些属性的每一帧的值 属性?
(脚注 1 - 一种变通方法是滥用关键帧动画,但这不是重点。)
(脚注 2 - 当然,通常最好使用 CADisplayLink 以普通的旧手动方式简单地制作动画,而不用担心 CAAnimation,但这个 QA 是关于 CAAnimation 的。)
粗略地说,CoreAnimation
让你可以根据一定的时间函数在一定的值之间进行插值。
CAAnimation
有一个 private method 在给定时间将其插值应用于图层。
在模型层 class(init(layer:)
调用)的实例中创建表示层之后,在 [= 的堆栈中将该值注入表示层15=] 方法调用在 draw(in ctx: CGContext)
方法调用之前。
因此,假设表示层的 class 与自定义层的 class 相同,一种解决方法是让动画 属性 不同于 属性 用于渲染层的内容。您可以为 interpolatedValue
属性 制作动画,并让另一个动态 属性 strokeEnd
动态计算出 interpolatedValue
值中每一帧的适当值。
在极端情况下,interpolatedValue
可能只是从 0 到 1 的动画进度,基于此您可以动态计算表示层中的 strokeEnd
值。您甚至可以向您的代表询问给定进度的价值:
@objc @NSManaged open dynamic var interpolatedValue: CGFloat
open override class func needsDisplay(forKey key: String) -> Bool {
var result = super.needsDisplay(forKey: key)
result = result || key == "interpolatedValue"
return result
}
open var strokeEnd : CGFloat {
get {
return self.delegate.crazyLayer(self, strokeEndFor: self.interpolatedValue)
}
set {
...
}
}
let animation = CABasicAnimation(keyPath: "interpolatedValue")
animation.duration = 2.0
animation.fromValue = 0.0
animation.toValue = 1.0
crazyLayer.add(animation, forKey: "interpolatedValue")
另一种解决方法是使用 CAKeyframeAnimation
,您可以在其中指定必须在相应时间将哪个值分配给动画 属性。
如果您可能想为每一帧分配一个内插分量的复数值,我有一个 project where I do that for animation of NSAttributedString
value by interpolation of its attributes, and the article 描述方法的地方。
假设你有一个简单的动画
let e : CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd")
e.duration = 2.0
e.fromValue = 0
e.toValue = 1.0
e.repeatCount = .greatestFiniteMagnitude
self.someLayer.add(e, forKey: nil)
一层
private lazy var someLayer: CAShapeLayer
很容易读取每个动画步骤的 strokeEnd 值。
只需更改为自定义图层
private lazy var someLayer: CrazyLayer
然后
class CrazyLayer: CAShapeLayer {
override func draw(in ctx: CGContext) {
print("Yo \(presentation()!.strokeEnd)")
super.draw(in: ctx)
}
}
实际上能够在每一步设置图层的 属性 值会非常方便。
示例:
class CrazyLayer: CAShapeLayer {
override func draw(in ctx: CGContext) {
print("Yo \(presentation()!.strokeEnd)")
strokeStart = presentation()!.strokeEnd - 0.3
super.draw(in: ctx)
}
}
当然你不能那样做 -
attempting to modify read-only layer
也许你可以有一个自定义动画 属性
class LoopyLayer: CAShapeLayer {
@NSManaged var trick: CGFloat
override class func needsDisplay(forKey key: String) -> Bool {
if key == "trick" { return true }
return super.needsDisplay(forKey: key)
}
... somehow for each frame
strokeEnd = trick * something
backgroundColor = alpha: trick
}
如何"manually"设置每帧动画属性的值?
是否有一种方法可以简单地提供(覆盖?)一个计算每帧值的函数?如何通过计算其他属性或其他自定义设置某些属性的每一帧的值 属性?
(脚注 1 - 一种变通方法是滥用关键帧动画,但这不是重点。)
(脚注 2 - 当然,通常最好使用 CADisplayLink 以普通的旧手动方式简单地制作动画,而不用担心 CAAnimation,但这个 QA 是关于 CAAnimation 的。)
粗略地说,CoreAnimation
让你可以根据一定的时间函数在一定的值之间进行插值。
CAAnimation
有一个 private method 在给定时间将其插值应用于图层。
在模型层 class(init(layer:)
调用)的实例中创建表示层之后,在 [= 的堆栈中将该值注入表示层15=] 方法调用在 draw(in ctx: CGContext)
方法调用之前。
因此,假设表示层的 class 与自定义层的 class 相同,一种解决方法是让动画 属性 不同于 属性 用于渲染层的内容。您可以为 interpolatedValue
属性 制作动画,并让另一个动态 属性 strokeEnd
动态计算出 interpolatedValue
值中每一帧的适当值。
在极端情况下,interpolatedValue
可能只是从 0 到 1 的动画进度,基于此您可以动态计算表示层中的 strokeEnd
值。您甚至可以向您的代表询问给定进度的价值:
@objc @NSManaged open dynamic var interpolatedValue: CGFloat
open override class func needsDisplay(forKey key: String) -> Bool {
var result = super.needsDisplay(forKey: key)
result = result || key == "interpolatedValue"
return result
}
open var strokeEnd : CGFloat {
get {
return self.delegate.crazyLayer(self, strokeEndFor: self.interpolatedValue)
}
set {
...
}
}
let animation = CABasicAnimation(keyPath: "interpolatedValue")
animation.duration = 2.0
animation.fromValue = 0.0
animation.toValue = 1.0
crazyLayer.add(animation, forKey: "interpolatedValue")
另一种解决方法是使用 CAKeyframeAnimation
,您可以在其中指定必须在相应时间将哪个值分配给动画 属性。
如果您可能想为每一帧分配一个内插分量的复数值,我有一个 project where I do that for animation of NSAttributedString
value by interpolation of its attributes, and the article 描述方法的地方。