At some point of time, most of us have used GSAP, Javascript IntersectionObserver
or jQuery(c'mon guys, its 2021... using jQuery instead of javascript is like using IE for web development), in the foreseeable future, this can be done using CSS!
No, it's not a typo and I'm not insane. It's possible using the (drumroll please...) @scroll-timeline
. However, everything is not fine and dandy, this is still in draft and is supported only by Chrome 89(after turning on an experimental flag).
The Scroll-linked Animations is a new addition to CSS that defines a way for creating animations that are linked to a scroll offset of a scroll container. The specification is still in draft, and in no way finalised nor official, it already has experimental support in Chromium.
In sometime, maybe in a year or so, all major browsers will support it(who cares about Internet Explorer anyway? Even Microsoft ditched it).
Setup
I'll be using codepen to demonstrate the animations. Let's start by enabling the expiremental feature. Go to chrome://flags and enable "Experimental Web Platform features".
Add some basic HTML -
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=">
<title></title>
</head>
<body>
<p>scroll down</p>
<div class="container">
<div class="inner">
<h1>Scroll based animations in CSS is so coooool</h1>
<img src="https://images.unsplash.com/photo-1542831371-29b0f74f9713?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80" alt="placeholder image" class="image" id="image">
</div>
</div>
<section></section>
Nothing too complicated here.... just a basic outline for the body. Inside that, I have a p
and 2 div
s with a h1
and img
. There's a section with the height of 100vh
below to give some scrolling space.
I'll use Sass for this tutorial but this can be done in CSS too. I'll add a link for the CSS version at the end of the post. I'm going to add some quick styling that is not at all related to scroll based animations.
// Unimportant and boring styling
body {
background-color: #ffd9d1;
}
section {
height: 100vh;
}
p {
font-size: 1.5em;
display: grid;
text-transform: uppercase;
letter-spacing: 1em;
place-items: center;
margin-top: 100px;
}
.inner {
width: 60%;
}
.container {
text-align: center;
min-height: 80vh;
display: grid;
place-items: center;
h1 {
font-size: 3em;
font-family: Poppins;
}
.image {
height: 400px;
background-size: cover;
margin-top: 3em;
}
}
I've just added some position, dimensions, fonts.... you get the idea. Not at all related to this demo. Let's get to the crux of the code. We'll create a very simple pulse
keyframe animation that works with the scale of the image.
@keyframes demo {
0% {
border: 20px solid yellow;
}
25% {
border: 20px solid green;
}
50% {
border: 20px solid blue;
}
100% {
border: 20px solid red;
}
}
Nothing too complicated here.... just changing the border at various stages of the animation. I've chosen to go with borders because every time you change the position of the scrollbar the color changes and it's helpful to debug(I know its not good to animate border but I don't want to complicate things). Next, we have to create a timeline.
@scroll-timeline timeline {
time-range: 1s;
}
We use the @scroll-timeline
rule to make a timeline. I've called it timeline
. Inside this, I've specified a time-range
i.e. the amount of time I want to run the animation for. As we have defined our animation-duration
to be 1s, we want our time-range
to reflect that same duration. So, Scrolling from top to bottom (e.g. from 0% to 100%) should advance the animation by 1s.
The time-range
property is not the time of a clock. It is a mapping. This property is one of the most important properties in a @scroll-timeline
rule. Let's make sure you understand this well by looking at some examples.
animation-duration
&time-range
is1 second
As I said,time-range
is a mapping. So, 0% of our keyframe maps to 0 seconds and 100% maps to 1 second. In this case, the animation will go from start to finish as the user scrolls from top to bottomanimation-duration
is1s
&time-range
is2s
This time around, 0% of our keyframe maps to 0 seconds and 100% maps to 2 second.
So, the animation will go twice as fast.
Properties of a Timeline
A scroll timeline has some more properties -
source
orientation
scroll-offsets
We'll look at these as we go through the tutorial.
Linking the Bits and Pieces
We need to link our scroll-timeline
, keyframe animation
with our object. Add the following properties to the object we want to animate.
#image {
animation: 1s linear forwards demo;
animation-timeline: pulse-timeline;
}
The first line is nothing new, just setting an animation for the object for it to repeat once for 1
second. The second line is where it gets interesting. I've specified an animation-timeline
that links the timeline we made earlier.
From this, we can deduce that the three components for scroll based animations are:
- An animation(eg keyframe)
- A scroll timeline(eg pulse-timeline we just made)
- The link(Linking animation with timeline)
Scroll Offsets
If you run the code, the animation continues even after the object is out of the viewport. This is where the scroll-offsets
property comes into the picture. The scroll-property
takes 2 values i.e. the offset for the starting point and the offset for the end.
@scroll-timeline pulse-timeline {
time-range: 1s;
scroll-offsets: 0%, 100vh;
}
I haven't fiddled with the starting value as that works for us however I set the second value to 100vh
which means that it ends at 100vh
. If you notice, the animation speed has also slowed down that shows the mapping ratio has decreased.
Scroll Orientation
By default a scroll-timeline
will scroll from top to bottom i.e. vertical orientation. If we specify an orientation of horizontal, the animation will work during horizontal scrolling.
Source
The source is scrollable element or container(default is body) whose scrolling triggers the activation of the animation and changes the timeline.
The full demo
Conclusion
Normally for scroll linked animations I'd use GSAP/Intersection Observer along with ScrollMagic. Some parts of GSAP are not free and I feel that scroll magic has big learning curve(or the documentation isn't great). These also increase bundle size.
It's great to see scroll based animations in CSS and I'm pretty certain this is going to be the way to go(once there's better support in browsers).
That's it for now, hope you liked this post. If you did please like this post and follow me. Bye 👋