How are you checking if an animation is in progress? I have been manually juggling that by adding an isAnimating member variable and updating it on valueChanged and completeCallback. Feels like this should be a part of the API, did I miss something?
How to best deal with debounces? It’s common for a user to click repeatedly, which tends to result in visual chaos unless debounces are handled. I’m just refusing to create a new animation until the old one is still in progress, but there’s situations where animations need to “undo from current position” and so on (be reversible). Example of no debounce in demo:
The API is verbose, especially when you just want to sprinkle some easings around. Are people building wrappers for it?
I’ve only built this very simple one so far. The goal was to have an API with 3 LOC, easy to “sprinkle” into a component:
Hold an Animation object
Call animation.springTo (nextLocation, 250) where nextLocation is a pixel value and 250 is the duration.
Access animation.currentPosition in paint.
The idea is to build a set of Animators that you’ll just reuse as needed. However, this is currently only optimized for only one animation per component (and other assumptions). Works well, but curious what others are doing.
The way CSS handles this is, when you change the target value of a transition/animation, the start value is updated to whatever the current value is.
So if you’re easing linearly from 0 to 100 over 10s, then after 5s update the target value to say 200, it will then take a further 10s to transition from 50 to 200.
How you’d do that with juce::Animator IDK, but that’s how I’d expect an API to behave when you change the target mid-transition.
This is exactly how JIVE works, FWIW:
No, but I’d be tempted to create some templated types that wrap the animator, the source & target values, etc. so you can just have a Animated<juce::Colour> or whatever
Awesome! Somehow didn’t realize JIVE has a full fledged animation api!
if you’re easing linearly from 0 to 100 over 10s, then after 5s update the target value to say 200, it will then take a further 10s to transition from 50 to 200.
Back to JUCE — It looks like runningIndefinitely isn’t available on AnimatorSetBuilder — I’m looking to fade something in, followedBy fading something out — and have that loop.
@Attila would this be a difficult addition, or am I missing a way to do that?
It would also really be convenient if followedBy and friends were methods on juce::ValueAnimatorBuilder so one could chain them like so (the set wrapping would be under the hood).
const auto pulseIn = defaultAnimator (1, durationMs).withEasing (juce::Easings::createEaseOut());
const auto pulseOut = defaultAnimator (0, durationMs).withEasing (juce::Easings::createEaseOut());
animator = pulseIn.followedBy (pulseOut).runningIndefinitely().build();
I have a related question which I wanted to ask about. If I remember correctly, why you set runningIndefinitely as true, the float value in the callback ramps to 1 in the allotted time and then stays there. How would you get around this, like if you wanted to pulse the brightness of something up and down?
The callback value is only determined by the easing function and can go beyond 1.0. From the ValueChangedCallback doc:
[…] unless the Animator is infinitely running, in which case the progress will go beyond 1.0 […]
You could create a pulsing effect by supplying an easing function e.g. [] (auto progress) { return std::sin (progress); } and use it with an infinitely running animator.
Yeah, not sure what to do without runningIndefinitely on SetBuilder. For my specific simple case, 2 separate Animators that trigger each other onComplete seemed like a possibility, but would remove some composability…
My plan was to have 2 Animators in a Set, one that eased the value from 0 to 1 and one that eased the value from 1 to 0.
The problem with this route is it eliminates being able to any of the built-in and classic cubic easings. Chaining 2 Animators in a Set seems more friendly. Adding a bool reverse to start () would also open up some options.
Just dropping a note to say this got me 90% of the way there — the limitation is that the same easing has to be used for both fade in and out.
My ideal path would still be 2 Animators in a Set (assuming runningIdefinitely can get added to Set) — that would let me easily tune timing/easings, make animations symmetrical, etc.
I thought maybe I could supply a delay to AnimatorSetBuilder::togetherWith (double delayMs) but actually I’m confused because that constructor doesn’t actually take an animator.
Example use case is animating 3 rects into existence:
for (auto i = 0; i < rects.size(); ++i)
{
g.fillRect (rects[i].withWidth (animation.currentPosition * width));
}
I’ve been building things like the above with a wrapper (handles the boilerplate, only exposes the eased value to my component) — I was thinking I’d create a wrapper function like .stagger (numberOfElements, msToStagger) function or something that spawns n animators in a set and provides ncurrentPositions to my component… but not quite sure how to wrangle AnimationSetBuilder…
In short, is there a way to do this with Set, or is the only way to have 3 separate animations that I manually start based on the previous animator’s value?
Hello folks! Not sure how to answer the above questions, but thought I’d share what I’m currently trying to do with these resources.
The plugin I’m working on shows a sort of map, you could say, and a section of it is lit up. The lit section can be moved by the user, and I’m shifting the map to follow the highlighted section around. Doing it naively felt extremely jerky, so the easings are super helpful here.
The first problem I’m running into is the debounce thing you mentioned Sudara. I’ve set up my animator so that it reacts the way ImJimmi described above if interrupted and restarted. Which would work well enough if linear, but I’m using createEaseInOut(), which looks much better generally for my purposes. But if interrupted by user input, the easing obviously resets, which feels really weird.
I’m wondering if it may be possible to solve it using createCubicBezier() instead, starting with the InOut numbers. Then before calling start() while already running, set the values to the Bezier function so the current rate of change is maintained, and ease out (or maybe in out) from there). I’m not sure what values those should be though. Anyone has any ideas I’m all ears.
I build a wrapper around VBankAnimatorUpdater which other people might find useful. I was trying to solve two problems:
The problem of multiple animations (i.e. debouncing).
Lifetime issues for juce::Animator (you have to keep them alive somewhere after you have added them, and I wanted a general solution for this).
There are three mehods which offer different options for animator sequences:
runAnimatorInSeries(): wait for the last Animator to finish before starting this one
runAnimatorInParallel() : run this animator now, regardless of what other animators are doing
runBlockingAnimator() : run this animator only if the last one has finished.
All of these methods keep the animators alive, and the last one solves the debouncing issue mentioned above. Note that these three methods do not interact with each other (i.e. runBlockingAnimator() won’t check to see if parallel or series animations are running, etc.). It’s really just three separate systems combined in one class.