【问题标题】:Modify a value during a CAAnimation在 CAAnimation 期间修改值
【发布时间】:2020-01-02 02:51:04
【问题描述】:

假设你有一个简单的动画

    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)
    }
}

你当然不能那样做——

试图修改只读层

也许你可以有一个自定义的动画属性

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
}

如何“手动”设置动画属性的值,每一帧?

有没有办法简单地提供(覆盖?)一个计算每帧值的函数?如何根据其他属性或其他自定义属性的计算,在某些属性上为每帧设置值?

(脚注 1 - 一个 hacky 解决方法是滥用关键帧动画,但这并不是重点。)

(脚注 2 - 当然,通常最好使用 CADisplayLink 以普通的老式手动方式简单地制作动画,而不用担心 CAAnimation,但这个 QA 是关于 CAAnimation 的。)

【问题讨论】:

  • 你知道如何暂停动画吗?在这里搜索一下,可以找到很多信息。
  • 嗨@E.Coms!我想你根本不明白这里问的是什么。
  • 我知道你的意思,但你不能用公共 API 来做到这一点。所以这些方法并不安全。

标签: ios swift caanimation


【解决方案1】:

粗略地说,CoreAnimation 使您能够根据某个计时函数在某些值之间执行插值。

CAAnimation 有一个 private method 在给定时间将其插值应用于图层。

将该值注入表示层发生在从模型层类的实例(init(layer:) 调用)创建表示层之后,在display 方法调用堆栈中draw(in ctx: CGContext) 之前方法调用。

因此,假设表示层的类与您的自定义层的类相同,一种解决方法是使动画属性与用于呈现层内容的属性不同。您可以为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 ,我通过对其属性的插值来为 NSAttributedString 值的动画执行此操作,而 article 其中我描述了这种方法。

【讨论】:

  • 谢谢,我认为您的 第 3 段 是问题的核心,很好。但我........不知道该怎么做 :) 例如你说 could animate interpolatedValue property 但我真的不在我们所说的上下文中知道其中的原因。 (对不起!:))也许及时你可以慷慨地添加更多的kode细节......:O
  • 查看我的文章,我将展示如何创建动画图层属性的示例。我将在此处添加一些代码。
  • 明白了!会做。很棒的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-20
  • 2010-10-13
  • 2017-03-04
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 2019-11-12
相关资源
最近更新 更多