forked from 77media/video-flow
265 lines
6.4 KiB
TypeScript
265 lines
6.4 KiB
TypeScript
import React from "react";
|
||
|
||
interface TemplateCardProps {
|
||
/** 图片URL */
|
||
imageUrl: string;
|
||
/** 图片alt文本 */
|
||
imageAlt?: string;
|
||
/** 标题 */
|
||
title: string;
|
||
/** 描述文字 */
|
||
description: string;
|
||
/** 是否选中,默认false */
|
||
isSelected?: boolean;
|
||
/** 卡片宽度,默认150px */
|
||
width?: number;
|
||
/** 卡片高度,默认200px */
|
||
height?: number;
|
||
}
|
||
|
||
/**
|
||
* 3D翻转卡片组件
|
||
* 正面显示图片,背面显示标题和描述
|
||
*/
|
||
const TemplateCard: React.FC<TemplateCardProps> = ({
|
||
imageUrl,
|
||
imageAlt = "",
|
||
title,
|
||
description,
|
||
isSelected = false,
|
||
width = 150,
|
||
height = 200,
|
||
}) => {
|
||
return (
|
||
<div
|
||
className={`card ${isSelected ? "selected" : ""}`}
|
||
style={{ width: `${width}px`, height: `${height}px` }}
|
||
data-alt="template-card"
|
||
>
|
||
<div className="card-container">
|
||
{/* 背面 - 显示图片 */}
|
||
<div className="card-back">
|
||
<div className="back-image-wrapper">
|
||
<img src={imageUrl} alt={imageAlt} className="back-image" />
|
||
</div>
|
||
</div>
|
||
|
||
{/* 正面 - 显示文字和流光效果 */}
|
||
<div className="card-front">
|
||
<div className="floating-circle floating-circle-1"></div>
|
||
<div className="floating-circle floating-circle-2"></div>
|
||
<div className="floating-circle floating-circle-3"></div>
|
||
<div className="front-content-overlay">
|
||
<div className="free-badge">Free</div>
|
||
<div className="text-content">
|
||
<h3 className="card-title">{title}</h3>
|
||
<p className="card-description">{description}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<style jsx>{`
|
||
.card {
|
||
overflow: visible;
|
||
}
|
||
|
||
.card-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
transform-style: preserve-3d;
|
||
transition: transform 300ms;
|
||
box-shadow: 0px 0px 10px 1px #000000ee;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.card-front,
|
||
.card-back {
|
||
background-color: #151515;
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
backface-visibility: hidden;
|
||
-webkit-backface-visibility: hidden;
|
||
border-radius: 5px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.card-back {
|
||
width: 100%;
|
||
height: 100%;
|
||
justify-content: center;
|
||
display: flex;
|
||
align-items: center;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.card-back::before {
|
||
position: absolute;
|
||
content: " ";
|
||
display: block;
|
||
width: 160px;
|
||
height: 160%;
|
||
background: linear-gradient(
|
||
90deg,
|
||
transparent,
|
||
rgb(106, 244, 249),
|
||
rgb(199, 59, 255),
|
||
rgb(106, 244, 249),
|
||
rgb(199, 59, 255),
|
||
transparent
|
||
);
|
||
animation: rotation_481 5000ms infinite linear;
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
}
|
||
|
||
.card.selected .card-back::before {
|
||
opacity: 1;
|
||
}
|
||
|
||
.back-image-wrapper {
|
||
position: absolute;
|
||
width: 99%;
|
||
height: 99%;
|
||
background-color: #151515;
|
||
border-radius: 5px;
|
||
color: white;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
padding: 0px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.free-badge {
|
||
background: linear-gradient(
|
||
135deg,
|
||
rgb(106, 244, 249),
|
||
rgb(199, 59, 255)
|
||
);
|
||
color: white;
|
||
padding: 3px 10px;
|
||
border-radius: 10px;
|
||
font-size: 10px;
|
||
font-weight: bold;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.text-content {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
gap: 15px;
|
||
}
|
||
|
||
.card-title {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
margin: 0;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.card-description {
|
||
font-size: 12px;
|
||
line-height: 1.4;
|
||
margin: 0;
|
||
color: #cccccc;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.card:hover .card-container {
|
||
transform: rotateY(180deg);
|
||
}
|
||
|
||
@keyframes rotation_481 {
|
||
0% {
|
||
transform: rotateZ(0deg);
|
||
}
|
||
100% {
|
||
transform: rotateZ(360deg);
|
||
}
|
||
}
|
||
|
||
.card-front {
|
||
transform: rotateY(180deg);
|
||
color: white;
|
||
}
|
||
|
||
.front-content-overlay {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(21, 21, 21, 0.6);
|
||
backdrop-filter: blur(10px);
|
||
border-radius: 5px;
|
||
color: white;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
padding: 1rem;
|
||
box-sizing: border-box;
|
||
z-index: 100;
|
||
}
|
||
|
||
.back-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
object-position: center;
|
||
}
|
||
|
||
.floating-circle {
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 50%;
|
||
background-color: #ffbb66;
|
||
position: absolute;
|
||
filter: blur(12px);
|
||
animation: floating 2600ms infinite linear;
|
||
}
|
||
|
||
.floating-circle-2 {
|
||
background-color: #ff8866;
|
||
left: 30px;
|
||
top: 70px;
|
||
width: 100px;
|
||
height: 100px;
|
||
animation-delay: -800ms;
|
||
}
|
||
|
||
.floating-circle-3 {
|
||
background-color: #ff2233;
|
||
left: 100px;
|
||
top: 30px;
|
||
width: 20px;
|
||
height: 20px;
|
||
animation-delay: -1800ms;
|
||
}
|
||
|
||
@keyframes floating {
|
||
0% {
|
||
transform: translateY(0px);
|
||
}
|
||
50% {
|
||
transform: translateY(10px);
|
||
}
|
||
100% {
|
||
transform: translateY(0px);
|
||
}
|
||
}
|
||
`}</style>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default TemplateCard;
|