Files
oki-foundation/src/lib/components/motion/ScrollProgressBar.svelte
T

65 lines
1.4 KiB
Svelte
Raw Normal View History

<script lang="ts">
import { onMount } from 'svelte';
let bar: HTMLDivElement;
let progress = $state(0);
onMount(() => {
let ticking = false;
function update() {
ticking = false;
const scrollable = document.documentElement.scrollHeight - window.innerHeight;
progress = scrollable > 0 ? Math.min(window.scrollY / scrollable, 1) : 0;
// Écriture DOM directe : jamais d'état réactif par frame (playbook §3)
bar.style.transform = `scaleX(${progress})`;
}
function onScroll() {
if (!ticking) {
ticking = true;
requestAnimationFrame(update);
}
}
update();
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll, { passive: true });
return () => {
window.removeEventListener('scroll', onScroll);
window.removeEventListener('resize', onScroll);
};
});
</script>
<div
class="scroll-progress"
role="progressbar"
aria-label="Progression de lecture"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow={Math.round(progress * 100)}
bind:this={bar}
></div>
<style>
.scroll-progress {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
z-index: 1001;
background: linear-gradient(to right, var(--or-oki), var(--vert-oki));
transform: scaleX(0);
transform-origin: left;
pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
.scroll-progress {
display: none;
}
}
</style>