useMotionValueEvent
Subscribes to a motion value's events for the lifetime of a component.
useMotionValueEvent adds an event listener to a motion value for the lifetime of a component.
<script lang="ts">
import { motion, useMotionValue, useMotionValueEvent } from "motion-sv";
const x = useMotionValue(0);
useMotionValueEvent(x, "animationStart", () => {
console.log("animation started on x");
});
useMotionValueEvent(x, "change", (latest) => {
console.log("x changed to", latest);
});
</script>
<motion.div style={{ x }} /> When the component unmounts, its listeners are removed.
Usage
Import useMotionValueEvent from motion-sv:
import { useMotionValueEvent } from "motion-sv"; To listen to a motion value's event, pass the motion value, the event's name and a callback:
const color = useMotionValue("#00f");
useMotionValueEvent(color, "change", (latest) => {
console.log(latest);
}); The available events are:
"change""animationStart""animationComplete""animationCancel"
"change" callbacks receive the motion value's latest state. "animationCancel" fires when an animation is stopped before it completes, for instance because another animation on the same value has started.
<script lang="ts">
import { animate, motion, useMotionValue, useMotionValueEvent } from "motion-sv";
const x = useMotionValue(0);
let latest = $state(0);
let log = $state<{ id: number; name: string }[]>([]);
let nextId = 0;
const record = (name: string) => (log = [{ id: nextId++, name }, ...log].slice(0, 4));
useMotionValueEvent(x, "change", (value) => (latest = Math.round(value)));
useMotionValueEvent(x, "animationStart", () => record("animationStart"));
useMotionValueEvent(x, "animationComplete", () => record("animationComplete"));
useMotionValueEvent(x, "animationCancel", () => record("animationCancel"));
const toggle = () => animate(x, x.get() < 100 ? 200 : 0, { duration: 1 });
</script>
<div class="stack">
<div class="track">
<motion.div class="box" style={{ x }} />
</div>
<button onclick={toggle}>Animate</button>
<output>change: {latest}</output>
<ol>
{#each log as entry (entry.id)}
<li>{entry.name}</li>
{/each}
</ol>
</div>
<style>
.stack {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
width: 260px;
}
.track {
width: 100%;
padding: 6px;
border-radius: 14px;
background: rgb(255 62 0 / 0.15);
}
.track :global(.box) {
width: 48px;
height: 48px;
border-radius: 10px;
background: #ff3e00;
}
button {
padding: 8px 20px;
border: none;
border-radius: 8px;
background: #ff3e00;
color: white;
font: inherit;
cursor: pointer;
}
output,
ol {
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 14px;
}
ol {
width: 100%;
height: 6em;
margin: 0;
padding: 0;
list-style: none;
text-align: center;
}
li:not(:first-child) {
opacity: 0.5;
}
</style> Like other hooks, call useMotionValueEvent during component setup, at the top level of the <script>. It starts listening once the component has mounted.
Advanced
useMotionValueEvent is a helper for a motion value's on method. With on, you can start listening whenever you like, for instance in an event handler. You're then responsible for unsubscribing, at the latest when the component unmounts.
on returns a function that unsubscribes the listener, so in an $effect, you can return it, or a function that calls several of them:
<script lang="ts">
$effect(() => {
const doSomething = () => {};
const unsubscribeX = x.on("change", doSomething);
const unsubscribeY = y.on("change", doSomething);
return () => {
unsubscribeX();
unsubscribeY();
};
});
</script>