ReactFrontendAnimation

On-Scroll Ripple Effect with React and SCSS

Implemented a dynamic on-scroll ripple animation for a client, creating an interactive visual experience.

On-Scroll Ripple Effect with React and SCSS

Overview

The client required a ripple effect animation that would dynamically respond to scrolling events on their website. The traditional hover-based ripple animations were insufficient, as the client wanted a visual storytelling experience where the animation scales, inverts colors, and changes dimensions based on the scroll progress.


Challenge

  • Create a scroll-driven ripple animation instead of hover-based effects.
  • Ensure the animation scales and transforms smoothly without performance lags.
  • Make the effect responsive across devices and viewport sizes.
  • Enable color inversion at specific scroll thresholds for added visual impact.
  • Integrate seamlessly into existing React and SCSS codebase without blocking page performance.

Solution

I implemented a React component with SCSS styling that leveraged:

  • requestAnimationFrame for efficient scroll event handling.
  • CSS variables (--milo-scale, --milo-border, --milo-border-radius, etc.) for dynamic style changes.
  • Nested SCSS pseudo-elements to create the ripple layers.
  • Conditional logic in the scroll handler to change scale, border width, and color inversion based on scroll progress.

The solution ensures smooth, performant animations even on large scroll distances, while maintaining responsive sizing for different screen dimensions.


Implementation Highlights

useEffect(() => {
  let ticking = false;
 
  const handleScroll = () => {
    if (!ticking) {
      window.requestAnimationFrame(() => {
        const scrollTop = window.scrollY;
        const docHeight = document.body.scrollHeight - window.innerHeight;
        const scrollProgress = scrollTop / docHeight;
 
        let scale = Math.max(0.1, Math.min(1.5, scrollProgress * 2));
        let borderWidth = scrollProgress < 0.5 ? 70 - scrollProgress * 120 : 10 + (scrollProgress - 0.5) * 120;
 
        document.documentElement.style.setProperty('--milo-scale', scale.toString());
        document.documentElement.style.setProperty('--milo-border', `${borderWidth}px`);
        document.documentElement.style.setProperty('--milo-border-radius', scrollProgress > 0.1 ? '50%' : '0%');
 
        const center = document.querySelector(".miloEffect");
        if (scrollProgress >= 0.6) center?.classList.add("inverted");
        else center?.classList.remove("inverted");
 
        ticking = false;
      });
 
      ticking = true;
    }
  };
 
  window.addEventListener("scroll", handleScroll);
  return () => window.removeEventListener("scroll", handleScroll);
}, []);

Outcome

  • Achieved interactive scroll-driven ripple animations without performance degradation.
  • Scales smoothly from small to full viewport size based on scroll progress.
  • Supports color inversion to enhance visual storytelling.
  • Fully responsive across screen sizes.
  • Delivered a highly engaging, modern UX that met client expectations.

Conclusion

The on-scroll ripple effect transformed a simple content section into a visually interactive experience. By combining React hooks, requestAnimationFrame, and SCSS pseudo-elements, the animation remains performant and responsive. This solution demonstrates the power of creative frontend engineering to deliver dynamic, user-focused visual experiences that go beyond traditional static layouts.