At the end of https://beta.observablehq.com/@forresto/labyrinth there are some animations that are done by the time the reader gets to bottom of page. Is there a way to trigger (re)setting the seed to start the animation when the person scrolls there (for the first time)?
Certainly â at least in nice browsers (not Safari, yet)! See my pauser notebook, which uses IntersectionObserver
to pause cells until a user can see them in their viewport.
pauser
works great in that notebook (scroll to last two sections). I have an initial board state to show until the el is in the viewport.
Plus, I learned some more about this generator stuff, which is all new to me. Thanks!!!
One caveat I noticed with IntersectionObserver
: if the element is wider than the cell view it wonât start.
@forresto That is the case if youâre using threshold = 1. If you use threshold = 0 it should work, albeit starting as soon as any part of the element is visible. Alternatively, you can set the intersection observer to observe the cellâs parent node:
{
const element = md`Hello, world!`;
yield element;
fadeIn(element.parentNode);
}
You must yield the element first so that itâs added to the DOM, but then you can register the intersection observer to fade-in when itâs visible.
https://beta.observablehq.com/@mbostock/intersection-observer
Thanks to Mike and Tom for making these small tools. Both pauser and fadeIn work great!
I wonder if thatâs something you could consider making the default for all the cells in a notebook (if the user chooses so), especially when they are writing a long computational essay and donât want to drain the readerâs browser.
Yes, Iâve had this thoughtâit should be possible to defer evaluation of a cell that isnât visible (and isnât referenced by another cell that is visible). In addition to making the page lighter by automatically pausing animations, it would allow animations to start automatically when they first become visible, improving readability.
But cells can also have side-effects that arenât tracked by the runtime, such as reaching into another cellâs value and mutating it. So probably weâll want to make pausing opt-in.
Intersection Observer should now (or soon) work in Safari,
https://webkit.org/blog/8718/new-webkit-features-in-safari-12-1/
There is also this polyfill, which seems to work for the bare basics at least (I have not tested it at all thoroughly)
The code I have been using is like:
Down at the bottom of the notebook:
IntersectionObserver = { //polyfill
await require('https://bundle.run/intersection-observer');
return window.IntersectionObserver; }
Then an animate function
animate = function(element, invalidation, drawframe) {
let frame, tick, newframe, cleanup, intersect;
newframe = function newframe() { frame = requestAnimationFrame(tick); };
cleanup = function cleanup() { cancelAnimationFrame(frame); };
tick = function tick() { drawframe(); newframe(); }
intersect = function intersect(entries) {
for (let e of entries) e.isIntersecting ? newframe() : cleanup(); }
new IntersectionObserver(intersect).observe(element);
newframe();
invalidation.then(cleanup);
return element;
}
And then finally in my cell,
{
element = whatever
... set up drawing ...
return animate(element, invalidation, () => {
... draw the frame ...
});
}
If anyone runs into any issues with this in any browsers on e.g.
Iâll try to fix.
Edit: I put both loading the polyfill (only if IntersectionObserver is unavailable) and the definition of the animate function into @jrus/misc#animate.