git地址体验地址 https://hongbin.xyz/swipe
import { FC, ReactElement, useEffect, useState } from "react";
import styled, { css, CSSObject } from "styled-components";
import { flexCenter } from "../components/BUI/styled";
import { ThemePrimaryBgc } from "../components/BUI/View/ThemeView";
interface IProps {
cardStyle?: CSSObject;
}
interface IData {
title: string;
article: string;
}
const data: IData[] = [
{
title: "HTML",
article: "HTML的全称为超文本标记语言",
},
{
title: "CSS",
article: "层叠样式表是一种用来表现HTML或XML等文件样式的计算机语言",
},
{
title: "REACT",
article: "用于构建用户界面的 JavaScript 库",
},
{
title: "JAVASCRIPT",
article:
"JavaScript 是一种具有函数优先的轻量级,解释型或即时编译型的编程语",
},
{
title: "TYPESCRIPT",
article: "TypeScript是微软开发的一个开源的编程语言",
},
];
const sm = 1; //小图
const smOpacity = 0.6;
const smLateX = 16;
const ms = 2; //中图
const msOpacity = 0.8; //中图
const msLateX = 8;
const lg = 3; //大图
const lgOpacity = 1;
type Obj<T> = { [key: string]: T };
const IndexObj: Obj<number> = { sm, ms, lg };
const Swipe: FC<IProps> = ({ cardStyle = {} }): ReactElement => {
const [index, setIndex] = useState(0);
const [direction, setDirection] = useState(0); //左 -1 右 1
const [list, setList] = useState(data);
const { length } = list;
const getDate = (index: number): IData =>
list[index >= length ? index - length : index];
const handleChange = (num: 1 | -1) => () => {
setIndex(index => {
const next = index + num * -1;
return next >= length ? 0 : next < 0 ? length - 1 : next;
});
setDirection(num * -1);
};
return (
<Container cardStyle={cardStyle}>
<aside>
<header />
<Main>
<ArrowIcon onClick={handleChange(-1)} />
<Card direction={direction} data={getDate(index)} zIndex={sm} />
<Card direction={direction} data={getDate(index + 1)} zIndex={ms} />
<Card direction={direction} data={getDate(index + 2)} zIndex={lg} />
<Card
direction={direction}
data={getDate(index + 3)}
zIndex={ms}
right={true}
/>
<Card
direction={direction}
data={getDate(index + 4)}
zIndex={sm}
right={true}
/>
<ArrowIcon onClick={handleChange(1)} right />
</Main>
</aside>
<aside />
</Container>
);
};
export default Swipe;
interface ICard {
zIndex: number;
right?: boolean;
data: IData;
direction: number;
}
const Card: FC<ICard> = ({ zIndex, right, data, direction }) => {
const [prev, setPrev] = useState(data);
const [move, setMove] = useState(false);
useEffect(() => {
if (prev.title !== data.title) {
setMove(true);
setTimeout(() => {
setPrev(data);
setMove(false);
}, 300);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data]);
return (
<CardStyled zIndex={zIndex} right={right} move={move} direction={direction}>
<h1>{prev.title}</h1>
<article>{prev.article}</article>
</CardStyled>
);
};
interface ICardStyled extends Omit<ICard, "data"> {
move: boolean;
}
const CardStyled = styled.div<ICardStyled>`
position: absolute;
z-index: ${({ zIndex }) => zIndex};
width: 22rem;
height: 16rem;
${({ zIndex, right }) =>
zIndex !== lg &&
css`
transform: ${() =>
`translateX(${ (zIndex === sm ? smLateX : msLateX) * (right ? 1 : -1) }rem) scale(${zIndex === ms ? msOpacity : smOpacity})`};
opacity: ${zIndex === sm ? smOpacity : msOpacity};
/* filter: brightness(0.5) */
`};
${ThemePrimaryBgc};
color: #fff;
${flexCenter};
flex-direction: column;
border-radius: 0.5rem;
article {
width: 90%;
margin: 0 auto;
text-align: center;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
/* font-size: 1.5rem; */
${({ move, zIndex, right, direction }) => {
if (move) {
if (zIndex === lg) {
return css`
transition: all 0.3s linear;
/* transition-property: opacity, transform; */
transform: ${`translateX(${msLateX * direction * -1}rem) scale(0.8)`};
opacity: ${msOpacity};
z-index: ${IndexObj.ms};
`;
} else {
const toMain =
(right && direction === 1) || (!right && direction === -1);
if (zIndex === ms) {
const next = smLateX * (right ? 1 : -1) * direction;
return css`
transition: all 0.3s linear;
transition-property: opacity, transform;
opacity: ${toMain ? lgOpacity : smOpacity};
transform: ${() =>
`translateX(${toMain ? 0 : next * (right ? -1 : 1)}rem) scale(${
toMain ? 1 : 0.6
})`};
${`z-index:${IndexObj[toMain ? "lg" : "sm"]};`};
`;
} else {
return css`
transition: all 0.3s linear;
${toMain
? `transform: translate(${
msLateX * direction
}rem) scale(0.8);opacity:${msOpacity}`
: `transform: translate(${
msLateX * direction * 2
}rem) scale(0.6);`};
`;
}
}
}
return "";
}}
`;
const d = (
<svg
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="2415"
width="32"
height="32"
>
<path
d="M378.24 512l418.88 418.88L704 1024 192 512l512-512 93.12 93.12z"
fill="#ffffff"
p-id="2416"
></path>
</svg>
);
const ArrowIcon = styled.div.attrs({
children: d,
})<{ right?: boolean }>`
padding: 3px 5px;
background-color: #0000007d;
position: absolute;
${({ right }) => (right ? "right:0;transform:rotate(-180deg)" : "left:0")};
width: 2rem;
height: 3rem;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: background-color 0.3s linear;
:hover {
background-color: #000000c8;
}
`;
const Main = styled.div`
height: 22rem;
width: 100%;
position: relative;
background-color: #ccc;
display: flex;
justify-content: center;
align-items: center;
`;
const Container = styled.div<{ cardStyle: CSSObject }>`
height: 100vh;
width: 100vw;
background-color: #fffae5;
display: flex;
aside:first-child {
flex: 2;
& > header {
height: 4rem;
width: 100%;
background: #faa;
}
}
aside:last-child {
flex: 1;
background: #eee;
}
${CardStyled} {
${({ cardStyle }) => ({ ...cardStyle })};
}
`;