playTo (and playFrames) being ignored when it switches from fastPreview to full
laurensV opened this issue · 4 comments
when you are in the middle of a playTo
animation (e.g. playTo(100)
), while using the fastPreview mode, and at some point during this animation it finished the full preload and switches from fast to full, it ignores the playTo and just switches to regular play()
, causing it to also continue after frame 100.
animate-images/src/ImagePreloader.js
Line 129 in a144706
const wasAnimating = plugin.isAnimating();
const matchFrame = this._settings?.fastPreview.matchFrame;
plugin.setFrame( matchFrame ? matchFrame(this._data.currentFrame) : 1 );
if ( wasAnimating ) plugin.play();
There you see that by first calling setFrame (causing a call to stop()
, which resets _framesLeftToPlay
) and then play()
again. Can't we just simply change it to this, so that it only loses the playTo animation when you dont use the matchFrame option?
const matchFrame = this._settings?.fastPreview.matchFrame;
if (!matchFrame) {
const wasAnimating = plugin.isAnimating();
plugin.setFrame(1);
if (wasAnimating) plugin.play();
}
If so, happy to make a PR for this :)
@laurensV In your version you don't call matchFrame
even if it's defined. So you should call setFrame
to use the result of matchFrame
, which will cause _framesLeftToPlay
to be reset anyway. That means we need a way to store _framesLeftToPlay
value before setFrame
and then restore it.
If you wanna make a PR you can create new function in AnimateImages
that will get of set _framesLeftToPlay
value and then pass it to Animation
constructor. Something like this:
#manageFramesLeftToPlay(value){
// 'arguments' to handle possible 'undefined' value
if (arguments.length) this.#animation._framesLeftToPlay = value;
else return this.#animation._framesLeftToPlay;
}
But do we really need to continue playTo
and playFrames
after full load? With play()
, we simply continue the infinite animation, saving the progress percentage after full load. With playTo
and playFrames
we have a specific number of frames to play, but the total frames numer changes.
For example when fastPreload is 30 images, full sequence is 150. playTo(15)
means animation to the middle. If full preload finishes when animation at frame 9, the rest is 6 frames. But if we play 6 remaining frames, end position will be at 15/150, which is far from the middle.
So maybe it's better to stop the animation if _framesLeftToPlay
is set and let developers start a new one (according to the updated quantity) in onFastPreloadFinished
callback?
My scenario is a bit complicated (will happily share the end-result with you once its live) and never just plays the entire animation. Based on certain steps in the process it will play a certain section of the animation back and forth and in some scenarios drag is enabled and in other scenarios drag is disabled etc. Thats why it would be helpful if it would just keep playing to playTo and playFrames in my scenario. For me the fastPreload has the same amount of frames as the full sequence (just with lower quality images), so that's why I don't need the matchFrame
function, but in your scenario you would need that of course.
For example when fastPreload is 30 images, full sequence is 150. playTo(15) means animation to the middle. If full preload finishes when animation at frame 9, the rest is 6 frames. But if we play 6 remaining frames, end position will be at 15/150, which is far from the middle.
This could be fixed when we automatically reinstantiate the playTo(15) animation to the new matching frame (e.g. playTo(matchFrame(15))
)
So maybe it's better to stop the animation if _framesLeftToPlay is set and let developers start a new one (according to the updated quantity) in onFastPreloadFinished callback?
I agree, for now this is the most simple solution (could even be achieved in its current form, without having to stop the animation), but I think you ment in the onPreloadFinished
?
but I think you ment in the onPreloadFinished
Yes, I mean someting like this:
let currentTargetFrame;
let instance = new AnimateImages(element, {
images: imagesArray,
...
onFastPreloadFinished: (plugin) => {
startActions();
},
onPreloadFinished: (plugin) => {
if (currentTargetFrame) plugin.playTo(currentTargetFrame);
},
...
}
function startActions(){
if (condition1) { // mb some scroll events
currentTargetFrame = 15;
instance.playTo(15);
}
if (condition2) {
currentTargetFrame = 45;
instance.playTo(45);
}
}
This could be fixed when we automatically reinstantiate the playTo(15) animation to the new matching frame (e.g. playTo(matchFrame(15)) )
I can't because playTo
is being converted to playFrames
, and 'playFrames' is being converted to _framesLeftToPlay
and direction. Target frame is not calculated internally and matchFrame
formula cannot be applied to number of frames. That's why I think it's better to call playTo
in load callback like in example above.
Now I see the login like this:
animation is not active => setFrame to the result of matchFrame or 1
animation is active, _framesLeftToPlay is undefined => setFrame to the result of matchFrame or 1, then play
animation is active, _framesLeftToPlay is number => stop animation, setFrame to the result of matchFrame or 1
If the callback version is not suitable, then I can probably add a new option fastPreview.continueAfter
that changes the logic to this:
animation is not active => setFrame to the result of matchFrame or 1
animation is active, _framesLeftToPlay is undefined, continue is true => setFrame to the result of matchFrame or 1, then play
animation is active, _framesLeftToPlay is undefined, continue is false => setFrame to the result of matchFrame or 1, then stop
animation is active, _framesLeftToPlay is number, continue is false => stop animation, setFrame to the result of matchFrame or 1
animation is active, _framesLeftToPlay is number, continue is true => directly change current frame to the result of matchFrame or 1, without stopping the animation
While the continueAfter
flow would be nice, for me the callback version is suitable and I am not sure how often people come across this problem, so I think we can close this issue for now, thanks for all your comments!