Scroll Animations, Intersection observer and React:: Frontend Experiments.

Scroll Animations, Intersection observer and React:: Frontend Experiments.

ughhh, I hate absolute positioning

Animations are cool,

While it may be difficult to implement, It is an important skill to have in order to become good at frontend and user interaction(this is what I believe at least).

On performant web applications

Let's destructure the most common one: Scroll Animations


Desired Outcome

You must have seen this awesome, apple site, if not do check this out, Apple surely doesn't disappoint.

Apple Website

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 with 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 begins my very first adventure with web animations.


Approaches

There are multiple ways of animating web components, among which I tried a handful, namely:

  1. CSS Animations ( animation keyframes and transform property to be very specific)
  2. Intersection Observer( Browser API)
  3. Third party Libraries and Packages
  4. 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

First Solution

General Concept:

  1. Converting the Hero section into a canvas with large enough height, so as to allow enough scrolling bandwidth for us to play around

  2. Creating a Function that returns image file path, depending on the index Value passed

  3. Tracking user scroll progress and multiplying it with the number of Images we have to generate frames

  4. Updating the canvas with different images as the user scrolls depending upon the frame

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.

Second Solution

General Concept:

  1. 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);
  1. 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 seems to pretty solid but this is the major issue I faced

  1. 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 provides an asynchronous way of observing various sections, this is the most efficient solution for animations involving different pages 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

Screenshot (346).png

Welcome to the npmverse of packages, a world where repetitive solutions are abundant.

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, let me highlight those

  1. Most of the packages are not maintained anymore which makes my application very fragile if I depend on them.

  2. 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 fitting to my particular use case.

  3. I find it to be a good idea in general to have minimum dependencies in 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,

Scroll-linked 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="https://images.unsplash.com/photo-1535223289827-42f1e9919769?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80"
          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 together

Now that I had a way to track the scroll height and a way to make them disappear, the only task that was left was to position them evenly across the screen and scaling the image according to the scroll position, which was fairly simple.

  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 was a part of my first internship task, it was uncomfortable for me in the beginning but I learned a lot about animations, ++ the awesome folks at Hallparty especially Kush helped me a lot in reaching to the solution.