feat: add animation effects and optimize homepage layout
Vercel Production Deployment / Deploy-Production (push) Successful in 2m8s
Vercel Production Deployment / Deploy-Production (push) Successful in 2m8s
This commit is contained in:
+96
-19
@@ -1,34 +1,111 @@
|
|||||||
|
"use client";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { websites } from "../data/urls";
|
import { websites } from "../data/urls";
|
||||||
import Header from "@/components/header";
|
import Header from "@/components/header";
|
||||||
import Footer from "@/components/footer";
|
import Footer from "@/components/footer";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
// 动画定义
|
||||||
|
const containerVariants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const itemVariants = {
|
||||||
|
hidden: { y: 20, opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
y: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: { type: "spring", stiffness: 100 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const textVariants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: { duration: 0.3, ease: "easeInOut" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col bg-gray-100 dark:bg-gray-900 transition-colors duration-300">
|
<div className="min-h-screen flex flex-col bg-gray-100 dark:bg-gray-900 transition-colors duration-300">
|
||||||
<Header />
|
<Header />
|
||||||
<main className="container mx-auto p-4 grow">
|
<motion.main className="container mx-auto px-4 py-8 grow" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5 }}>
|
||||||
<section className="text-center mb-12 max-w-2xl mx-auto">
|
<motion.section className="mb-16 max-w-3xl mx-auto" initial={{ opacity: 0, y: -20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.7 }}>
|
||||||
<h2 className="text-4xl font-extrabold text-gray-900 dark:text-gray-100">智能计算实验室简介</h2>
|
<motion.h2 className="text-4xl font-extrabold text-gray-900 dark:text-gray-100 mb-6 text-center" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5, delay: 0.2 }}>
|
||||||
<p className="text-xl text-gray-600 dark:text-gray-300 mt-4 text-left indent-8">
|
智能计算实验室简介
|
||||||
在物理的世界里,粒子有形形色色的活法,有的粒子很轻,终生不为人所注意;有的粒子会一鸣惊人,在湮灭时释放出大量的光;有的粒子狡黠善变,一辈子不停地改变身份,时时刻刻要成为别的粒子;但有的粒子一生稳定,怀揣着出发时得到的巨大能量,一生孤独而平静地飞行,只活在自己的轨道里。(摘自《丁肇中
|
</motion.h2>
|
||||||
伟大的是物理》,作者:李斐然)
|
<motion.p className="text-lg leading-relaxed text-gray-700 dark:text-gray-300 mt-4 indent-8 tracking-wide" variants={textVariants} initial="hidden" animate="visible">
|
||||||
</p>
|
在物理的世界里,粒子有形形色色的活法,有的粒子很轻,终生不为人所注意;有的粒子会一鸣惊人,在湮灭时释放出大量的光;有的粒子狡黠善变,一辈子不停地改变身份,时时刻刻要成为别的粒子;但有的粒子一生稳定,怀揣着出发时得到的巨大能量,一生孤独而平静地飞行,只活在自己的轨道里。
|
||||||
</section>
|
</motion.p>
|
||||||
<section className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8">
|
<motion.p className="text-right text-sm text-gray-500 dark:text-gray-400 mt-3 italic" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5, delay: 0.4 }}>
|
||||||
|
——摘自《丁肇中 伟大的是物理》,作者:李斐然
|
||||||
|
</motion.p>
|
||||||
|
</motion.section>
|
||||||
|
|
||||||
|
<motion.section
|
||||||
|
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8"
|
||||||
|
variants={containerVariants}
|
||||||
|
initial="hidden"
|
||||||
|
animate="visible"
|
||||||
|
transition={{ staggerChildren: 0.1, delayChildren: 0.3 }}
|
||||||
|
>
|
||||||
{websites.map((website, index) => (
|
{websites.map((website, index) => (
|
||||||
<a key={index} href={website.link} rel="noopener noreferrer" target="_blank" className="flex justify-center">
|
<motion.a
|
||||||
<div className="flex items-center bg-gray-50 dark:bg-gray-800 p-3 rounded-xl shadow-sm hover:shadow-xl transition-all duration-300 w-full max-w-md">
|
key={index}
|
||||||
<Image src={website.icon} alt={website.title} width={80} height={80} className="list-icon rounded-lg overflow-hidden shrink-0" />
|
href={website.link}
|
||||||
<div className="list-info shrink pl-3 pt-2 my-1 truncate w-full">
|
rel="noopener noreferrer"
|
||||||
<h3 className="list-name text-xl font-bold my-1 truncate text-gray-900 dark:text-gray-100">{website.title}</h3>
|
target="_blank"
|
||||||
<div className="text-sm text-gray-500 dark:text-gray-400 truncate">{website.description}</div>
|
className="group flex justify-center"
|
||||||
|
variants={itemVariants}
|
||||||
|
whileHover={{ scale: 1.02 }}
|
||||||
|
whileTap={{ scale: 0.98 }}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
className="flex flex-col bg-white dark:bg-gray-800 p-5 rounded-2xl shadow-md w-full max-w-md border border-gray-100 dark:border-gray-700 overflow-hidden"
|
||||||
|
whileHover={{ boxShadow: "0 10px 25px rgba(0, 0, 0, 0.1)" }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
>
|
||||||
|
<div className="flex items-center mb-4">
|
||||||
|
<motion.div
|
||||||
|
className="relative w-16 h-16 mr-4 bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-blue-100 dark:to-indigo-200 p-1 rounded-lg flex items-center justify-center"
|
||||||
|
whileHover={{ rotate: 5 }}
|
||||||
|
transition={{ type: "spring", stiffness: 300 }}
|
||||||
|
>
|
||||||
|
<Image src={website.icon} alt={website.title} width={52} height={52} className="rounded-md object-cover" />
|
||||||
|
</motion.div>
|
||||||
|
<h3 className="text-xl font-bold truncate text-gray-900 dark:text-gray-100 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors duration-300">
|
||||||
|
{website.title}
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="text-sm text-gray-600 dark:text-gray-300 line-clamp-2 leading-relaxed">{website.description}</div>
|
||||||
</a>
|
<div className="mt-3 pt-3 border-t border-gray-100 dark:border-gray-700 flex justify-between items-center">
|
||||||
|
<span className="text-xs text-gray-500 dark:text-gray-400">访问站点</span>
|
||||||
|
<motion.svg
|
||||||
|
className="w-4 h-4 text-gray-400 dark:text-gray-500 group-hover:text-blue-500 dark:group-hover:text-blue-400"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
initial={{ x: 0 }}
|
||||||
|
whileHover={{ x: 3 }}
|
||||||
|
transition={{ type: "spring", stiffness: 400 }}
|
||||||
|
>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14 5l7 7m0 0l-7 7m7-7H3" />
|
||||||
|
</motion.svg>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</motion.a>
|
||||||
))}
|
))}
|
||||||
</section>
|
</motion.section>
|
||||||
</main>
|
</motion.main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user