Destructuring Scroll Animations :: Array of solutions
ughhh, I hate absolute positioning
Animations are cool,
While it may be difficult to implement, I think it is necessary skill to have in order to be good at frontend interactions.
Let's destructure the most common one: Scroll Animations
Desired Outcome
Now you must have seen this awesome, apple site, if not do check this out, Apple surely doesn't disappoint.
At this point it is not wrong to think that apple and good design are synonyms, but I want you guys to zoom out a bit.
Let's talk about outcomes,
As a frontend engineer, my job is to make your life easier by creating better experiences, Interaction of a user with the product is what matters the most. Maximizing on these metrics is one way of contributing towards shaping the habitat we now call as the Internet.
To have an end goal in mind is surely very useful while doing anything in web, and thus my adventure with web animations.
Approaches
There are multiple ways of animating web components, among which I tried a handful, namely:
- CSS Animations (transform property to be very specific)
- Intersection Observer( Browser API)
- Third party Libraries and Packages
- React Framer Motion (why not? abstractions are the way to go)
Let me walk through all these egg shells to give you a mental picture of how I achieved my desired outcome
CSS Animations
General Concept:
Converting the Hero section into a canvas with large enough height, so as to allow enough scrolling bandwidth for us to play around
Creating a Function that returns image file path, depending on the index Value passed
Tracking user scroll progress and multiplying it with the number of Images we have to generate frames
Updating the canvas with different images as the user scrolls based on the scroll position
It correctly tracks the scroll progress, which is something I needed in order to scale Images, but the fundamental idea behind this is very different from what I wanted, which was to scale images on the basis of scroll progress.
General Concept:
- Tracking the scroll position and converting it into an css custom property
const onScroll = () => {
document.body.style.setProperty(
"--scroll",
(
window.pageYOffset /
(document.body.offsetHeight - window.innerHeight)
).toString()
);
window.addEventListener("scroll", onScroll, false);
- Using CSS animation keyframe, to delay the motion of animation on the basis of scroll value.
I did use this solution to track the scroll position but parted ways with the rest of the logic, though it seemed pretty solid but this is the major issue I faced
- Scaling Images gradually, as I was just oscillating between the initial scale and final scale.
After dabbling around with CSS transform properties and animation keyframes, I stumbled upon Intersection Observer.
Intersection Observer
Intersection Observer is a browser api which helps in asynchronous observation of various sections, this is the most efficient solution for animations involving different sections but since the positioning of my Images were not that far from each other it was quite unhelpful in the core aspect.
Third party Libraries
Welcome to the npmverse of packages, a world where repetitive solutions are in abundance.
As you can see I had 132 other alternatives to try out for my particular use case but, there are some issues which comes with these go to solutions
Most of the packages are not maintained anymore which makes my application very fragile if I depend on them.
Most of the packages are intended for a certain set of use cases and I didn't had the bandwidth to try each one of them out.
I find it to be a good idea in general to have minimum dependencies in my applications, makes them more robust and less error prone.
Abstractions are great but, wasting too much time on too many abstractions is like picking a needle in from the haystack
React Framer Motion
React framer motion is the most popular abstraction in the react ecosystem for animations on web,
While framer motion has certain set of out of the box features for scroll based animations,
I used a tiny portion of it's capabilities to achieve what I wanted.
i.e., Images disappearing after a certain scroll height
<motion.div
animate={{ opacity: scrollValue > 36 ? 0 : 1 }}
>
<ImageComponent
scrollVal={scrollValue}
imgSrc="images.unsplash.com/photo-1535223289827-42…"
width={612}
height={612}
cName="fixed top-0 right-0 "
/>
</motion.div>
animate is a framer motion property, which helps us in controlling the opacity of the Image elements, which would have been a little lengthier with CSS
Remixing all the solutions
Now that I had a way to track scroll progress and make images disappear after a certain scroll height the only thing left was to position images and scale them according to the scroll value, which was a fairly simple task.
style={{
transform: `scale(${
scrollVal * 0.2 > 1 ? 1 : scrollVal * 0.2 + 0.02
})`,
}}
Conclusion
Oftentimes there are no clear solutions, the best solution in such cases is to create one of your own by remixing others.
The animation is still janky, but it's good to have a rough path.
This task was a part of my first internship at Hallparty, I was uncomfortable in the beginning but learned a lot of things ++ received a lot of help from the hallparty team especially Kush in reaching to the desired solution