繁体   English   中英

为什么在CATransactions大约同时开始和结束时动画抖动?

[英]Why does animation jitter when CATransactions begin and end at about the same time?

问题

如何解决滚动动画中的抖动?

如下面的动画所示,每次音符(黑色椭圆形)到达垂直蓝线时都会出现短暂的抖动,这使得音符向后移动了一秒钟。

滚动动画是由一系列CATransactions触发的,每当一个滚动动画完成而另一个滚动动画开始时,就会发生抖动。

全屏录制

在下面的慢动作视频中,看起来好像实际上彼此重叠有两个椭圆,一个椭圆停止并逐渐消失,而另一个继续滚动。 但是,该代码实际上并没有在另一个椭圆上创建一个椭圆。

慢动作变焦屏幕录制

视频(gif)来自iPhone SE屏幕录像,而不是模拟器。

问题约束:

  • 该动画的主要目标是在每个音符之间进行平滑, 线性的滚动,并在每个音符头到达蓝线时准确地开始和结束。 蓝线代表伴随音乐的当前时间点。

  • 滚动持续时间和距离会有所不同,并且这些值是在滚动过程中动态生成的,因此在执行期间硬编码滚动速率将不起作用。

尝试的解决方案

  1. 设置isScrolling标志(以防止新动画在以前的动画完成之前开始)不能解决抖动问题。
  2. 将滚动开始时间设置为稍早一点(即重新绘制1或2个屏幕的长度),也不起作用。
  3. 一起执行1和2可以稍微改善该问题,但不能解决。

代码段

StaffLayer (定义如下)控制滚动:

  • .scrollAcrossCurrentChordLayer()管理CATransaction .scrollTimer CADisplayLink调用此方法。

  • .scrollTimer .start().scrollTimer管理CADisplayLink

为清楚起见,代码严重缩写

class StaffLayer: CAShapeLayer, CALayerDelegate {

    var currentTimePositionX: CGFloat // x-coordinate of blue line
    var scrollTimer: CADisplayLink? = nil

    /// Sets and starts `scrollTimer`, which is a `CADisplayLink`
    func start() {
        scrollTimer = CADisplayLink(
            target: self,
            selector: #selector(scrollAcrossCurrentChordLayer)
        )
        scrollTimer?.add(
            to: .current,
            forMode: .defaultRunLoopMode
        )
    }

    /// Trigger scrolling when the currentChordLayer.startTime has passed
    @objc func scrollAcrossCurrentChordLayer() {

        // don't scroll if the chord hasn't started yet
        guard currentChordLayer.startTime < Date().timeIntervalSince1970 else { return }

        // compute how far to scroll
        let nextChordMinX = convert(
            nextChordLayer.bounds.origin,
            from: nextChordLayer
        ).x
        let distance = nextChordMinX - currentTimePositionX // distance from note to vertical blue line

        // perform scrolling in CATransaction
        CATransaction.begin()
        CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(
            name: kCAMediaTimingFunctionLinear
        ))
        CATransaction.setAnimationDuration(
            chordLayer.chord.lengthInSeconds ?? 0.0
        )
        bounds.origin.x += distance
        CATransaction.commit()

        // set currentChordLayer to next chordLayer
        currentChordLayer = currentChordLayer.nextChordLayer
    }
}

使CATransactions重叠

这似乎是一种破解,但它可以解决抖动问题。

如果CATransaction应转向起源x一段y秒,你可以设置动画去1.1 * x在一段1.1 * y秒。 滚动速率相同,但是第二个CATransaction在第一个完成之前开始,并且抖动消失。

这可以通过对原始代码进行少量修改来实现:

let overlapFactor = 1.1
CATransaction.setAnimationDuration(
        (chordLayer.chord.lengthInSeconds ?? 0.0)
        * overlapFactor // <- ADDED OVERLAP FACTOR HERE
)
bounds.origin.x += distance*CGFloat(overlapFactor) // <- ADDED OVERLAP FACTOR HERE
CATransaction.commit()

我不能对此做一个严格的解释。 这可能与CoreAnimation中幕后发生的优化有关。

缺点是,如果重叠太大,则重叠会干扰后续的动画,因此,这不是一个好的通用解决方案,只是一个hack。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM