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

效果图

简单效果图:
在这里插入图片描述

逻辑:

通过定时改变一个状态变量,使得每个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>
  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值