【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为播放时延(可选)
编写组件
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

浙公网安备 33010602011771号