基于React实现的一个轮播图
样例
前言
你永远想不到焦急等待放假的时候人能做出什么事情来,比如我就过来写了一篇文章。
首先,这是我的第一篇,希望也是最后一篇。
其次,写这篇文章不是为了传播什么知识,毕竟2022年了,这可是人人知道轮播图的时代,我的实现也没有任何有新意的地方。如果除了写本身以外非要赋予一些意义的话,就是我还希望能从诸位身上得到一些在我的认识范围以外的指导。但之所以这样,也是因为我现在懒得去翻一篇篇文章,只想坐等评论指点,要么就无事发生。
最后,希望你已经放假了。最后的最后,如果你看完了整篇文章,你任何非攻击性的批判都能帮助到我。
实现思路
就像轮播图一样,在末尾补上第一页,移动完成后转换到第一页;在第一页之前补上最后一页,在第一页向前移动时转换到最后一页。
如何使用
<Carousel
otherCss={css`
height: 300px;
`}
showNumber={4}
childSpace="1rem"
>
{times(10).map((item) => (
<div
key={item}
css={css`
background: #ddd;
color: #fff;
display: flex;
align-items: center;
justify-content: space-around;
`}
>
{item + 1}
</div>
))}
</Carousel>
参数解释
- showNumber: 每页轮播图显示的子元素个数
- childSpace: 组件与组件之间的间隔
- otherCss: 自定义组件上添加样式的别称,会累加到组件内部的div上
- children: 展示的内容列表(组件标签里面的)
实现过程
- 将传入的children转换为数组,并根据每页展示的数量(showNumber)补充最后一页(如果最后一页的个数不足showNumber)以及首尾
// 规划子元素
const childrenList = useMemo(() => {
const childArray = React.Children.toArray(children);
//最后一页不足showNumber,复制前一页相应的个数补充
const r = childArray.length % showNumber;
if (r !== 0) {
childArray.splice(-r, 0, ...childArray.slice(-showNumber, -r));
}
//复制起始的元素补充新的最后一页
childArray.push(...childArray.slice(0, showNumber));
//复制末尾的元素(倒数第二页)补充到新的第一页
childArray.unshift(...childArray.slice(-showNumber * 2, -showNumber));
return childArray.map((item) => ({ ele: item, _id: uniqueId('_') }));
}, [children, showNumber]);
- 计算子卡片宽度
const childWidth = useMemo(
() => `calc(${(1 / showNumber) * 100}% - ${childSpace})`,
[showNumber, childSpace]
);
- 左右切换按钮单击
// 更新left实现轮播效果
const [targetLeft, setTargetLeft] = useState('-100%');
const handleBtnClick = useCallback(
(action) => {
// action? leftBtn:rightBtn
let rollWidth = 0;
const roll = () => {
const stepRadio = action ? -10 : 10;
rollWidth += 1;
if (rollWidth <= 10) {
setTargetLeft((v) => {
const curValue = parseInt(v, 10) + stepRadio;
const pageNumber = childrenList.length / showNumber;
// 到了最后一页后,回到第一页
if (curValue <= -(pageNumber - 1) * 100) {
return '-100%';
}
// 到了第一页后,回到倒数第二页(倒数第二页的数据即是最末尾的数据)
if (curValue === 0) {
return `-${(pageNumber - 2) * 100}%`;
}
return `${curValue}%`;
});
requestAnimationFrame(roll);
}
};
requestAnimationFrame(roll);
},
[childrenList, showNumber]
);
- 组件dom
<div
css={css`
overflow: hidden;
position: relative;
padding: ${16 / FONT_BASE}rem 1rem 0;
${otherCss}
`}
>
<div
css={css`
width: 100%;
height: 100%;
position: absolute;
left: ${targetLeft};
display: flex;
`}
>
{childrenList.map((item) =>
React.cloneElement(item.ele as React.ReactElement, {
style: {
flexShrink: 0,
width: childWidth,
marginLeft: childSpace,
},
key: item._id,
})
)}
</div>
<CarouselButton
onLeftClick={() => {
handleBtnClick(false);
}}
onRightClick={() => {
handleBtnClick(true);
}}
/>
</div>
- CarouselButton 就是两个放在左右垂直居中的按钮
总结
第一次写,颇有毕业论文疯狂贴代码的感觉。GGBoy!!! 源码地址(Github)