簡體   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