飞道的博客

绕圆旋转动画组件,拿过来直接用

252人阅读  评论(0)

效果图

简单效果图:

开发效果图:

逻辑:

通过定时改变一个状态变量,使得每个Item的类名发生改变,然后剩下的就是写css动画了。

用到的依赖库:

classNames:灵活控制类名的一个库

代码

TSX:

/*
 * @Author: atwLee
 * @Date: 2022-06-14 11:03:41
 * @LastEditors: atwLee
 * @LastEditTime: 2022-06-14 16:15:36
 * @FilePath: \big-screen-lib\packages\big-screen\src\aroundCircleLoop\index.tsx
 * @Description: 绕圈轮播组件
 */

/**
 * @param
 * AroundCircleWrapper:
 * 1、interval:轮播时间间隔
 * 2、blockW:item宽
 * 3、blockH:item高
 * 4、dataLength:数据集长度
 * 5、openLoop:是否轮播
 * 6、position:切换成相对定位,切换为相对定位后要设置外层宽高(默认为绝对定位,宽高100%)
 *  6.1、wrapperWidth:外层宽
 *  6.2、wrapperHeight:外层高
 *  6.3、show:是否是相对定位
 *
 * AroundCircleItem:
 * num:当前数据项的下标(默认填写index即可)
 * @returns
 *
 * @note
 * 1、数据最少为8个
 * 2、AroundCircleWrapper默认绝对定位,相对定位需手动开启
 */
import React, {
    useEffect, useRef, useState, useContext, useMemo } from "react";
import styles from "./index.less";
import classNames from "classnames";

const LoopParamContext = React.createContext({
   });
export const AroundCircleWrapper: React.FC<{
   
  /**
   * @description 轮播时间间隔。必填
   */
  interval: number;
  /**
   * @description item宽。必填
   */
  blockW: string;
  /**
   * @description item高。必填
   */
  blockH: string;
  /**
   * @description 数据集长度,填写--数据.length--即可。必填。最少为8个
   */
  dataLength: number;
  /**
   * @description 是否开启轮播,必填
   */
  openLoop: boolean;
  /**
   * @description show:false 为绝对定位,宽高默认100%。
   * true为相对定位,相对定位需定义宽高(wrapperWidth,wrapperHeight)
   */
  position: {
   
    show: boolean;
    wrapperWidth: string;
    wrapperHeight: string;
  };
}> = (props: any) => {
   
  const {
    interval, blockW, blockH, dataLength, openLoop, position } = props;
  let [animateNum, setAnimateNum] = useState(0);
  let timer = useRef<any>();
  useEffect(() => {
   
    openLoop && startLoop(); // 开启数据块轮播
    return () => {
   
      clearInterval(timer.current);
      setAnimateNum(0);
    };
  }, []);

  function startLoop() {
   
    timer.current = setInterval(() => {
   
      setAnimateNum(animateNum++);
    }, interval);
  }
  const contextValue = useMemo(
    () => ({
    animateNum, blockW, blockH, dataLength }),
    [animateNum]
  );
  return (
    <LoopParamContext.Provider value={
   contextValue}>
      <div
        className={
   styles.dimensionLayer}
        style={
   
          position.show
            ? {
   
                position: "relative",
                width: `${
     position.wrapperWidth}px`,
                height: `${
     position.wrapperHeight}px`,
              }
            : {
   }
        }
      >
        <div className={
   styles.dimensionWrapper}>{
   props.children}</div>
      </div>
    </LoopParamContext.Provider>
  );
};

export const AroundCircleItem: React.FC<{
   
  /**
   * @description 控制item轮播的索引,传数组下标即可
   */
  num: number;
}> = (props: any) => {
   
  const {
    num } = props;
  const {
    animateNum, blockW, blockH, dataLength }: any =
    useContext(LoopParamContext);
  // 判断不在屏幕显示的其他的item
  function isDimensionOther(
    animateNum: number,
    index: number,
    dataLength: number
  ): boolean {
   
    if (
      (animateNum + index) % dataLength !== 0 &&
      (animateNum + index) % dataLength !== 1 &&
      (animateNum + index) % dataLength !== 2 &&
      (animateNum + index) % dataLength !== 3 &&
      (animateNum + index) % dataLength !== 4 &&
      (animateNum + index) % dataLength !== dataLength - 1 &&
      (animateNum + index) % dataLength !== dataLength - 2 &&
      (animateNum + index) % dataLength !== dataLength - 3
    )
      return true;
    else return false;
  }
  return (
    <div
      className={
   classNames(
        styles["dimensionItem"],
        {
    [styles[`dimensionItem0`]]: (animateNum + num) % dataLength === 0 },
        {
    [styles[`dimensionItem1`]]: (animateNum + num) % dataLength === 1 },
        {
    [styles[`dimensionItem2`]]: (animateNum + num) % dataLength === 2 },
        {
    [styles[`dimensionItem3`]]: (animateNum + num) % dataLength === 3 },
        {
    [styles[`dimensionItem4`]]: (animateNum + num) % dataLength === 4 },
        {
   
          [styles[`dimensionItemL1`]]:
            (animateNum + num) % dataLength === dataLength - 1,
        },
        {
   
          [styles[`dimensionItemL2`]]:
            (animateNum + num) % dataLength === dataLength - 2,
        },
        {
   
          [styles[`dimensionItemL3`]]:
            (animateNum + num) % dataLength === dataLength - 3,
        },
        {
   
          [styles[`dimensionItemOther`]]: isDimensionOther(
            animateNum,
            num,
            dataLength
          ),
        }
      )}
      style={
   {
   
        width: `${
     blockW}px`,
        height: `${
     blockH}px`,
        left: `calc(50% - ${
     blockW / 2}px)`,
      }}
    >
      {
   props.children}
    </div>
  );
};

Less:

// 0
@dimensionItem0Transform: translate(0, 0) scale(1);
// 1&l1
@dimensionItem1-l1scale: scale(0.88);
@dimensionItem1TranslateXY: translate(110%, -16%);
@dimensionItemL1TranslateXY: translate(-110%, -16%);
// 2&l2
@dimensionItem2-l2scale: scale(0.76);
@dimensionItem2TranslateXY: translate(140%, -110%);
@dimensionItemL2TranslateXY: translate(-140%, -110%);
// 3&l3
@dimensionItem3-l3scale: scale(0.64);
@dimensionItem3TranslateXY: translate(110%, -190%);
@dimensionItemL3TranslateXY: translate(-110%, -190%);
// 4&l4
@dimensionItem4TranslateXY: translate(80%, -230%) scale(0);
@dimensionItemL4TranslateXY: translate(-80%, -230%) scale(0);

.dimensionLayer {
   
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  .dimensionWrapper {
   
    position: relative;
    width: 100%;
    height: 100%;
    contain: strict;
    .dimensionItem {
   
      position: absolute;
      bottom: 0;
      font-size: 62px;
      background-size: 100% 100%;
      background-repeat: no-repeat;
      cursor: default;
    }
    .dimensionItem0 {
   
      animation: myAnimation15-0 2s linear forwards;
      @keyframes myAnimation15-0 {
   
        from {
   
          transform: @dimensionItemL1TranslateXY @dimensionItem1-l1scale;
        }
        to {
   
          transform: @dimensionItem0Transform;
        }
      }
    }
    .dimensionItem1 {
   
      animation: myAnimation0-1 2s linear forwards;
      @keyframes myAnimation0-1 {
   
        from {
   
          transform: @dimensionItem0Transform;
        }
        to {
   
          transform: @dimensionItem1TranslateXY @dimensionItem1-l1scale;
        }
      }
    }
    .dimensionItemL1 {
   
      animation: myAnimation14-15 2s linear forwards;
      @keyframes myAnimation14-15 {
   
        from {
   
          transform: @dimensionItemL2TranslateXY @dimensionItem2-l2scale;
        }
        to {
   
          transform: @dimensionItemL1TranslateXY @dimensionItem1-l1scale;
        }
      }
    }
    .dimensionItem2 {
   
      animation: myAnimation1-2 2s linear forwards;
      @keyframes myAnimation1-2 {
   
        from {
   
          transform: @dimensionItem1TranslateXY @dimensionItem1-l1scale;
        }
        to {
   
          transform: @dimensionItem2TranslateXY @dimensionItem2-l2scale;
        }
      }
    }
    .dimensionItemL2 {
   
      animation: myAnimation13-14 2s linear forwards;
      @keyframes myAnimation13-14 {
   
        from {
   
          transform: @dimensionItemL3TranslateXY @dimensionItem3-l3scale;
        }
        to {
   
          transform: @dimensionItemL2TranslateXY @dimensionItem2-l2scale;
        }
      }
    }
    .dimensionItem3 {
   
      animation: myAnimation2-3 2s linear forwards;
      @keyframes myAnimation2-3 {
   
        from {
   
          transform: @dimensionItem2TranslateXY @dimensionItem2-l2scale;
        }
        to {
   
          transform: @dimensionItem3TranslateXY @dimensionItem3-l3scale;
        }
      }
    }
    .dimensionItemL3 {
   
      animation: myAnimation12-13 2s linear forwards;
      @keyframes myAnimation12-13 {
   
        from {
   
          transform: @dimensionItemL4TranslateXY;
          opacity: 0;
        }
        to {
   
          transform: @dimensionItemL3TranslateXY @dimensionItem3-l3scale;
          opacity: 1;
        }
      }
    }
    .dimensionItem4 {
   
      animation: myAnimation3-4 2s linear forwards;
      @keyframes myAnimation3-4 {
   
        from {
   
          transform: @dimensionItem3TranslateXY @dimensionItem3-l3scale;
          opacity: 1;
        }
        to {
   
          transform: @dimensionItem4TranslateXY;
          opacity: 0;
        }
      }
    }
    .dimensionItemOther {
   
      opacity: 0;
    }
  }
}

//example
.name {
   
  padding-top: 3%;
  text-align: center;
  color: white;
  font-size: 0.9em;
  width: fit-content;
  margin: auto;
  display: flex;
  align-items: center;
  .arrowLeft {
   
    width: 80px;
    transform: rotate(180deg);
    margin-right: 26px;
  }
  .arrowRight {
   
    width: 80px;
    margin-left: 26px;
  }
}
.value {
   
  font-size: 2em;
  text-align: center;
  font-family: DiGiFaceWide;
  color: white;
  font-weight: normal;
  margin-top: 10px;
  span {
   
    font-size: 0.8em;
  }
}
.unit {
   
  text-align: center;
  color: white;
  // font-weight: bold;
  font-size: 1em;
  margin-top: 2%;
  font-family: PingFang SC-Bold, PingFang SC;
}

用法

参数见上文

// 引入
import {
    AroundCircleWrapper, AroundCircleItem } from './components/AroundCircleLoop';

<AroundCircleWrapper
          blockW="750"
          blockH="480"
          dataLength={
   dataArr.length}
          interval={
   10000}
          openLoop={
   true}
          position={
   {
   
            show: false,
          }}
        >
          {
   dataArr.map((item, index) => {
   
            return (
              <AroundCircleItem key={
   index} num={
   index}>
                <p>{
   item.name}</p>
              </AroundCircleItem>
            );
          })}
        </AroundCircleWrapper>

转载:https://blog.csdn.net/BWater_monster/article/details/125545069
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场