[英]Why should I use the block-based animation rather than begin/end animation?
[英]Why does animation jitter when CATransactions begin and end at about the same time?
如何解決滾動動畫中的抖動?
如下面的動畫所示,每次音符(黑色橢圓形)到達垂直藍線時都會出現短暫的抖動,這使得音符向后移動了一秒鍾。
滾動動畫是由一系列CATransactions觸發的,每當一個滾動動畫完成而另一個滾動動畫開始時,就會發生抖動。
在下面的慢動作視頻中,看起來好像實際上彼此重疊有兩個橢圓,一個橢圓停止並逐漸消失,而另一個繼續滾動。 但是,該代碼實際上並沒有在另一個橢圓上創建一個橢圓。
視頻(gif)來自iPhone SE屏幕錄像,而不是模擬器。
問題約束:
該動畫的主要目標是在每個音符之間進行平滑, 線性的滾動,並在每個音符頭到達藍線時准確地開始和結束。 藍線代表伴隨音樂的當前時間點。
滾動持續時間和距離會有所不同,並且這些值是在滾動過程中動態生成的,因此在執行期間硬編碼滾動速率將不起作用。
嘗試的解決方案
isScrolling
標志(以防止新動畫在以前的動畫完成之前開始)不能解決抖動問題。 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
}
}
這似乎是一種破解,但它可以解決抖動問題。
如果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.