效果图
简单效果图:
开发效果图:
逻辑:
通过定时改变一个状态变量,使得每个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
查看评论