简体   繁体   English

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

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

Problem 问题

How can I fix the jitter in my scrolling animation? 如何解决滚动动画中的抖动?

As seen in the animations below, there is a brief jitter every time the notes (black ovals) reach the vertical blue line, which makes it appear that the notes went backwards for a split second. 如下面的动画所示,每次音符(黑色椭圆形)到达垂直蓝线时都会出现短暂的抖动,这使得音符向后移动了一秒钟。

The scrolling animations are triggered by a series of CATransactions, and the jitter occurs every time one scrolling animation completes and another starts. 滚动动画是由一系列CATransactions触发的,每当一个滚动动画完成而另一个滚动动画开始时,就会发生抖动。

全屏录制

In the slow motion video below, it looks like there are actually two ovals on top of each other, one which stops and fades out while the other keeps scrolling. 在下面的慢动作视频中,看起来好像实际上彼此重叠有两个椭圆,一个椭圆停止并逐渐消失,而另一个继续滚动。 But, the code does not actually create one oval atop another. 但是,该代码实际上并没有在另一个椭圆上创建一个椭圆。

慢动作变焦屏幕录制

The videos (gifs) are from an iPhone SE screen recording, not a simulator. 视频(gif)来自iPhone SE屏幕录像,而不是模拟器。

Problem Constraints: 问题约束:

  • A key objective of this animation is to have smooth, linear scrolling across each note, that begins and ends exactly as each note head reaches the blue line. 该动画的主要目标是在每个音符之间进行平滑, 线性的滚动,并在每个音符头到达蓝线时准确地开始和结束。 The blue line represents the current point in time in accompanying music. 蓝线代表伴随音乐的当前时间点。

  • The scrolling durations and distances will vary, and these values are generated dynamically during the scrolling, so hard coding the scroll rate for the duration of execution will not work. 滚动持续时间和距离会有所不同,并且这些值是在滚动过程中动态生成的,因此在执行期间硬编码滚动速率将不起作用。

Attempted Solutions 尝试的解决方案

  1. Setting an isScrolling flag, to prevent new animations from starting before previous animations have completed, did not fix the jitter. 设置isScrolling标志(以防止新动画在以前的动画完成之前开始)不能解决抖动问题。
  2. Setting the scrolling start time to happen slightly early (ie the length of 1 or 2 screen redraws), didn't work either. 将滚动开始时间设置为稍早一点(即重新绘制1或2个屏幕的长度),也不起作用。
  3. Doing 1 and 2 together slightly improved the problem, but did not fix it. 一起执行1和2可以稍微改善该问题,但不能解决。

Code Snippet 代码段

A StaffLayer (defined below) controls the scrolling: StaffLayer (定义如下)控制滚动:

  • .scrollAcrossCurrentChordLayer() manages the CATransaction . .scrollAcrossCurrentChordLayer()管理CATransaction This method is called by the .scrollTimer CADisplayLink .scrollTimer CADisplayLink调用此方法。

  • .start() and .scrollTimer manage the CADisplayLink .scrollTimer .start().scrollTimer管理CADisplayLink

Code heavily abbreviated for clarity 为清楚起见,代码严重缩写

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

Make the CATransactions overlap 使CATransactions重叠

This seems like a hack, but it fixes the jitter. 这似乎是一种破解,但它可以解决抖动问题。

If the CATransaction should shift origin by x over a period of y seconds, you can set the animation to go 1.1 * x over a period of 1.1 * y seconds. 如果CATransaction应转向起源x一段y秒,你可以设置动画去1.1 * x在一段1.1 * y秒。 The the scroll rate is the same, but the second CATransaction starts before the first one has finished, and the jitter disappears. 滚动速率相同,但是第二个CATransaction在第一个完成之前开始,并且抖动消失。

This can be achieved by a small modification of the original code: 这可以通过对原始代码进行少量修改来实现:

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

I can't give a rigorous explanation for why this works. 我不能对此做一个严格的解释。 It may be related to the optimizations happening behind the scenes in CoreAnimation. 这可能与CoreAnimation中幕后发生的优化有关。

A drawback is that the overlap can interfere with subsequent animations if the overlap is too large, so this is not a good general purpose solution, just a hack. 缺点是,如果重叠太大,则重叠会干扰后续的动画,因此,这不是一个好的通用解决方案,只是一个hack。

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

相关问题 为什么我应该使用基于块的动画而不是开始/结束动画? - Why should I use the block-based animation rather than begin/end animation? CAKeyframeAnimation从头到尾重复动画 - CAKeyframeAnimation repeat animation from begin to end 为什么同时插入和删除UITableView会崩溃? - Why does UITableView crash when I insert and delete at the same time? 使用AVSynchronizedLayer时,动画无法启动 - Animation doesn't begin when using AVSynchronizedLayer iPad方向问题。 该应用何时开始了解其方向? - iPad orientation issue. When does the app begin to know about its orientation? 显示视图时如何开始动画? - How to begin animation when view is displayed? 如何在iOS中从开始时间到结束时间修剪或播放音频? - how to trim or play a audio from begin time to end time in iOS? 在核心动画开始时间之前分层样式的正确方法是什么? - What is a correct way to layer styling before begin time of core animation? iOS:了解有关UIView上的动画启动和动画结束的信息 - iOS: get informed about a started animation on a UIView and the animation end 为什么UITableView在同时滚动和更新时会丢失对标题标题的跟踪? - Why does UITableView lose track of section header when scrolling and updating at the same time?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM