Parallax Scrolling in React

Loading

Implementing Parallax Scrolling in React Applications

Parallax scrolling creates a visually engaging effect where background elements move slower than foreground elements as users scroll. Here’s how to implement high-performance parallax effects in React.

1. Basic Parallax Implementation

Using React Spring (Recommended)

import { useScroll } from '@react-spring/web';

function ParallaxComponent() {
  const { scrollYProgress } = useScroll();

  return (
    <div className="container">
      <animated.div
        style={{
          transform: scrollYProgress.to(v => `translateY(${v * 100}px)`),
        }}
        className="parallax-background"
      />
      <div className="content">
        {/* Normal scrolling content */}
      </div>
    </div>
  );
}

CSS-Only Fallback

function SimpleParallax() {
  return (
    <div className="parallax-container">
      <div className="parallax-background" />
      <div className="parallax-content">
        {/* Your content here */}
      </div>
    </div>
  );
}

/* CSS */
.parallax-container {
  position: relative;
  height: 100vh;
  overflow-x: hidden;
  overflow-y: auto;
}

.parallax-background {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 200%;
  background-image: url('your-image.jpg');
  background-attachment: fixed;
  background-position: center;
  background-size: cover;
  z-index: -1;
}

2. Layered Parallax Effect

Multiple Parallax Layers

import { useScroll, animated } from '@react-spring/web';

function LayeredParallax() {
  const { scrollYProgress } = useScroll();

  return (
    <div className="scene">
      <animated.div
        className="layer layer-1"
        style={{
          transform: scrollYProgress.to(v => `translateY(${v * 30}px)`),
        }}
      />
      <animated.div
        className="layer layer-2"
        style={{
          transform: scrollYProgress.to(v => `translateY(${v * 60}px)`),
        }}
      />
      <animated.div
        className="layer layer-3"
        style={{
          transform: scrollYProgress.to(v => `translateY(${v * 90}px)`),
        }}
      />
      <div className="content">
        {/* Page content */}
      </div>
    </div>
  );
}

/* CSS */
.scene {
  position: relative;
  height: 500vh; /* Extra space for scrolling */
}

.layer {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  will-change: transform;
}

.layer-1 {
  background: url('far-background.jpg') center/cover;
}

.layer-2 {
  background: url('mid-background.png') center/cover;
}

.layer-3 {
  background: url('foreground.png') center/cover;
}

.content {
  position: relative;
  min-height: 100vh;
}

3. Performance-Optimized Parallax

Using Intersection Observer

import { useEffect, useRef, useState } from 'react';

function OptimizedParallax() {
  const [scrollY, setScrollY] = useState(0);
  const parallaxRef = useRef(null);

  useEffect(() => {
    const handleScroll = () => {
      if (parallaxRef.current) {
        const rect = parallaxRef.current.getBoundingClientRect();
        const offset = window.pageYOffset - rect.top;
        setScrollY(offset);
      }
    };

    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            window.addEventListener('scroll', handleScroll, { passive: true });
          } else {
            window.removeEventListener('scroll', handleScroll);
          }
        });
      },
      { threshold: 0.1 }
    );

    if (parallaxRef.current) {
      observer.observe(parallaxRef.current);
    }

    return () => {
      window.removeEventListener('scroll', handleScroll);
      if (parallaxRef.current) {
        observer.unobserve(parallaxRef.current);
      }
    };
  }, []);

  return (
    <div ref={parallaxRef} className="parallax-container">
      <div 
        className="parallax-element"
        style={{
          transform: `translateY(${scrollY * 0.5}px)`,
        }}
      />
    </div>
  );
}

4. Horizontal Parallax Scrolling

Horizontal Motion Effect

import { useScroll, animated } from '@react-spring/web';

function HorizontalParallax() {
  const { scrollYProgress } = useScroll();

  return (
    <div className="horizontal-container">
      <animated.div
        className="moving-element"
        style={{
          transform: scrollYProgress.to(v => `translateX(${v * 200}px)`),
        }}
      />
      <div className="long-content">
        {/* Content that makes the page scrollable */}
      </div>
    </div>
  );
}

5. Parallax with React Parallax Library

Using react-parallax

import { Parallax, ParallaxLayer } from '@react-spring/parallax';

function FullPageParallax() {
  return (
    <Parallax pages={3}>
      <ParallaxLayer offset={0} speed={0.5}>
        <h1>First Page</h1>
      </ParallaxLayer>

      <ParallaxLayer offset={1} speed={0.2}>
        <div className="background" />
      </ParallaxLayer>

      <ParallaxLayer offset={1} speed={0.5}>
        <h2>Second Page</h2>
      </ParallaxLayer>

      <ParallaxLayer offset={2} speed={0.8}>
        <h2>Third Page</h2>
      </ParallaxLayer>
    </Parallax>
  );
}

6. 3D Parallax Effect

Using perspective transforms

function Parallax3D() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  const handleMouseMove = (e) => {
    const x = (e.clientX - window.innerWidth / 2) / 20;
    const y = (e.clientY - window.innerHeight / 2) / 20;
    setPosition({ x, y });
  };

  return (
    <div 
      className="parallax-3d-container" 
      onMouseMove={handleMouseMove}
    >
      <div
        className="parallax-3d-element"
        style={{
          transform: `rotateX(${position.y}deg) rotateY(${position.x}deg)`,
          transition: 'transform 0.1s ease-out',
        }}
      />
    </div>
  );
}

/* CSS */
.parallax-3d-container {
  perspective: 1000px;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.parallax-3d-element {
  width: 300px;
  height: 300px;
  background: linear-gradient(45deg, #ff3366, #ba265d);
  transform-style: preserve-3d;
  will-change: transform;
}

7. Mobile-Friendly Parallax

Device Orientation Detection

function AdaptiveParallax() {
  const [isMobile, setIsMobile] = useState(false);
  const { scrollYProgress } = useScroll();

  useEffect(() => {
    const checkMobile = () => {
      setIsMobile(window.innerWidth < 768 || 
        /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
    };

    checkMobile();
    window.addEventListener('resize', checkMobile);
    return () => window.removeEventListener('resize', checkMobile);
  }, []);

  return (
    <div className="parallax-container">
      {!isMobile ? (
        <animated.div
          className="parallax-element"
          style={{
            transform: scrollYProgress.to(v => `translateY(${v * 100}px)`),
          }}
        />
      ) : (
        <div className="static-element" />
      )}
      <div className="content">
        {/* Your content here */}
      </div>
    </div>
  );
}

Best Practices for Parallax in React

  1. Performance Optimization:
  • Use will-change: transform in CSS
  • Debounce scroll events
  • Implement intersection observers to activate effects only when visible
  1. Accessibility Considerations:
  • Provide toggle to disable motion for users with vestibular disorders
  • Ensure content remains readable during animations
  • Maintain proper contrast ratios
  1. Mobile Considerations:
  • Disable or simplify effects on mobile devices
  • Use lighter assets for mobile
  • Test performance on low-end devices
  1. Fallbacks:
  • Provide static layouts when JavaScript is disabled
  • Use CSS-only parallax as a progressive enhancement
  • Gracefully degrade on unsupported browsers
  1. Asset Optimization:
  • Compress images and videos
  • Use modern formats like WebP
  • Implement lazy loading for offscreen assets

By implementing these parallax techniques in React, you can create immersive scrolling experiences while maintaining performance and accessibility. Choose the approach that best fits your project’s requirements and always test across different devices and browsers.

Leave a Reply

Your email address will not be published. Required fields are marked *