109 리액트 - 이미지 레이지로드

source: categories/study/vue-experiance/vue-experiance_9-99_10.md

109 리액트 - 이미지 레이지로드

상황

  • 이미지 로드되는 시점이 서로 다 달라서 처음 로드된 화면이 엉성하게 보이는 이슈
  • 빠르게 로드된 이미지는 애니메이션 시작점부터 제대로 보여서 자연스러우나 애니메이션이 실행되고나서 진행된 도중에 로드된 이미지는 부자연스럽게 보임
  • 또한 이미지 로드되는 시점이 서로 달라 처음 로딩시 영역이 뚝뚝 끊겨서 늘어나는 것처럼 보이는 현상도 발생

해결방법

  1. 아래 oxMain 클래스를 가진 요소에 opacity: 0 속성을 주었습니다.
  2. lazy-load를 적용해야하는 img 태그에 lazy-load 클래스명과 lazy-src 속성을 적용했습니다.
  3. 그리고 Promise 객체의 allSettled라는 static 메소드를 활용하여 lazy-load 클래스를 가지고있는 img 태그들의 이미지 로드가 완전히 끝나는 시점을 인식하도록 했습니다.
  4. 이미지 로드가 완전히 끝나면 oxMain 요소에 is_loaded 클래스를 추가하여 opacity: 1 속성을 주었습니다.

Promise.allSettled 메소드와 Promise.all 메소드의 차이점

  • Promise.allSettled 메소드는 순회 가능한 객체를 통해 나열된 Promise 각각이 resolve를 하던 reject를하던 상관없이 각 Promise의 결과를 배열 형태로 반환합니다.
  • Promise.all 메소드는 순회 가능한 객체를 통해 나열된 Promise들 중에서 어느것 하나라도 reject하는 경우 이를 사용해 자기 자신도 reject합니다.

이러한 차이 때문에 요즘은 Promise.all 보단 Promise.allSettled 메소드를 많이 사용한다고합니다.


import React, {useEffect, useRef} from 'react'
import './MainPage.css'
import imgTitle from '../../resources/picture/oxQuizTitle3.png';
import imgH1 from '../../resources/picture/h1_fix.png';
import imgH2 from '../../resources/picture/h2_fix.png';
import btnStart from '../../resources/picture/btnStart3.png';
import imgCloudMain from '../../resources/picture/cloudMain.png'
import imgHill from '../../resources/picture/hill3.png'
import {Link} from 'react-router-dom';
import lottie from 'lottie-web';


function MainPage() {

    const container1 = useRef(null)
    const container2 = useRef(null)
    const container3 = useRef(null)

    const imgLoad = () => {
        const imgArr = Array.prototype.slice.call(document.querySelectorAll('.lazy-load'));
        Promise.allSettled(imgArr.map((item, index) => {
            return new Promise(resolve => {
                item.addEventListener('load', () => {
                    resolve();
                })
                item.src = item.getAttribute('lazy-src');
            })
        }))
            .then(_ => {
                document.querySelector('.oxMain').classList.add('is_loaded');
            })
            .catch(err => console.log(err));
    }


    useEffect(() => {
        lottie.loadAnimation({
            container: container1.current,
            renderer: 'svg',
            loop: true,
            autoplay: true,
            animationData: require('../../resources/svg/lottie_gift.json'),
        })
        lottie.loadAnimation({
            container: container2.current,
            renderer: 'svg',
            loop: true,
            autoplay: true,
            animationData: require('../../resources/svg/lottie_play1.json'),
        })
        lottie.loadAnimation({
            container: container3.current,
            renderer: 'svg',
            loop: true,
            autoplay: true,
            animationData: require('../../resources/svg/lottie_play2.json'),
        })
        imgLoad();
    }, [])

    return (
        <div className="oxMain">
            <div className="LottieGift" ref={container1}/>
            <div className="wrapImgTitle">
                <img lazy-src={imgTitle} className="lazy-load imgTitle" alt=""/>
            </div>
            <div className="wrapImgH1">
                <img lazy-src={imgH1} className="lazy-load imgH1" alt=""/>
            </div>
            <div className="wrapImgH2">
                <img lazy-src={imgH2} className="lazy-load imgH2" alt=""/>
            </div>
            <div className="wrapButton">
                <Link to="/quiz">
                    <img lazy-src={btnStart} className="lazy-load btnStart" alt=""/>
                </Link>
            </div>
            <div className="wrapOtherImgs">
                <img lazy-src={imgCloudMain} className="lazy-load imgCloudMain" alt=""/>
                <img lazy-src={imgHill} className="lazy-load imgHill" alt=""/>
            </div>
            <div className="LottiePlay1" ref={container2}/>
            <div className="LottiePlay2" ref={container3}/>
        </div>

    )
}

export default MainPage