Closed ivodolenc closed 2 months ago
Can someone please advise how to stop
the timeline (Scroll
or View
Timeline) after we start it?
const timeline = new ScrollTimeline() // or new ViewTimeline()
// how to stop it manually?
timeline.stop()
I can't find it anywhere in the docs or in the css draft documentation.
Any suggestion is welcome, maybe I'm missed something.
IIUC by “stop the the timeline” you mean freeze the animation at its current point.
You can take inspiration from https://www.bram.us/2023/10/05/run-a-scroll-driven-animation-only-once/ here. At its core, these two lines matter:
animation.commitStyles();
animation.cancel();
If you want to remove the timeline without freezing the frames, you can simply set the animation’s timeline to null
.
I’m going to close this issue as your problem is more of a general question unrelated to the polyfill. The aim of this polyfill is to support what the native implementation supports. Your issue is related to the Web Animations API in general, not the polyfill.
@bramus Thanks for the reply and suggestion, but I mean to use ScrollTimeline
without Web Animation API, i.e. without using element.animate()
, so I can't use animation API:
animation.commitStyles();
animation.cancel();
Maybe I asked the wrong question, so I'll rephrase it.
Is it necessary to stop the timeline
after it is started, with this polyfill or native implementation in Chrome?
My use case is, I want to start a timeline and then observe the progress of the timeline as it scrolls.
I don't need to use any extra animation API, all I need is timeline.currentTime
to convert it into progress and then use that progress for other stuff. Also, this all works great, but I'm worried that if I create a timeline multiple times, without manually stopping it, it will run infinite in the background and maybe negatively impacts on site performance.
I hope this makes more sense now.
Is it necessary to stop the timeline after it is started, with this polyfill or native implementation in Chrome?
Animations automatically get cleaned up when the nodes get trashed. If you have an instance of ScrollTimeline
lingering around after you have trashed all the nodes, you can set it null
to actually destroy it.
let st = new ScrollTimeline(…);
// … use it as the `timeline` for some animations
// … remove all animations
st= null;
My use case is, I want to start a timeline and then observe the progress of the timeline as it scrolls. I don't need to use any extra animation API, all I need is timeline.currentTime to convert it into progress and then use that progress for other stuff.
Aha, got it what you want to do now.
To do this today, you need to attach your timeline to an animation and then read the progress from that animation. There is active discussion about progress
events on animations, but that’s still in the works. For now, you’ll have to read the effect’s progress in requestAnimationFrame
and use that. I have done this in https://www.bram.us/2023/06/21/synchronize-videos-3d-models-to-scroll-driven-animations/ and have extracted away the functionality into a package named @bramus/sda-utilities
import { trackProgress } from '@bramus/sda-utilities';
// Update text of the `.animation-subject` element with the effect progress
trackProgress(document.querySelector('.animation-subject').getAnimations()[0], (progress) => {
document.querySelector('.animation-subject').innerText = `${(progress * 100).toFixed(5)}%`;
});
I’ll make note of you requesting a way to get a timeline’s progress without needing to attach it to an animation first.
... you can set it null to actually destroy it.
I think this information 👆, if I understood correctly, is all I need. So after all, it's super simple to do (stop the timeline), if this 👇 is true?
// start the timeline normally via JS API
let st = new ScrollTimeline(…);
// ...
// observing the `st.currentTime` in `requestAnimationFrame` on each tick, and converting it to 0-1 progress (custom function)
// ...
// later when I don't need it anymore, or change route in SPAs etc...,
// I simply stop the `requestAnimationFrame` and set this `st` to null
// so that the timeline also finishes its processes
st = null;
Also, thanks again for the help and links, all this information is very useful and hard to find without tips. I will definitely be checking out this sda-utils package and using it as inspiration, it's something similar to what I had in mind.
Overall, I think all developers can be very happy when this polyfill and API lands and becomes a standard because it's definitely a game-changer and makes a lot of work easier.
Yes, that's correct. If there are no animations running on the timeline then removing the last reference will clean up all memory associated with it. Running animations implicitly keep the timeline alive until they are finished.
... then removing the last reference will clean up all memory associated with it ...
Ok, that's what I was wondering about, how to manually clear all memory associated with the timeline when needed.
Thanks for confirmation. 👍
Hi, just wanted to note that reassigning the timeline
to null
doesn't work for the polyfill as expected, the scroll and observe events still fire after that.
Also, for native Chrome timelines I can't test properly so please give me some feedback.
Here's a simple example of how I tested this in polyfill:
console.log
to scrollListener
function inside of ScrollTimeline
class in polyfill:This will tell us when the scroll event is triggered:
// `scrollListener` function is inside `ScrollTimeline` class
const scrollListener = () => {
// Sample and store scroll pos
details.sourceMeasurements.scrollLeft = source.scrollLeft
details.sourceMeasurements.scrollTop = source.scrollTop
console.log('scroll test') // 👈 add this line here
for (const ref of details.timelineRefs) {
const timeline = ref.deref()
if (timeline) {
updateInternal(timeline)
}
}
}
import { ScrollTimeline } from './polyfill'
let scrollTimeline = new ScrollTimeline()
// check the browser console, it works as expected as it outputs 'scroll test'...
setTimeout(() => {
scrollTimeline = null
}, 500)
// after 500 ms, I expect that reassigning the `timeline` to `null` would disconnect all scroll and observe events
// check browser console, still outputs 'scroll test'... infinite time
I have a local polyfill solution to solve this:
// add simple `cancel()` method to ST class:
class ScrollTimeline {
// ...
cancel() {
const details = sourceDetails.get(this.source)
details.disconnect()
}
// ...
}
// starts timeline
const st = new ScrollTimeline()
setTimeout(() => {
st.cancel() // 👈 disconnects all timeline events as expected
}, 500)
But now I'm still worried if this will really stop all events for Chrome's built-in timelines, since I can't test it like the examples above?
Hi, awesome work on polyfill! Hope this will land in all major browsers soon.
I was wondering how we can
cancel/disconnect
the scrolltimeline
after we don't need it anymore, for example when changing route etc.I want to avoid creating all timelines without properly disconnecting the previous ones. Also, I'm not using it with the animation API.
Here is example for built-in ScrollTimeline API in Chrome: