场景
如图 粉色底的盒子是可视区域,可视区域内有一组数据,需要实现自动轮播功能;
预期: 自动轮播 假设有9张图片,当滚动到第九张图片时后面接上图片组的第一张,以此实现一个循环滚动的效果
自定义hook
思路:
通过控制maxIndex来操作当前可视区域内的展示的图片的数量;
initIndex初始化的index值;
const prevIndex = index ? index - 1 : maxIndex;来实现向前翻页;
const toNext = () => toIndex(nextIndex);实现向后翻页
import {useState} from 'react';
function useCarousel(maxIndex: number, initIndex: number = 0) {
if (maxIndex < 0) maxIndex = 0;
if (initIndex > maxIndex) initIndex = maxIndex;
const [index, toIndex] = useState(initIndex);
const prevIndex = index ? index - 1 : maxIndex;
const toPrev = () => toIndex(prevIndex);
const nextIndex = index + 1 > maxIndex ? 0 : index + 1;
const toNext = () => toIndex(nextIndex);
const indexList = Array.from({length: maxIndex + 1}, (item, index) => index);
return {prevIndex, toPrev, nextIndex, toNext, index, toIndex, indexList};
}
export default useCarousel;
组件
import useCarousel from '../hooks/useCarousel';
import React, {useEffect, useRef, useState} from 'react';
import clazz from './Demo.module.scss';
type demoProps = {}
const demo: React.FunctionComponent<demoProps> = (props) => {
// 假设有9组图片数据
const list = [1,2,3,4,5,6,7,8,9]
// 初始化maxIndex
const maxIndex = list.length - 1 + 2;
const {index, toIndex, toPrev, toNext} = useCarousel(maxIndex, 1);
// 用于控制鼠标hover 停止轮播效果
const [autoSwitch, setAutoSwitch] = useState(true);
// 轮播位移的效果
const getTransform = index => `translateX(-${(index + 1) * 220}px)`;
const ulRef = useRef<HTMLDivElement>();
const jumpToIndex = (targetIndex: number) => {
ulRef.current.style.transform = getTransform(targetIndex);
ulRef.current.style.transition = 'none';
window.scrollY;
ulRef.current.style.transition = null;
};
useEffect(() => {
// 动画效果为切换到最后一张, 移到头部执行
if (index === maxIndex) {
jumpToIndex(0);
toIndex(1);
}
// 动画效果为切换到第一张, 移到末尾执行
if (index === 0) {
jumpToIndex(maxIndex);
toIndex(maxIndex - 1);
}
}, [index]);
useEffect(() => {
if (autoSwitch) {
const ti = setTimeout(toNext, 1800);
return () => clearTimeout(ti);
}
}, [toNext, index, autoSwitch]);
return (
<div className={clazz.box}>
<div className={clazz.cardList} style={{transform: getTransform(index)}} ref={ulRef}>
{[...list.slice(-1), ...list, ...list.slice(0, 6)]?.map((item,indexNum) => {
return <div className={clazz.card}>
{item}
</div>
})}
</div>
</div>
);
};
export default demo;
CSS
.box {
margin: 200px auto;
width: 1100px;
height: 200px;
background-color: pink;
overflow: hidden;
.cardList {
display: flex;
}
.card {
height: 200px;
min-width: 200px;
background-color: skyblue;
margin-right: 20px;
text-align: center;
line-height: 200px;
font-size: 20px;
font-weight: 600;
}
}