Scroll Timeline
This is an exciting new thing that's currently only available in Chrome and Edge. It allows you to scroll elements on the page as you scroll the page. Previously, the only way to do it was to use an Intersection Observer. But this doesn't use JS at all.
Some set up
I'm starting with a box in the middle of the page (vertically) and on the left. I've made the body twice the height of the browser window to make sure you can scroll and therefore see the effect.
<div class="box"></div>
html {
overflow-x: clip;
}
body {
height: 200vh;
position: relative;
display: flex;
align-items: center;
}
.box {
width: 200px;
height: 200px;
background-color: pink;
position: absolute;
left: 0;
}
I'm going to move the box from the left of the screen to the right, hence the need to stop the horizontal scrollbar from showing.
Scroll-timeline
Most of the set up for the animation is a standard animation:
.box {
animation: slide-right linear forwards;
}
@keyframes slide-right {
to {
left: 100%;
}
}
So we're moving the box from left: 0
to left: 100%
. So far so normal. But the animation is missing the time. That's because we want it to move as we scroll, which could take any amount of time. There are only two things to add to get it to work.
.box {
animation-timeline: view();
animation-range: entry exit;
}
Both of these together are our timing function.
animation-timeline: view()
means that as soon as the element is visible it's in view and as soon as it's not visible it's not in view.
animation-range: entry exit
means that the animation should start once the element enters the viewport and should stop when it leaves.
What this all means is that as you scroll the page the box moves across the page - and also appears to move up since there is nothing else on the page to compare its position to.
Final code
Here is the whole code in CodePen:
Other considerations
If you open this CodePen in Firefox right now, you'll see the box is over to the right. This is because it's run the animation - since we didn't specify a time it took no time at all.
The solution to this is to wrap the animation in a supports query - this means that if the browser doesn't support it, the box will just stay where it started as you scroll.
.box {
width: 200px;
height: 200px;
background-color: pink;
position: absolute;
left: 0;
}
@supports (animation-timeline: view()) {
.box {
animation: slide-right linear forwards;
animation-timeline: view();
animation-range: entry exit;
}
}
You also need to consider people for whom animation is a problem. This is easy, because you can wrap the whole animation in a prefers-reduced motion media query:
@media (prefers-reduced-motion: no-preference) {
@supports (animation-timeline: view()) {
.box {
animation: slide-right linear forwards;
animation-timeline: view();
animation-range: entry exit;
}
}
}
And just like that, the animation will only run if the browser supports it and the user hasn't turned on reduced motion settings.