使用heroui完成对页面的重构
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import Image from 'next/image';
|
||||
import { Card, CardBody, Avatar, AvatarGroup } from '@heroui/react';
|
||||
import { useLanguage } from '@/contexts/LanguageContext';
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
|
||||
// 数字增长动画Hook - 可重复触发
|
||||
function useCountUp(end: number, duration: number = 1500, startRangePercent: number = 0.75) {
|
||||
@@ -93,39 +94,31 @@ function formatNumber(num: number, prefix: string = '', suffix: string = '', for
|
||||
|
||||
export default function StatsSection() {
|
||||
const { t } = useLanguage();
|
||||
const tvl = useCountUp(485, 1500, 0.80); // 从80-95%开始
|
||||
const apy = useCountUp(515, 1500, 0.85); // 5.15 * 100,从85-100%开始
|
||||
const yield_ = useCountUp(45, 1500, 0.75); // 从75-90%开始
|
||||
const users = useCountUp(23928, 1800, 0.80); // 从80-95%开始
|
||||
const { theme } = useTheme();
|
||||
const tvl = useCountUp(485, 1500, 0.80);
|
||||
const apy = useCountUp(515, 1500, 0.85);
|
||||
const yield_ = useCountUp(45, 1500, 0.75);
|
||||
const users = useCountUp(23928, 1800, 0.80);
|
||||
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [imagesVisible, setImagesVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// 客户端挂载
|
||||
setMounted(true);
|
||||
|
||||
// 延迟显示图片动画
|
||||
const timer = setTimeout(() => {
|
||||
setImagesVisible(true);
|
||||
}, 500);
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const isDark = theme === 'dark';
|
||||
return (
|
||||
<section
|
||||
className="bg-white flex flex-row items-center justify-center flex-shrink-0 w-full relative"
|
||||
style={{
|
||||
borderStyle: 'solid',
|
||||
borderColor: '#e5e7eb',
|
||||
borderWidth: '0px 0px 1px 0px',
|
||||
padding: '0px 0px 1px 0px'
|
||||
}}
|
||||
className={`flex flex-row items-center justify-center flex-shrink-0 w-full relative border-b transition-colors ${
|
||||
isDark ? 'bg-[#0a0a0a] border-[#27272a]' : 'bg-white border-[#e5e7eb]'
|
||||
}`}
|
||||
>
|
||||
{/* .section2 */}
|
||||
<div
|
||||
className="flex flex-row items-center justify-center w-[1440px] h-[183px] relative"
|
||||
>
|
||||
{/* .container4 - Grid container */}
|
||||
<div className="flex flex-row items-center justify-center w-[1440px] h-[183px] relative">
|
||||
<div
|
||||
className="flex-shrink-0 grid gap-0 relative w-full"
|
||||
style={{
|
||||
@@ -133,279 +126,140 @@ export default function StatsSection() {
|
||||
gridTemplateRows: 'repeat(1, minmax(0, 1fr))'
|
||||
}}
|
||||
>
|
||||
{/* Card 1 - Total Value Locked */}
|
||||
<div
|
||||
className="bg-white flex flex-row items-center justify-between h-[182px] relative"
|
||||
<Card
|
||||
className={`h-[182px] rounded-none border-r transition-colors ${
|
||||
isDark ? 'bg-[#0a0a0a] border-[#27272a]' : 'bg-white border-[#e5e7eb]'
|
||||
}`}
|
||||
shadow="none"
|
||||
style={{
|
||||
borderStyle: 'solid',
|
||||
borderColor: '#e5e7eb',
|
||||
borderWidth: '0px 1px 0px 0px',
|
||||
gridColumn: '1 / span 1',
|
||||
gridRow: '1 / span 1'
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-2 items-start justify-center flex-1 relative">
|
||||
<div
|
||||
className="text-[#4b5563] text-left relative font-inter"
|
||||
style={{
|
||||
fontSize: '16px',
|
||||
lineHeight: '150%',
|
||||
fontWeight: 400
|
||||
}}
|
||||
>
|
||||
<CardBody className="flex flex-col gap-2 items-start justify-center">
|
||||
<p className={`text-base font-domine transition-colors ${isDark ? 'text-[#a1a1aa]' : 'text-[#4b5563]'}`}>
|
||||
{t('stats.tvl')}
|
||||
</div>
|
||||
</p>
|
||||
<div
|
||||
ref={tvl.elementRef}
|
||||
className="text-[#111827] text-left relative font-jetbrains"
|
||||
className={`text-5xl font-extrabold font-domine transition-colors ${isDark ? 'text-[#fafafa]' : 'text-[#111827]'}`}
|
||||
style={{
|
||||
fontSize: '48px',
|
||||
lineHeight: '110%',
|
||||
letterSpacing: '-0.01em',
|
||||
fontWeight: 800
|
||||
letterSpacing: '-0.01em'
|
||||
}}
|
||||
>
|
||||
{formatNumber(tvl.count, '$', 'M+', 'M')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
{/* Card 2 - Avg. APY */}
|
||||
<div
|
||||
className="bg-white flex flex-row items-center justify-between h-[182px] relative"
|
||||
<Card
|
||||
className={`h-[182px] rounded-none border-r px-8 transition-colors ${
|
||||
isDark ? 'bg-[#0a0a0a] border-[#27272a]' : 'bg-white border-[#e5e7eb]'
|
||||
}`}
|
||||
shadow="none"
|
||||
style={{
|
||||
borderStyle: 'solid',
|
||||
borderColor: '#e5e7eb',
|
||||
borderWidth: '0px 1px 0px 0px',
|
||||
padding: '0px 32px',
|
||||
gridColumn: '2 / span 1',
|
||||
gridRow: '1 / span 1'
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-2 items-start justify-center flex-1 relative">
|
||||
<div
|
||||
className="text-[#4b5563] text-left relative font-inter"
|
||||
style={{
|
||||
fontSize: '16px',
|
||||
lineHeight: '150%',
|
||||
fontWeight: 400
|
||||
}}
|
||||
>
|
||||
<CardBody className="flex flex-col gap-2 items-start justify-center">
|
||||
<p className={`text-base font-domine transition-colors ${isDark ? 'text-[#a1a1aa]' : 'text-[#4b5563]'}`}>
|
||||
{t('stats.apy')}
|
||||
</div>
|
||||
</p>
|
||||
<div
|
||||
ref={apy.elementRef}
|
||||
className="text-[#111827] text-left relative font-jetbrains"
|
||||
className={`text-5xl font-extrabold font-domine transition-colors ${isDark ? 'text-[#fafafa]' : 'text-[#111827]'}`}
|
||||
style={{
|
||||
fontSize: '48px',
|
||||
lineHeight: '110%',
|
||||
letterSpacing: '-0.01em',
|
||||
fontWeight: 800
|
||||
letterSpacing: '-0.01em'
|
||||
}}
|
||||
>
|
||||
{(apy.count / 100).toFixed(2)}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
{/* Card 3 - Yield Captured */}
|
||||
<div
|
||||
className="bg-white flex flex-row items-center justify-between h-[182px] relative"
|
||||
<Card
|
||||
className={`h-[182px] rounded-none border-r px-8 transition-colors ${
|
||||
isDark ? 'bg-[#0a0a0a] border-[#27272a]' : 'bg-white border-[#e5e7eb]'
|
||||
}`}
|
||||
shadow="none"
|
||||
style={{
|
||||
borderStyle: 'solid',
|
||||
borderColor: '#e5e7eb',
|
||||
borderWidth: '0px 1px 0px 0px',
|
||||
padding: '0px 32px',
|
||||
gridColumn: '3 / span 1',
|
||||
gridRow: '1 / span 1'
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-2 items-start justify-center flex-1 relative">
|
||||
<div
|
||||
className="text-[#4b5563] text-left relative font-inter"
|
||||
style={{
|
||||
fontSize: '16px',
|
||||
lineHeight: '150%',
|
||||
fontWeight: 400
|
||||
}}
|
||||
>
|
||||
<CardBody className="flex flex-col gap-2 items-start justify-center">
|
||||
<p className={`text-base font-domine transition-colors ${isDark ? 'text-[#a1a1aa]' : 'text-[#4b5563]'}`}>
|
||||
{t('stats.yield')}
|
||||
</div>
|
||||
</p>
|
||||
<div
|
||||
ref={yield_.elementRef}
|
||||
className="text-[#111827] text-left relative self-stretch font-jetbrains"
|
||||
className={`text-5xl font-extrabold font-domine transition-colors ${isDark ? 'text-[#fafafa]' : 'text-[#111827]'}`}
|
||||
style={{
|
||||
fontSize: '48px',
|
||||
lineHeight: '110%',
|
||||
letterSpacing: '-0.01em',
|
||||
fontWeight: 800
|
||||
letterSpacing: '-0.01em'
|
||||
}}
|
||||
>
|
||||
{formatNumber(yield_.count, '$', 'M', 'M')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
{/* Card 4 - Connected Users */}
|
||||
<div
|
||||
className="bg-white flex flex-row items-center justify-between h-[182px] relative"
|
||||
<Card
|
||||
className={`h-[182px] rounded-none pl-8 transition-colors ${
|
||||
isDark ? 'bg-[#0a0a0a]' : 'bg-white'
|
||||
}`}
|
||||
shadow="none"
|
||||
style={{
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'transparent',
|
||||
borderWidth: '0px 1px 0px 0px',
|
||||
padding: '0px 0px 0px 32px',
|
||||
gridColumn: '4 / span 1',
|
||||
gridRow: '1 / span 1'
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-2 items-start justify-center flex-1 relative">
|
||||
<div
|
||||
className="text-[#4b5563] text-left relative font-inter"
|
||||
style={{
|
||||
fontSize: '16px',
|
||||
lineHeight: '150%',
|
||||
fontWeight: 400
|
||||
}}
|
||||
>
|
||||
<CardBody className="flex flex-col gap-2 items-start justify-center">
|
||||
<p className={`text-base font-domine transition-colors ${isDark ? 'text-[#a1a1aa]' : 'text-[#4b5563]'}`}>
|
||||
{t('stats.users')}
|
||||
</div>
|
||||
</p>
|
||||
|
||||
{/* .frame-38 - Number and avatars */}
|
||||
<div className="flex flex-row items-center justify-between self-stretch flex-shrink-0 relative">
|
||||
{/* Number */}
|
||||
<div className="flex flex-row items-center justify-between self-stretch">
|
||||
<div
|
||||
ref={users.elementRef}
|
||||
className="text-[#111827] text-left relative font-jetbrains"
|
||||
className={`text-5xl font-extrabold font-domine transition-colors ${isDark ? 'text-[#fafafa]' : 'text-[#111827]'}`}
|
||||
style={{
|
||||
fontSize: '48px',
|
||||
lineHeight: '110%',
|
||||
letterSpacing: '-0.01em',
|
||||
fontWeight: 800
|
||||
letterSpacing: '-0.01em'
|
||||
}}
|
||||
>
|
||||
{users.count.toLocaleString()}+
|
||||
</div>
|
||||
|
||||
{/* .container9 - Avatar group */}
|
||||
<div className="flex flex-row items-center justify-start flex-shrink-0 relative overflow-hidden">
|
||||
{/* Avatar 1 - image-23 */}
|
||||
<div
|
||||
className={`flex-shrink-0 w-9 h-9 relative overflow-hidden border-white transition-all duration-700 ease-out ${
|
||||
mounted && imagesVisible ? 'translate-y-0 opacity-100' : 'translate-y-12 opacity-0'
|
||||
}`}
|
||||
style={{
|
||||
borderRadius: '15239273px',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1.82px',
|
||||
padding: '1.82px',
|
||||
aspectRatio: 1,
|
||||
transitionDelay: '100ms'
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/image-230.png"
|
||||
alt="User"
|
||||
width={33}
|
||||
height={33}
|
||||
className="absolute object-cover"
|
||||
style={{
|
||||
left: '1.82px',
|
||||
top: '1.82px',
|
||||
aspectRatio: 1
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Avatar 2 - image-24 */}
|
||||
<div
|
||||
className={`flex-shrink-0 w-9 h-9 relative overflow-hidden border-white transition-all duration-700 ease-out ${
|
||||
mounted && imagesVisible ? 'translate-y-0 opacity-100' : 'translate-y-12 opacity-0'
|
||||
}`}
|
||||
style={{
|
||||
margin: '0 0 0 -8px',
|
||||
borderRadius: '15239273px',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1.82px',
|
||||
padding: '1.82px',
|
||||
aspectRatio: 1,
|
||||
transitionDelay: '200ms'
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/image-240.png"
|
||||
alt="User"
|
||||
width={36}
|
||||
height={36}
|
||||
className="absolute object-cover"
|
||||
style={{
|
||||
left: '0px',
|
||||
top: '0px',
|
||||
aspectRatio: 1
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Avatar 3 - image-25 */}
|
||||
<div
|
||||
className={`flex-shrink-0 w-9 h-9 relative overflow-hidden border-white transition-all duration-700 ease-out ${
|
||||
mounted && imagesVisible ? 'translate-y-0 opacity-100' : 'translate-y-12 opacity-0'
|
||||
}`}
|
||||
style={{
|
||||
margin: '0 0 0 -8px',
|
||||
borderRadius: '15239273px',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1.82px',
|
||||
padding: '1.82px',
|
||||
aspectRatio: 1,
|
||||
transitionDelay: '300ms'
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/image-250.png"
|
||||
alt="User"
|
||||
width={40}
|
||||
height={40}
|
||||
className="absolute object-cover"
|
||||
style={{
|
||||
left: '6px',
|
||||
top: '0.5px',
|
||||
aspectRatio: 1
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Avatar 4 - image-252 */}
|
||||
<div
|
||||
className={`flex-shrink-0 w-9 h-9 relative overflow-hidden border-white transition-all duration-700 ease-out ${
|
||||
mounted && imagesVisible ? 'translate-y-0 opacity-100' : 'translate-y-12 opacity-0'
|
||||
}`}
|
||||
style={{
|
||||
margin: '0 0 0 -8px',
|
||||
borderRadius: '15239273px',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1.82px',
|
||||
padding: '1.82px',
|
||||
aspectRatio: 1,
|
||||
transitionDelay: '400ms'
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/image-251.png"
|
||||
alt="User"
|
||||
width={36}
|
||||
height={36}
|
||||
className="absolute object-cover"
|
||||
style={{
|
||||
left: '0px',
|
||||
top: '0px',
|
||||
aspectRatio: 1
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<AvatarGroup
|
||||
max={4}
|
||||
className={`transition-all duration-700 ease-out ${
|
||||
mounted && imagesVisible ? 'translate-y-0 opacity-100' : 'translate-y-12 opacity-0'
|
||||
}`}
|
||||
>
|
||||
<Avatar
|
||||
src="/image-230.png"
|
||||
size="sm"
|
||||
/>
|
||||
<Avatar
|
||||
src="/image-240.png"
|
||||
size="sm"
|
||||
/>
|
||||
<Avatar
|
||||
src="/image-250.png"
|
||||
size="sm"
|
||||
/>
|
||||
<Avatar
|
||||
src="/image-251.png"
|
||||
size="sm"
|
||||
/>
|
||||
</AvatarGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user