Animation using styled components in ReactJs

I’m trying to create a testimonial section similar to this (www.runway.com) website’s testimonials. I am using styled-components to create it. So far I have managed to stack up the cards on top of each another at the middle of the screen and when it comes into view, the cards rise from the bottom of the screen to the middle with a linear animation. But I want a scroll feature here, (like the website mentioned above) where the cards would come from the bottom of the screen one-by-one when the user gradually scrolls down.

//

import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import ticket from "./assets/ticket0.webp";

const TestimonialSection = styled.section``;

const TestimonialDiv = styled.div`
height: 80vh;
margin: auto;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
min-height: 70%;
`;

const BottomCenter = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 100%;
@keyframes splash {
    from {
        transform: translate(0%, 100%);
    }
    to { 
        transform: rotate(var(--rx));
    }
}

img{
    width: 0%;
    position:absolute;
    transform:rotate(0deg);
}

.f-image-25{
    width:15%;
    --rx: -3deg;
    z-index:25;
    animation: splash 1s normal forwards ease-in-out;
}
.f-image-35{
    width:17%;
   --rx: -3deg;
    z-index:35;
    animation: splash 1s normal forwards ease-in-out;
}
.f-image-40{
    width:20%;
    --rx: 3deg;
    z-index:40;
    animation: splash 1s normal forwards ease-in-out;
}
.f-image-30{
    width:17%;
    --rx: -6deg;
    z-index:30;
    animation: splash 1s normal forwards ease-in-out;
}
.f-image-20{
    width:15%;
    --rx: 10deg;
    z-index:20;
    animation: splash 1s normal forwards ease-in-out;
}
`;

const Testimonial = () => {

const [isAnimated, setIsAnimated] = useState(false);
const options = {
    root: null,
    margin: '0px',
    threshold: 0.5
}

const observerCallback = (entries) => {
    const [mockImg] = entries

    if (!isAnimated) {
        setIsAnimated(true);
        if (mockImg.isIntersecting) {
            document.getElementById('f-image-40').classList.add('f- image-40')
            document.getElementById('f-image-35').classList.add('f-image-35')
            document.getElementById('f-image-30').classList.add('f-image-30')
            document.getElementById('f-image-25').classList.add('f-image-25')
            document.getElementById('f-image-20').classList.add('f-image-20')
        }
    }
}

useEffect(() => {
    const fImage = document.getElementById('f-image')
    const observer = new IntersectionObserver(observerCallback, options)
    observer.observe(fImage)
    return () => {
        observer.unobserve(fImage);
    }
}, [])

return (
    <TestimonialSection>

        <TestimonialDiv>

            <BottomCenter id='f-image'>
                <img alt="testimonial-card" id='f-image-25' src={ticket}></img>
                <img alt="testimonial-card" id='f-image-35' src={ticket}></img>
                <img alt="testimonial-card" id='f-image-40' src={ticket}></img>
                <img alt="testimonial-card" id='f-image-30' src={ticket}></img>
                <img alt="testimonial-card" id='f-image-20' src={ticket}></img>
            </BottomCenter>

        </TestimonialDiv>

    </TestimonialSection>
)
}

export default Testimonial

  • You’ll need to use react-intersection-observer. You’d use the useInView hook to figure out if the card(s) in view and then pass that as a prop to each card, conditionally invoking the fade animations

    – 




I would recommend using the Framer library for React.

You can use a combination of the useInView hook with the animate function. Framer sounds very suitable for your use case as it comes equipped with multiple hooks and functions to handle these scenarios.

https://www.framer.com/motion/animate-function/
https://www.framer.com/motion/use-in-view/

Leave a Comment