我尝试创建卡片组件和背景渐变以及动画。但它与提供的 gif 相差甚远。如何创建平滑的动画和精确的渐变,并使卡片如图所示弹出?
[注意:请点击gif,它可能无法在问题部分正确呈现”]
import { useCallback, useEffect, useRef, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import Card from "../Card";
import demoCardData from "../../constants/cardData";
export const ScrollableCardList = ({
cardData: propCardData,
}: {
cardData?: Array<any>;
}) => {
const data = propCardData || demoCardData;
const containerRef = useRef<HTMLDivElement>(null);
const [activeIndex, setActiveIndex] = useState(0);
const handleScroll = useCallback(() => {
if (!containerRef.current) return;
const scrollTop = containerRef.current.scrollTop;
const scrollHeight = containerRef.current.scrollHeight;
const clientHeight = containerRef.current.clientHeight;
const scrollRatio = scrollTop / (scrollHeight - clientHeight);
const newActiveIndex = Math.floor(scrollRatio * data.length);
setActiveIndex(newActiveIndex);
}, [data]);
useEffect(() => {
if (!containerRef.current) return;
containerRef.current.addEventListener("scroll", handleScroll);
return () => {
if (!containerRef.current) return;
containerRef.current.removeEventListener("scroll", handleScroll);
};
}, [handleScroll]);
return (
<div
ref={containerRef}
className="flex flex-col items-center h-screen overflow-auto"
style={{ scrollBehavior: "smooth" }}
>
{data.map((card, index) => (
<Card
key={uuidv4()}
imageUrl={card.imageUrl}
avatarImageUrl={card.avatarImageUrl}
titleText={card.titleText}
subTitleText={card.subTitleText}
isActive={index === activeIndex}
/>
))}
</div>
);
};
import { Circular, Rectangular, SubTitle, Title } from "./placeholders";
type CardProps = {
isActive?: boolean;
cardContainerStyles?: string;
rectangleContainerStyles?: string;
circleContainerStyles?: string;
imageUrl?: string;
avatarImageUrl?: string;
titleText?: string;
subTitleText?: string;
};
const Card = ({
isActive,
cardContainerStyles,
rectangleContainerStyles,
circleContainerStyles,
imageUrl,
avatarImageUrl,
titleText,
subTitleText,
}: CardProps) => {
const activeClass = isActive ? "scale-110" : "";
// Ensure transition-transform is part of the base class string
const cardBaseStyles = "transition-transform duration-300 ease-in-out";
const cardDynamicStyles =
cardContainerStyles ||
"w-80 min-h-80 rounded-2xl shadow-4xl p-4 my-4 bg-gradient-to-br from-rose-500 to-rose-400";
const cardStyles = `${cardBaseStyles} ${activeClass} ${cardDynamicStyles}`;
const rectangleStyles =
rectangleContainerStyles ||
"bg-gradient-to-br from-rose-400 to-[#db7483] w-[100%] h-36 rounded-2xl shadow-2xl";
const circleStyles =
circleContainerStyles ||
"w-12 h-12 rounded-full bg-gradient-to-br from-rose-400 to-rose-300 shadow-md";
return (
<div className={cardStyles}>
<Rectangular rectangleStyles={rectangleStyles} imageUrl={imageUrl} />
<div className="flex items-center mt-5">
<Circular circleStyles={circleStyles} avatarImageUrl={avatarImageUrl} />
<div className="flex-grow ml-4">
<Title titleText={titleText} />
<SubTitle subTitleText={subTitleText} />
</div>
</div>
</div>
);
};
export default Card;
我应该使用动画库吗?如何创建精确的渐变、弹出效果、动画等?有人可以指导我完成整个过程吗?如果有人提供了很好的解决方案,如果有人指导我就更好了。
谢谢你。
这里是codesandbox链接:https://codesandbox.io/p/github/Habi-Thapa/rockandscroll/main?import=true
为了实现您所描述的效果,您可以根据 isActive 属性动态修改 Card 组件样式,该属性指示当前是否是焦点卡。您还可以在卡片转变为焦点和失焦时为其添加平滑滚动和不透明效果。
以下是您可能对 ScrollableCardList 组件和 Card 组件进行的更改的实现概要。请记住,虽然此示例只是一个起点,但您可能希望改进类型以提高代码的清晰度和可读性。
对于ScrollableCardList组件:
更新handleScroll函数来确定最接近容器中心的卡片,而不是使用滚动比率。 添加额外的状态来处理卡片大小和不透明度值。
更新了 ScrollableCardList 代码:
// ... import and setup continues
export const ScrollableCardList = ({
cardData: propCardData,
}: {
cardData?: Array<any>;
}) => {
const data = propCardData || demoCardData;
const containerRef = useRef<HTMLDivElement>(null);
const [activeIndex, setActiveIndex] = useState(0);
const [cardStyles, setCardStyles] = useState<Array<any>>([]);
const updateCardStyles = useCallback(() => {
if (!containerRef.current) return;
const cardElements = containerRef.current.children;
const cardElementsArray = Array.from(cardElements);
const containerCenter = containerRef.current.clientHeight / 2;
const newCardStyles = cardElementsArray.map((cardEl, index) => {
const cardRect = cardEl.getBoundingClientRect();
const cardCenter = cardRect.top + cardRect.height / 2;
const distanceFromCenter = Math.abs(containerCenter - cardCenter);
const scale = 1 - Math.min(distanceFromCenter / containerCenter, 0.5); // Scale down max to 0.5
return {
scale: 0.5 + scale * 0.5, // Scale range: [0.5, 1]
opacity: 0.5 + scale * 0.5, // Opacity range: [0.5, 1]
};
});
setCardStyles(newCardStyles);
}, []);
useEffect(() => {
if (!containerRef.current) return;
updateCardStyles();
containerRef.current.addEventListener("scroll", updateCardStyles);
return () => {
if (!containerRef.current) return;
containerRef.current.removeEventListener('scroll', updateCardStyles);
};
}, [updateCardStyles]);
return (
<div
ref={containerRef}
className="flex flex-col items-center h-screen overflow-auto"
style={{ scrollBehavior: "smooth" }}
>
{data.map((card, index) => (
<Card
key={uuidv4()}
imageUrl={card.imageUrl}
avatarImageUrl={card.avatarImageUrl}
titleText={card.titleText}
subTitleText={card.subTitleText}
style={{
transform: `scale(${cardStyles[index]?.scale || 1})`,
opacity: cardStyles[index]?.opacity || 1,
}}
/>
))}
</div>
);
};
对于卡片组件:
修改组件以接受样式作为道具,并将这些样式应用到主卡片元素。
以下是更新卡片组件以接受新样式的方法:
// ... import and setup continues
const Card = ({ imageUrl, avatarImageUrl, titleText, subTitleText,style }) => {
return (
<div className="card-container" style={{ ...style,
transition: 'transform 0.3s, opacity 0.3s' }}>
{/* Card content goes here */}
</div>
);
};
export default Card;
为了让您有更好的视觉理解,我创建了您的 CodeSandbox 的一个分支,其中包含已实施的更改。您可以在此处查看修改后的示例并与之交互:CodeSandbox Demo
确保根据您想要的数据结构和样式适当修改现有代码,调整过渡速度和其他值以最适合所需的动画平滑度和视觉效果。
我希望这可以帮助您推进项目。