useTime

Creates a motion value that updates every frame with the time since it was created.

useTime returns a motion value that updates once per frame with the time, in milliseconds, since it started.

It's especially useful for perpetual animations.

<script lang="ts">
	import { motion, useTime, useTransform } from "motion-sv";

	const time = useTime();
	const rotate = useTransform(time, [0, 4000], [0, 360], { clamp: false });
</script>

<motion.div style={{ rotate }} />

Here, two more layers of boxes spin at 1.5 and 2 times that speed, by multiplying rotate in useTransform functions:

<script lang="ts">
	import { motion, useTime, useTransform } from "motion-sv";

	const time = useTime();
	const rotate = useTransform(
		time,
		[0, 4000], // time in milliseconds
		[0, 360], // rotation in degrees
		{ clamp: false }
	);
	const tinyRotate = useTransform(() => rotate.get() * 2); // 2x speed
	const smallRotate = useTransform(() => rotate.get() * 1.5); // 1.5x speed
</script>

<div class="example">
	<div class="layer" style:filter="blur(4px)">
		<div class="box-container tiny">
			{#each { length: 16 }, i (i)}
				<motion.div class="tiny-box" style={{ rotate: tinyRotate }} />
			{/each}
		</div>
	</div>
	<div class="layer" style:filter="blur(2px)">
		<div class="box-container small">
			{#each { length: 4 }, i (i)}
				<motion.div class="small-box" style={{ rotate: smallRotate }} />
			{/each}
		</div>
	</div>
	<div class="layer">
		<div class="box-container">
			<motion.div class="box" style={{ rotate }} />
		</div>
	</div>
</div>

<style>
	.example {
		position: relative;
		width: 100%;
		height: 400px;
	}

	.layer {
		position: absolute;
		inset: 0;
		display: flex;
		justify-content: center;
		align-items: center;
		gap: 20px;
	}

	.box-container {
		display: flex;
		flex-wrap: wrap;
		justify-content: center;
		align-items: center;
		gap: 50px;
	}

	.box-container.tiny {
		width: 500px;
		gap: 80px;
	}

	.box-container.small {
		width: 300px;
	}

	.example :global(.tiny-box) {
		width: 40px;
		height: 40px;
		border-radius: 5px;
		background-color: #9911ff;
	}

	.example :global(.small-box) {
		width: 80px;
		height: 80px;
		border-radius: 5px;
		background-color: #dd00ee;
	}

	.example :global(.box) {
		width: 100px;
		height: 100px;
		border-radius: 5px;
		background-color: #ff0088;
	}
</style>

Usage

Import useTime from motion-sv:

import { useTime } from "motion-sv";

useTime creates a new motion value. Once the component has mounted, it updates every frame with the time elapsed since its first frame.

Like other hooks, call useTime during component setup, at the top level of the <script>. It stops updating when the component unmounts.

You can use the value directly, or compose it with other motion value hooks:

const time = useTime();
const rotate = useTransform(
	time,
	[0, 4000], // For every 4 seconds...
	[0, 360], // ...rotate 360deg
	{ clamp: false }
);

useTransform with a function is handy for maths-based motion, like a sine wave. Here, each dot follows the same wave a little behind the one before it:

const time = useTime();

const offsets = Array.from({ length: 7 }, (_, i) =>
	useTransform(() => Math.sin(time.get() / 300 - i * 0.6) * 40)
);

Calling hooks in a loop like this is fine in Svelte, because a component's setup only runs once.

<script lang="ts">
	import { motion, useTime, useTransform } from "motion-sv";

	const time = useTime();

	// Each dot follows the same sine wave, a little behind the one before it
	const offsets = Array.from({ length: 7 }, (_, i) =>
		useTransform(() => Math.sin(time.get() / 300 - i * 0.6) * 40)
	);
</script>

<div class="row">
	{#each offsets as y, i (i)}
		<motion.div class="dot" style={{ y }} />
	{/each}
</div>

<style>
	.row {
		display: flex;
		gap: 16px;
	}

	.row :global(.dot) {
		width: 24px;
		height: 24px;
		border-radius: 50%;
		background: #0cdcf7;
	}
</style>