Skip to content

Commit

Permalink
Update Core Animation engine to support negative speed values (airbnb…
Browse files Browse the repository at this point in the history
…#1660)

Co-authored-by: Cal Stephens <cal.stephens@airbnb.com>
  • Loading branch information
2 people authored and Igor Moroz committed May 22, 2024
1 parent 80e2a7d commit e50c2f4
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 2 deletions.
35 changes: 35 additions & 0 deletions Example/iOS/ViewControllers/AnimationPreviewViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class AnimationPreviewViewController: UIViewController {
private var displayLink: CADisplayLink?

private var loopMode = LottieLoopMode.autoReverse
private var speed: CGFloat = 1
private var fromProgress: AnimationProgressTime = 0
private var toProgress: AnimationProgressTime = 1

Expand Down Expand Up @@ -142,6 +143,39 @@ class AnimationPreviewViewController: UIViewController {
}),
]),

UIMenu(
title: "Speed",
children: [
UIAction(
title: "-100%",
state: speed == -1 ? .on : .off,
handler: { [unowned self] _ in
speed = -1
updateAnimation()
}),
UIAction(
title: "-50%",
state: speed == -0.5 ? .on : .off,
handler: { [unowned self] _ in
speed = -0.5
updateAnimation()
}),
UIAction(
title: "50%",
state: speed == 0.5 ? .on : .off,
handler: { [unowned self] _ in
speed = 0.5
updateAnimation()
}),
UIAction(
title: "100%",
state: speed == 1 ? .on : .off,
handler: { [unowned self] _ in
speed = 1
updateAnimation()
}),
]),

UIMenu(
title: "From Progress...",
children: [
Expand Down Expand Up @@ -205,6 +239,7 @@ class AnimationPreviewViewController: UIViewController {

private func updateAnimation() {
animationView.play(fromProgress: fromProgress, toProgress: toProgress, loopMode: loopMode)
animationView.animationSpeed = speed
configureSettingsMenu()
}

Expand Down
17 changes: 15 additions & 2 deletions Sources/Public/Animation/AnimationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1210,10 +1210,19 @@ final public class AnimationView: AnimationViewBase {

if let coreAnimationLayer = animationlayer as? CoreAnimationLayer {
var animationContext = animationContext

// Core Animation doesn't natively support negative speed values,
// so instead we can swap `playFrom` / `playTo`
if animationSpeed < 0 {
let temp = animationContext.playFrom
animationContext.playFrom = animationContext.playTo
animationContext.playTo = temp
}

var timingConfiguration = CoreAnimationLayer.CAMediaTimingConfiguration(
autoreverses: loopMode.caAnimationConfiguration.autoreverses,
repeatCount: loopMode.caAnimationConfiguration.repeatCount,
speed: Float(animationSpeed))
speed: abs(Float(animationSpeed)))

// The animation should start playing from the `currentFrame`,
// if `currentFrame` is included in the time range being played.
Expand All @@ -1234,7 +1243,11 @@ final public class AnimationView: AnimationViewBase {
// the duration of the _first_ loop. Instead of setting `playFrom`, we just add a `timeOffset`
// so the first loop begins at `currentTime` but all subsequent loops are the standard duration.
default:
timingConfiguration.timeOffset = currentTime - animation.time(forFrame: animationContext.playFrom)
if animationSpeed < 0 {
timingConfiguration.timeOffset = animation.time(forFrame: animationContext.playFrom) - currentTime
} else {
timingConfiguration.timeOffset = currentTime - animation.time(forFrame: animationContext.playFrom)
}
}
}

Expand Down

0 comments on commit e50c2f4

Please sign in to comment.