useRaf
useRaf(): Service<RafProps, void | RafRender>The frame clock as a service. It has nothing to scope, so there is one instance.
Props
interface RafProps {
readonly time: DOMHighResTimeStamp;
readonly delta: number;
}delta is clamped to [1, 40] ms, and the first tick after the loop wakes reports 1000/60. time stays the raw rAF timestamp.
Usage
import { Base, useRaf } from '@studiometa/js-toolkit';
class Ticker extends Base {
static config = { name: 'Ticker' };
mounted() {
return useRaf().subscribe(({ delta }) => {
// …
});
}
}A callback can return a render function
useRaf is the one service whose callback return value is used. Return a function and it runs in the frame's write phase:
import { Base, useRaf } from '@studiometa/js-toolkit';
class Follower extends Base {
static config = { name: 'Follower' };
mounted() {
return useRaf().subscribe(({ delta }) => {
const x = this.$el.offsetLeft + delta;
return () => {
this.$el.style.transform = `translateX(${x}px)`;
};
});
}
}The service collects the render functions of its callbacks and cancels a render whose subscriber left between the two phases.
It does not own a loop
It subscribes to scheduler.tick(). The scheduler requests the next frame while a tick subscriber stays, so there is no permanent rAF loop and no loop of the service's own.
{ immediate: true } does nothing here: the frame tick has no current value between two frames, which is what its hasProps() says.
Switching it on and off
import { Base, toggle, useRaf } from '@studiometa/js-toolkit';
class SliderItem extends Base {
static config = { name: 'SliderItem' };
#frame = toggle(() => useRaf().subscribe(({ delta }) => this.follow(delta)));
mounted() {
return this.#frame.stop;
}
follow(delta) {}
}See toggle(), and withRaf(Base, { manual: true }) in Mixins.
Mixin
class Follower extends withRaf(Base) {
ticked({ delta }) {}
}