Как сделать индикатор прочтения страницы
Короткий ответ
Привяжите анимацию не ко времени, а к прокрутке страницы:
.progress {
position: fixed; top: 0; left: 0; width: 100%; height: 4px;
background: tomato; transform-origin: left;
animation: grow linear;
animation-timeline: scroll(root);
}
@keyframes grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }animation-timeline: scroll(root) заменяет секунды на положение прокрутки: в начале страницы анимация на 0%, в конце — на 100%. Полоса растёт от scaleX(0) до полной ширины. JavaScript не нужен.
Живой пример в блоке с прокруткой
Без аргумента scroll() берёт ближайший прокручиваемый родитель. Прокрутите текст:
<style>
.article { height: 160px; overflow: auto; font: 14px/1.6 system-ui, sans-serif; border: 1px solid #ddd; }
.bar { position: sticky; top: 0; height: 4px; background: tomato; transform-origin: left;
animation: grow linear; animation-timeline: scroll(); }
@keyframes grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.article p { margin: 12px; }
</style>
<div class="article">
<div class="bar"></div>
<p>Scroll down to fill the bar.</p>
<p>CSS can tie an animation to the scroll position instead of time.</p>
<p>The bar grows as you read and reaches full width at the end.</p>
<p>No JavaScript is involved: the browser does all the work.</p>
<p>The same trick works for the whole page with scroll(root).</p>
<p>You have reached the end.</p>
</div>position: sticky держит полосу вверху блока во время прокрутки.
Запасной вариант
Анимации по прокрутке есть ещё не во всех браузерах. Там, где их нет, свойство animation-timeline пропускается, анимация без длительности заканчивается мгновенно, и полоса застывает на всю ширину. Страница не ломается, но индикатор врёт. Поэтому оберните стили полосы в @supports (animation-timeline: scroll()) — тогда в старых браузерах её просто не будет. Если индикатор нужен везде, его можно двигать несколькими строками JavaScript по событию scroll.
Частая ошибка
Записывать animation-timeline перед animation. Сокращённое свойство animation сбрасывает animation-timeline в значение по умолчанию, и анимация снова идёт по времени, а не по прокрутке. animation-timeline всегда ставьте после animation.
Функции из этого ответа
- animation-timeline — Связывает CSS-анимацию с прокруткой вместо времени: анимация продвигается по мере прокрутки страницы или элемента.
- scroll() — Создают безымянные шкалы прокрутки для `animation-timeline`: `scroll()` следит за прокручиваемым контейнером, `view()` — за проходом самого элемента через видимую область.
- @keyframes — Описывает шаги анимации, которую затем запускают свойством `animation`.
- position — Как позиционируется элемент: в обычном потоке или по смещениям.
- @supports — Применяет правила, только если браузер поддерживает указанное свойство, значение или селектор.