【react】基於umi框架編寫輪播圖組件


引言

基於該文仿寫:web 完整輪播圖——帶只拖鞋去流浪 https://zhuanlan.zhihu.com/p/138232728

組件源碼:https://gitee.com/leftstan/rotation.git

組件效果:https://www.jianguoyun.com/p/Dd81zscQzLDdCRiKuo4E

 

 

 

創建項目

創建一個umi2.x的項目

選擇項目類型為app,不使用ts

npm create umi

 

安裝依賴

yarn

 

運行項目

yarn start

 

組件調用

data為需要展示圖片的數據

size輪播圖組件尺寸

delay為播放時延(可選)

const  data  = [ {
   key :  " 1 " ,
   src :  " https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/a532e33470d046b3f044d5ea49fc5e9e.png?thumb=1&w=1226&h=460&f=webp&q=90 " ,
   action :  " https://www.mi.com "
},
...
]
 
< div >
   { /* 輪播圖數據 */ }
   { /* 輪播圖尺寸, 單位px*/ }
   { /* 輪播時延 */ }
   < Rotation
      data ={ data }
      size ={{  width :  ' 1200 ' ,  height :  ' 480 '  }}
      delay ={ 2000 } >
    </ Rotation >
</ div >

  

 

編寫組件

 html布局為三部分內容

圖片,導航按鈕,上/下一頁

        <div
            className={styles.content}
            style={{ width: size.width + 'px', height: size.height + 'px' }}
            onMouseOver={() => stopPlay()}
            onMouseLeave={() => autoPlay()}
        >
            {/* 圖片展示輪播 */}
            <ul className={styles.picContent} id="picContent">
                {data.map((item) =>
                    <li key={`rotate${item.key}`} style={{ width: size.width + 'px' }}>
                        <a href={item.action}>
                            <img style={{ width: size.width + 'px', height: size.height + 'px' }} src={item.src} id={`rotate${item.key}`} />
                        </a>
                    </li>
                )}
            </ul>

            {/* 底部導航按鈕跳轉 */}
            <ul className={styles.dotButton}>
                {data.map((item, index) =>
                    <li
                        className={`${styles.dot} ${Math.abs(btn) === index ? styles.current : ''}`}
                        key={`btn${index}`} id={`btn${index}`}
                        onClick={() => jump(index)}
                    >
                    </li>
                )}
            </ul>

            {/* 上下頁按鈕 */}
            <div className={`${styles.btn} ${styles.left}`} onClick={() => {
                prev();
            }}><</div>
            <div className={`${styles.btn} ${styles.right}`} onClick={() => {
                next();
            }}>></div>
        </div>

  

無縫切換

需要在圖片【盒子尾部】插入一份第一張圖片;

當播放到【最后一張圖】(數據最后一張),要跳轉到第一張圖時,執行動畫操作跳轉到我們插入到【盒子尾部】的【第一張圖片的副本】

此時再播放下一張時,先無動畫跳轉到【第一張圖片】,再執行動畫操作跳轉到【第二張圖片】

 

使用react hooks定義需要用到的參數

useState進行定義

//圖片數據
    const [data, setData] = useState(props.data);
    //圖片尺寸
    const [size, setSize] = useState(props.size);
    //圖片寬度
    const [width, setWidth] = useState(size.width > 0 ? size.width : 1200);
    //右下角導航按鈕當前選項
    const [current, setCurrent] = useState(0);
    const [btn, setBtn] = useState(0);
    //自動播放計時器
    const [timer, setTimer] = useState();

 

useEffect中進行初始化

useEffect(() => {
        //獲取單張圖片寬度
        const wid = size.width > 0 ? size.width : 1200;
        setWidth(wid);
        //設置圖片盒子寬度
        let pics = document.getElementById('picContent');
        pics.style.width = (data.length + 1) * width + 'px';
        // 將第一張圖片 clone 到最后
        let firstLi = pics.firstChild.cloneNode(true);
        pics.appendChild(firstLi);
        //設置自動播放
        autoPlay();
    }, [])

 

下一頁

    const next = () => {
        let pics = document.getElementById('picContent');
        let len = pics.children.length - 1;
        let ind = current;
        //無動畫,從尾部跳轉到第一張圖片
        if (ind >= len) {
            ind = 0;
            pics.style.left = 0;
        }
        ind++;
        //跳轉動畫
        animate(pics, -width * ind);
        //更新導航按鈕
        setCurrent(ind);
        ind >= len ? setBtn(0) : setBtn(ind);
        // console.log("next is: ", ind)
    }

 

底部導航按鈕跳轉

   const jump = (ind) => {
        let pics = document.getElementById('picContent');
        animate(pics, -width * ind);
        setCurrent(ind);
        setBtn(ind);
    }

 

動畫效果

//動畫對象,結束幀位置(目標位置)
    const animate = (obj, target) => {
        clearInterval(obj.timer);
        obj.timer = setInterval(() => {
            var leader = obj.offsetLeft;
            var step = 30;
            //設置不同動畫方向
            step = leader < target ? step : -step;
            if (Math.abs(leader - target) >= Math.abs(step)) {
                leader += step;
                obj.style.left = leader + 'px';
            } else {
                obj.style.left = target + 'px';
                clearInterval(obj.timer);
            }
        }, 10)
    }

 

自動播放

react hooks與setInterval

在react hooks中直接使用setInterval無法達到預期的效果,需要使用useReducer

(具體緣由參考該文:https://www.cnblogs.com/qcloud1001/p/10408634.html)

//設置自動播放
    const autoPlay = () => {
        setTimer(setInterval(() => {
            dispatch('inc');
        }, props.delay));
    }
    //取消自動播放
    const stopPlay = () => {
        clearInterval(timer);
        setTimer(null);
    }
    const [count, dispatch] = useReducer((state, action) => {
        if (action === 'inc') {
            next();
        }
    }, 0);

  

  

參考資料:

https://v2.umijs.org/zh/guide/create-umi-app.html#%E5%88%9B%E5%BB%BA-umi-%E9%A1%B9%E7%9B%AE

https://zhuanlan.zhihu.com/p/138232728

https://www.cnblogs.com/qcloud1001/p/10408634.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM