Home > Mobile >  Show h1/img elements when scrolled into viewport
Show h1/img elements when scrolled into viewport

Time:12-03

I have a site where the h1 tag and an image load in when I scroll to them. I have the css set to load an animation on the tags when they load, so I really don't want them to load before they are visible.

I have it working perfectly on desktop/laptop, but on mobile the elements are just loaded automatically with everything else, and the animations don't have a chance to work. The console logs that I call show that the window.scrollY is only returning "0".

import React, { useEffect, useState } from 'react';
import Headshot from '../../../assets/images/about/Headshot';

const About = () => {
    const [isVisible, setIsVisible] = useState(true);

    useEffect(() => {
        document.addEventListener("touchmove", listenToScroll);
        window.addEventListener("scroll", listenToScroll);
        return () => {
            document.addEventListener("touchmove", listenToScroll);
            window.removeEventListener("scroll", listenToScroll);
        }
    }, [])


    const listenToScroll = () => {
        const homeHeight = document.getElementById('Home').clientHeight;
        const folioHeight = document.getElementById('Portfolio').clientHeight;
        const skillsHeight = document.getElementById('Skills').clientHeight;
        let heightToShow;
        let vh = window.innerHeight;
        if (homeHeight > vh   100) {
            heightToShow = homeHeight - vh   folioHeight   skillsHeight;
        } else {
            heightToShow = 100   folioHeight   skillsHeight;
        }
        const winScroll = window.scrollY;  
        console.log("winScroll: "   winScroll);
        console.log("heightToShow: "  heightToShow);
        console.log("wS > hTS: "   (winScroll > heightToShow));

        if (winScroll > heightToShow) {
            isVisible && setIsVisible(true);
        } else {
            setIsVisible(false);
        }
    };

    return ( 
        <>
            <div className='container aboutContainer' id="About">
                { isVisible ? (
                    <>
                    <h1 className="aboutH1">This is Me</h1>
                    <div className="headshot">
                            <Headshot />
                            <img 
                                src="/assets/images/about/headshot.webp" 
                                alt="" 
                                id="headshotImg"
                            />
                    </div>
                </>
                ) : ""}
            </div>
        </>
    );
}

export default About

If there's a simpler solution, I am certainly open to it, but please don't just tell me "use this library, and put the tags in. It'll take care of it." The point of this exercise is that I am trying to learn how to do it, so that I can tell if a library is a good choice for myself later.

CodePudding user response:

better you do it with intersection observer, https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

CodePudding user response:

Many thanks to jorgepelcastre for pointing me in the right direction. After an entire day spent chasing ghosts to solve this, his solution fixed it in under 10 minutes, without just dumping a bloated library on me.

For anyone interested in the working solution based on his help, I have posted the updated code below:

import React, { useEffect, useState } from 'react';
import Headshot from '../../../assets/images/about/Headshot';

const About = () => {
    const containerRef = useRef(null);
    const [isVisible, setIsVisible] = useState(false);

    const callbackFunction = (entries) => {
        const [entry] = entries;
        setIsVisible(entry.isIntersecting);
    }
    const options = {
        root: null,
        rootMargin: "0px",
        threshold: 1.0
    }

    useEffect(() => {
        const observer = new IntersectionObserver(callbackFunction, options);
        if (containerRef.current) observer.observe(containerRef.current);

        return () => {
            if (containerRef.current) observer.unobserve(containerRef.current);
        }
    }, [containerRef, options])

    return ( 
        <>
            <div className='container aboutContainer' id="About">
                { isVisible ? (
                    <>
                    <h1 className="aboutH1">This is Me</h1>
                    <div className="headshot">
                            <Headshot />
                            <img 
                                src="/assets/images/about/headshot.webp" 
                                alt="Brian Quinney - Programmer, Geek, Lifelong Learner" 
                                id="headshotImg"
                            />
                    </div>
                </>
                ) : ""}
                <div className="venn">
                    <h2 ref={ containerRef }>Who am I?</h2>
                </div>
            </div>
        </>
    );
}

export default About

Note the ref={ containerRef } in the h2 tag below the conditional rendering. The detailed description of how it works can be found in the link jorgepelcastre provided. The long and short of it is that the code checks if the tag with the 'ref' prop is fully in view. If so, it renders the conditional section, including running any attached animations.

  • Related