引言
基於該文仿寫: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
