React-hooks封装无缝滑动轮播图(js动画,css动画)

4,605 阅读1分钟

前言

由于现在在基础架构组,写的项目也没有ui框架,基本上都是要自己去封装的。于是需要写一个

轮播图组件,心想这不是分分钟的事情嘛。说写就写...

第一版

思路:用ul li 的结构,再结合js定时器去实现轮播动画,说干就干...

无缝滑动原理: 红色的1, 3都是克隆的过渡元素. index从 3 4 动画结束时,马上将指针滑动到1, 这样就可以实现无缝轮播. image.png

下面上js动画伪代码

Carousel.tsx


interface Props {
    children: React.ReactNode
}
const PERCENTAGE = 100; // 百分比

const Carousel = (props: Props) => {

  const [left, setLeft] = useState(0)
  let  tempLeft = useRef(left);
  
  const transform = (target) => {
      let timer = setInterval(() => {
          if (Math.abs(tempLeft.current - target) <= 1 ) {
            clearInterval(timer)
            tempLeft.current = target;
            setLeft(target);
            
            if (target/PERCENTAGE >= len + 1) {
                // 此时已经滑动到我们克隆的最后一张元素了
                setIndex(1). // 这里就直接滑动到第一张完成无缝.
                setLeft(100)
                return
            }
            
          }
          
          
          let step = (target - tempLeft.current) / 10;
          step = step > 0 ? Math.ceil(step) : Math.floor(step);
          tempLeft.current = tempLeft.current + step;
          setLeft(() => tempLeft.current);
      }, 20)
  }

    return (
        <div className="carousel">
            <ul 
                className="carousel__container"
                style={{
                  left: `-${left}%`,
                  width: (len + 2) * PERCENTAGE + "%"
                }}
            >
                {children[len - 1]} // 这里将最后一个元素克隆到第一个
                {children}
                {children[0]} // 这里将第一个元素克隆到最后一个
            </ul>
            
        </div>
    )

}


基本上无缝轮播就实现了,具体还需要加一个正在动画的标识,防止用户点的太快,发生异常。

  • 这样实现还会有几个问题,频繁改变state, 组件渲染次数过多. 因此我们可以考虑使用css实现动画.
  • 使用定时器去完成动画,可能还会发生不可描述的事...

第二版

css动画



const transform = (target) => {

      timeoutId.current && clearTimeout(timeoutId.current);
      needAnimate.current = true;
      setLeft(target);

      if (target / 100 >= len + 1) {
        // 下面要不带动画的 滚动到真正的第一个元素
        timeoutId.current = setTimeout(() => {
          needAnimate.current = false;
          setLeft(100);
          setIndex(1);
        }, 350);
      }

    
}



    <ul
        style={{
            left: `-${index * 100}%`,
            width: (len + 2) * 100 + '%',
            transition: needAnimate.current ? "all 0.3s" : "none"
        }}
    
    >
        ...
    </ul>

上面就是这两种写法, 推荐还是用css动画比较好