React实现轮播图

109 阅读2分钟

前后切换轮播图

实现前后切换的轮播图依靠transform这个属性,只要得到每张图片的宽度,左右移动时候根据其第几张图片的位置切换即可。

无缝衔接轮播图

需要添加两张图片,把第一张图片复制一份添加到末尾,然后把末尾的图片复制一份添加到开头。
当是最后第二张图片时候再往右,这个时候就不加动画的切换到第一张图片,然后添加动画切换到第二张图片,这样就实现了往右滑动的无缝轮播。
!!!切换到第二张图片时候需要强制渲染,不然会添加动画的代码会覆盖不添加动画的代码
往左同理

实现代码

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

// 实现轮播图
import One from '../../assets/images/1.jpeg';
import Last from '../../assets/images/1.jpeg';
import Two from '../../assets/images/2.jpeg';
import Three from '../../assets/images/3.jpeg';
import Start from '../../assets/images/3.jpeg';
import './style.css';

export default function Swiper() {
    const data = [One, Two, Three];
    const [imagesInfo, setImagesInfo] = useState([]);
    const [currentIndex, setCurrentIndex] = useState(0);
    const [autoPlayFlag, setAutoPlayFlag] = useState(true);
    const containerRef = useRef(null);
    const flagRef = useRef(false);
    const len = data.length;
    useEffect(() => {
        let startX = 0;
        const images = data.map((item) => {
            return new Promise((resolve, reject) => {
                const img = new Image();
                img.src = item;
                img.onload = () => {
                    startX += img.width;
                    resolve({ src: item, widthSum: startX, height: img.height, width: img.width });
                }
            });
        });
        Promise.all(images).then((res) => {
            setImagesInfo(res);
        });
    }, [])

    useEffect(() => {
        let timer = null;
        if (containerRef.current) {
            const images = containerRef.current.children;
            if (flagRef.current) {
                for (let item of images) {
                    item.style.transition = 'none';
                    if (currentIndex === 0) {
                        item.style.transform = `translate(${0}px)`;
                    } else {
                        item.style.transform = `translate(${-(imagesInfo[currentIndex].widthSum + imagesInfo[len - 1].width)}px)`;
                    }
                }
                // 强制渲染
                containerRef.current.getBoundingClientRect();
            // }
            for (let item of images) {
                item.style.transition = 'transform .5s ease-out';
                item.style.transform = `translate(${imagesInfo[currentIndex].width - imagesInfo[currentIndex].widthSum - images[0].width}px)`;
            }

            if (autoPlayFlag) {
                timer = autoPlay();
            }
        }
        return () => {
            timer && timer();
        }
    })

    const forward = (e) => {
        if (currentIndex === len - 1) {
            // 消除动画效果
            flagRef.current = true;
            console.log('%c [ 消除动画效果 ]-34', 'font-size:13px; background:pink; color:#bf2c9f;', "无缝当前是最后一张")
            // 回到第一张
            setCurrentIndex(0);
        } else {
            flagRef.current = false;
            setCurrentIndex(currentIndex + 1);
            console.log("下一张", currentIndex);
        }
    }
    const backward = (e) => {
        if (currentIndex === 0) {
            // 无缝轮播
            flagRef.current = true;
            console.log('%c [  ]-45', 'font-size:13px; background:pink; color:#bf2c9f;', "无缝轮播 当前是第一张");
            setCurrentIndex(len - 1);
        } else {
            flagRef.current = false;
            setCurrentIndex(currentIndex - 1);
            console.log("上一张", currentIndex);
        }
    }

    const autoPlay = () => {
        const timer = setInterval(() => {
            forward();
        }, 3000);
        return () => {
            clearInterval(timer);
        }
    }

    return (
        <div>

            {imagesInfo.length && (
                <div ref={containerRef} className="container">
                    <img key={'start'} src={Start} alt="img" />
                    {imagesInfo.map((item, index) => {
                        return <img key={index} src={item.src} alt="img" />
                    })}
                    <img key={'Last'} src={Last} alt="img" />
                </div>
            )}

            <div>
                <button onClick={() => {
                    setAutoPlayFlag(false);
                    backward();
                }}>上一张</button>
                <button onClick={ () => {
                    setAutoPlayFlag(false);
                    forward();
                }}>下一张</button>
            </div>
        </div>
    )
}