Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What about computing the animation duration dinamically based on the time to load the previous page? #6

Open
zanhk opened this issue Apr 25, 2024 · 1 comment

Comments

@zanhk
Copy link

zanhk commented Apr 25, 2024

<div
	transition:persist
	id="astro-loading-indicator"
	class="astro-loading-indicator"
>
</div>

<style>
	.astro-loading-indicator {
		--progress: 0;
		--animation-duration: 300ms;
		--opacity-animation-duration: 150ms;
		pointer-events: none;
		background-color: hsl(192, 41%, 84%);
		position: fixed;
		z-index: 1031;
		top: 0;
		left: 0;
		width: 100%;
		height: 3px;
		transition:
			transform var(--animation-duration) ease-out,
			opacity var(--opacity-animation-duration)
				var(--opacity-animation-duration) ease-in;
		transform: translate3d(0, 0, 0) scaleX(var(--progress, 0));
		transform-origin: 0;
	}
</style>

<script>
	(() => {
		// This is the initial interval
		let animationDuration = 50;
		let progress = 0.25;
		let opacity = 0;
		let trickleInterval: number | undefined = undefined;

		let preparationTimestamp: number = 0;

		const element = document.getElementById("astro-loading-indicator");

		/** @param {typeof progress} _progress */
		const setProgress = (_progress: number) => {
			progress = _progress;
			element?.style.setProperty("--progress", String(progress));
		};

		/** @param {typeof opacity} _opacity */
		const setOpacity = (_opacity: number) => {
			opacity = _opacity;
			element?.style.setProperty("opacity", String(opacity));
		};

		const setAnimationDuration = (duration: number) => {
			animationDuration = duration;

			let styleAnimationDuration = Math.min(300, animationDuration);

			element?.style.setProperty(
				"--animation-duration",
				`${styleAnimationDuration}ms`,
			);
			element?.style.setProperty(
				"--opacity-animation-duration",
				`${styleAnimationDuration / 2}ms`,
			);

			return styleAnimationDuration;
		};

		setOpacity(opacity);

		document.addEventListener("astro:before-preparation", () => {
			preparationTimestamp = performance.now();
			setOpacity(1);
			trickleInterval = window.setInterval(() => {
				setProgress(progress + Math.random() * 0.05);
			}, animationDuration * 0.05);
		});

		document.addEventListener("astro:before-swap", (ev) => {
			let computedDuration = performance.now() - preparationTimestamp;
			var styleAnimationDuration = setAnimationDuration(computedDuration);

			window.clearInterval(trickleInterval);
			trickleInterval = undefined;

			setProgress(1);

			window.setTimeout(() => {
				setOpacity(0);
			}, styleAnimationDuration / 2);

			window.setTimeout(() => {
				setProgress(0.25);
			}, styleAnimationDuration * 1.5);
		});
	})();
</script>
@florian-lefebvre
Copy link
Owner

I can't remember exactly where it is but @martrapp had been doing experiments about getting the upcoming page loading time for better loaders, but it's not possible because of streaming or something. I'm not in favor of adjusting the loader based on the previous page because there can be many differences between 2 pages (eg. not as heavy, connection downgrade etc) but thanks for the snippet! still useful for anyone wanting it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants