Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add better support for negative animation speeds #377

Merged
merged 7 commits into from
Aug 17, 2017

Conversation

welshm
Copy link
Contributor

@welshm welshm commented Aug 17, 2017

  • The progress can be calculated basd on negative speed

    • The end frame would be a progress of 0 and the start frame a progress of 1
    • Recalculate progress when the speed changes, otherwise a "jump" will be seen when restarting animation
  • The frame for the progress can be calculated using the newly inverted progress

    • Determine the absolute progress based on current progress and determine which frame to use based on animation speed
  • This fixes issues of animations playing with negative speeds "bouncing" back to their final position
    which was caused by the final frame being calculated based on a completed progress of 1 which then did not take into
    account the negative animation speed.

- The progress can be calculated basd on negative speed
  + The end frame would be a progress of 0 and the start frame a progress of 1
  + Recalculate progress when the speed changes, otherwise a "jump" will be seen when restarting animation

- The frame for the progress can be calculated using the newly inverted progress
  + Determine the absolute progress based on current progress and determine which frame to use based on animation speed

- This fixes issues of animations playing with negative speeds "bouncing" back to their final position
  which was caused by the final frame being calculated based on a completed progress of 1 which then did not take into
  account the negative animation speed.
- The progress can be calculated basd on negative speed
  + The end frame would be a progress of 0 and the start frame a progress of 1
  + Recalculate progress when the speed changes, otherwise a "jump" will be seen when restarting animation

- The frame for the progress can be calculated using the newly inverted progress
  + Determine the absolute progress based on current progress and determine which frame to use based on animation speed

- This fixes issues of animations playing with negative speeds "bouncing" back to their final position
  which was caused by the final frame being calculated based on a completed progress of 1 which then did not take into
  account the negative animation speed.
return absoluteProgress;
} else {
// If the animation is playing backwards, the progress is inverted.
return 1 - absoluteProgress;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_animationProgress should always be in absolute time, not relative time. So if an animation is playing in reverse the progress will decrease, if it plays in autoReverse it will go up then down.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reasoning for inverting this was as follows:

If I have a 10 second animation that has run for 3 seconds and then I set the speed to -1, it would begin reversing the animation and the progress should jump from 30% to 70%.

Same for the frames. If there are 12 frames and I'm at frame 3 going forward, that is 25% completion but going in reverse it's 75% completion.

Otherwise, callers of the SDK who set the speed to negative one need to do all these inversions themselves. If that is the case, I'll keep the constant change above (kCompContainerAnimationKey) and add documentation to the header file about using negative speeds?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but speed implicitly changes the animation.
If you have auto-reverse animation going and you are tracking the animation in realtime via animationProgress it will be unclear where the animation is unless animationProgress is absolute.

Animation progress is intended to be a 1:1 mapping to the entire animation timeline, not to the currently playing animation.

This comes up in a lot of different states.
When playFromProgress:0.5 toProgress:1 is called then animationProgress is still absolute and reports a from 0.5 to 1 even though the internal animation 0-1 would map 0.5 to 1.

Another way to reverse playing an animation is to call playFromProgress:1 toProgress:0 with a positive speed. Then when you reverse the speed the animation would actually play forward.

@@ -14,6 +14,8 @@
#import "LOTAnimationCache.h"
#import "LOTCompositionContainer.h"

static NSString * const kCompContainerAnimationKey = @"play";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this! Things I always forget to do haha.

} else {
// It the animation is playing backwards, subtract the progress from the end.
return @(_sceneModel.endFrame.floatValue - absoluteProgress);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If _animationProgress stays absolute then this will also be fine as it was.

frame = (NSNumber *)playAnimation.toValue;
// Set the final frame based on the animation to/from values. If playing forward, use the
// toValue otherwise we want to end on the fromValue.
frame = [self _isPlayingForwards] ? (NSNumber *)playAnimation.toValue : (NSNumber *)playAnimation.fromValue;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just check the anim.speed here and to the binary switch?
This way the rest of the logic can be removed but it will still have the effect you are looking for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explained my reasoning for the change above. If you disagree, will keep this part of the change and revert the rest. Not sure which binary switch you're referring to, though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im referring to frame = [self _isPlayingForwards] ? (NSNumber *)playAnimation.toValue : (NSNumber *)playAnimation.fromValue;

Instead of [self _isPlayingForwards] ?
You can do anim.speed < 0 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left it as [self _isPlayingForwards] because I think that still makes sense to use in

- (void)playFromFrame:(nonnull NSNumber *)fromStartFrame
               toFrame:(nonnull NSNumber *)toEndFrame
        withCompletion:(nullable LOTAnimationCompletionBlock)completion

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems reasonable. In that case, on last nitpick can you change _isPlayingForwards to _isSpeedNegative or something similar? Since there are cases where speed can be negative and the animation is still technically playing forward.

Just to keep confusion down in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me! Thanks for the speedy comments and review

…olutes

- Update header documentation to make it clear how animatinoSpeed and animationProgress interact
- Leave fixes in there that stop reversed animations "snapping" back
@@ -46,9 +46,12 @@ typedef void (^LOTAnimationCompletionBlock)(BOOL animationFinished);

// TODO
/// Sets a progress from 0 - 1 of the animation. If the animation is playing it will stop and the compeltion block will be called.
/// The animation progress is in terms of absolute progress of the defined animation and does not
/// take into account negative speeds.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind changing the wordage here a bit? Currently it seems as if animationProgress does not work if the speed is negative, thats not true.

Something like 'the current progress of the animation in absolute time. EG a value of 0.75 always represents the same point in animaiton regardless of positive or negative speed.'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

Just a thought... if I call play, the progress goes from 0 to 1 (regardless of play direction) where as setting the value directly will cause a different behaviour so the property does different things depending on if the animation is playing or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually that's not necessarily true - never mind!

@property (nonatomic, assign) CGFloat animationProgress;

/// Sets the speed of the animation. Accepts a negative value for reversing animation
/// Sets the speed of the animation. Accepts a negative value for reversing animation.
/// Negative speeds do not affect animationProgress
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, not necessarily true. Negative speeds do affect animation progress in that as the animation plays the progress is updated to its absolute value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed comment as I think the comment on animationProgress better captures the intent

@buba447
Copy link
Collaborator

buba447 commented Aug 17, 2017

Great! Thank you for grabbing this and for working with me on it!

@buba447
Copy link
Collaborator

buba447 commented Aug 17, 2017

@welshm Do you mind grabbing those merge conflicts?

@buba447
Copy link
Collaborator

buba447 commented Aug 17, 2017

@welshm Nevermind! I got it!

@buba447 buba447 merged commit 8f86819 into airbnb:master Aug 17, 2017
@welshm welshm deleted the reverseSupport branch August 17, 2017 22:09
NSGolova pushed a commit to clario-tech/lottie-ios that referenced this pull request Nov 19, 2020