引言
基於該文仿寫: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為播放時延(可選)
編寫組件
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