Comments (5)
Hi @ryanmoelter,
nice to hear you are enjoying the library!
You are right, this behaviour might seem unexpected, but it's a design decision I made when writing the library.
The then()
-delayed animations are (more or less) treated as if you had created them after the delay.
If you don't do this, you might find yourself with even stranger behaviour, where views have intermediate states you could never get to with the target animation values.
Consider this example:
view.setX(0);
AdditiveAnimator.animate(view).x(100).then().x(0).start();
AdditiveAnimator.animate(view).x(50).then().x(100).start();
In this case, because of the additive nature of the animations the view would actually go past the maximum set value of 100 while it was animating to 50, before settling at 100!
The relative changes are:
0 to 100 = +100 100 to 0 = -100
0 to 50 = +50 50 to 100 = +50
But the order in which the animations would be played is be this:
+100 +50 -100 +50
Notice that x shortly goes to 150 in this version!
The way the library works right now would make the changes in this order:
0 to 100 = +100 100 to 50 = -50
50 to 0 = -50 0 to 100 = +100
=>
+100 -50 -50 +100
This implementation makes sure that the animation never produces intermediate values that were not enqueued, but suffers from the problem that you described in your ticket.
Changing this behaviour itself would not require a lot of code (1 line, if I'm not mistaken (famous last words)), but I'm hesitant to make this change because of the implications it would have on existing projects.
Maybe a flag to explicitly enable this behaviour would make sense?
In the meantime, you could opt out of the additive behaviour by calling cancelAnimations()
on the views in question for this specific case, or use AnimationEndListener
s instead of then()
-chaining, where you can check for some condition before animating to alpha = 1
.
Cheers,
David
from android_additive_animations.
Another idea I had was to add an optional parameter to the then()
method family: a condition to be evaluated before running the next animation block.
In you example, the code could look like this:
animator.animate(view)
.alpha(0f)
.thenIf(() -> mIsViewHidden)
.target(otherView)
.alpha(1f)
.start();
In this case, otherView
would not be animated to alpha = 1f
if the instance variable mIsViewHidden
is false
.
This condition would be propagated to all subsequently enqueued animations in the same animation chain, but you could always override it manually at any point to skip only some animations.
What would you think of this solution? It also has some pitfalls, but solves a common problem of statefulness in animation chaining.
from android_additive_animations.
Wow, this ended up at the bottom of my inbox, sorry for the late reply!
What I'm trying to achieve with the proposed style of blending is to make it easy for my teammates (who may not understand the library as well as you or I do) to get the right behavior, defined mostly by not ending in an unexpected state.
I see the tradeoff you made, and it makes sense. You want to keep the animated value within the expected range, i.e. within the bounds of the values passed into the animations.
I think adding a flag would work fine! I understand not wanting to change the behavior for existing codebases.
Unfortunately, the other two options won't work for me. Calling cancelAnimations()
defeats the purpose of the library by preventing the blending of animations. And the thenIf(...)
, while potentially useful, it requires users to assert that the animation finished, which is extra and obscure work and very easy to accidentally leave out.
Overall, I'd love if we could add a flag for the style of blending I'm asking for!
P.S. I understand not wanting to have animations go outside of the expected bounds, but you can already do that. Consider this:
view.setX(100);
AdditiveAnimator.animate(view).setDuration(200).x(0).start();
AdditiveAnimator.animate(view).setDuration(50).x(100).start();
This will do the following if I understand how animations get added together correctly:
t | x |
---|---|
0ms | 100 |
50ms | 75 |
100ms | 150 |
150ms | 125 |
200ms | 100 |
So, the existing framework allows for out-of-bounds animations, as long as they're overlapping. That doesn't address the people who are relying on the library, so I still don't think we should change the default implementation without a major version bump, but I do still think it makes more sense.
from android_additive_animations.
Hi Ryan,
I understand your needs in this case, but I'm still not quite convinced the proposed change is a good one - it breaks basically all of the then()
-chaining demos quite badly.
Some exhibit the 'out of bounds' problem where views drift off-screen, others break down completely with views jumping from place to place.
It's not obvious exactly when and how an animation will break (except for repeating and reversing animations, which are always busted).
I couldn't predict exactly what would happen in each of the demos by looking at the code, so it's probably incomprehensible to someone who is not familiar with the library works...
I can push the change to a branch if you'd like to try it out yourself?
Regarding the thenIf()
idea - I'm not really convinced by the argument that you would need to additionally keep track of the state of your views, you need to do that anyway in my opinion.
You are probably going to have a state flag of some sort in your ViewModel (if you use the MVVM architecture) to preserve the state for interface rotation etc, which you could simply pass to the thenIf()
lambda.
It's true that it might be easy to forget about this, or miss it completely as a newcomer, but I would argue the same holds true for selecting the proper blend-mode for each animation (which also requires more knowledge of the inner workings of the library)?
You're right about the possibility of going out-of-bounds by starting two animations with different durations, I had not thought about that!
I don't think I can do anything about the specific case you mentioned though :/
from android_additive_animations.
Hi Ryan,
I'm happy to tell you that I've made some progress on this issue, by rethinking what the user actually wants to do in cases such as your initial example.
I think the best way of abstracting the expected behaviour is to think about it as the state of the view, so that is the basis of a new approach I'm trying: explicitly animate between predefined States as opposed to animating specific parameters which coincide with the expected UI state.
One of the new constructs is an AnimationState
object, which has a list of animations it wants to run, an ID, and optional start/end actions.
The animations are ONLY run if the animated object is still in the state that enqueued them.
Using the new API looks like this:
AdditiveAnimator.animate(view1)
.state(AnimationStates.HIDDEN)
.thenWithDelay(150)
.target(view2)
.state(AnimationStates.VISIBLE)
.start();
… and behaves exactly like you want, even when quickly triggering between the HIDDEN
and VISIBLE
states!
(The AnimationStates
class just holds some predefined AnimationState
values for better readability.)
I'm interested in hearing what you think about it, you can check out the code and play around with the demo in the animation_states branch!
from android_additive_animations.
Related Issues (19)
- Gradle build failed HOT 4
- Leak HOT 9
- Provide Kotlin extensions HOT 2
- How to reset, reverse and pause/resume additive animations? HOT 1
- Using setStartDelay after creating a staggered animation removes stagger
- Move to AndroidX HOT 3
- Move away from jcenter HOT 2
- Library sometimes crashes when mRunningAnimationsManager is null internally HOT 6
- Content gone in 1.9.2 but works in 1.9.1 HOT 2
- Remove gifs from this repo
- iOS? HOT 1
- superfluous? HOT 1
- purpose? HOT 1
- effect? HOT 1
- Annoying `AdditiveAnimator` constructor with Kotlin HOT 7
- Question HOT 13
- pivotXY for view rotation around center HOT 1
- Cancel repeating animation HOT 8
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from android_additive_animations.