增加过度动画

This commit is contained in:
小朱
2025-07-10 12:54:04 +08:00
parent e72425c1ff
commit f0c2f52233
5 changed files with 127 additions and 21 deletions

View File

@@ -15,6 +15,7 @@
"antd": "^5.15.0",
"axios": "^1.6.8",
"clsx": "^2.0.0",
"framer-motion": "^11.0.0",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.363.0",
"monaco-editor": "^0.47.0",
@@ -3213,6 +3214,33 @@
"url": "https://github.com/sponsors/rawify"
}
},
"node_modules/framer-motion": {
"version": "11.18.2",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
"integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==",
"license": "MIT",
"dependencies": {
"motion-dom": "^11.18.1",
"motion-utils": "^11.18.1",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -3970,6 +3998,21 @@
"integrity": "sha512-VabVvHvQ9QmMwXu4du008ZDuyLnHs9j7ThVFsiJoXSOQk18+LF89N4ADzPbFenm0W4V2bGHnFBztIRQTgBfxzw==",
"license": "MIT"
},
"node_modules/motion-dom": {
"version": "11.18.1",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
"integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==",
"license": "MIT",
"dependencies": {
"motion-utils": "^11.18.1"
}
},
"node_modules/motion-utils": {
"version": "11.18.1",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz",
"integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -5788,6 +5831,12 @@
"dev": true,
"license": "Apache-2.0"
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",

View File

@@ -14,6 +14,7 @@
"antd": "^5.15.0",
"axios": "^1.6.8",
"clsx": "^2.0.0",
"framer-motion": "^11.0.0",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.363.0",
"monaco-editor": "^0.47.0",

View File

@@ -4,6 +4,7 @@ import { ConfigProvider, theme } from 'antd'
import { useAuthStore } from '@/stores/authStore'
import { useThemeStore } from '@/stores/themeStore'
import Layout from '@/components/Layout'
import PageTransition from '@/components/PageTransition'
import LoginPage from '@/pages/LoginPage'
import HomePage from '@/pages/HomePage'
import TerminalPage from '@/pages/TerminalPage'
@@ -97,14 +98,14 @@ function App() {
<ProtectedRoute>
<Layout>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/terminal" element={<TerminalPage />} />
<Route path="/instances" element={<InstanceManagerPage />} />
<Route path="/game-deployment" element={<GameDeploymentPage />} />
<Route path="/scheduled-tasks" element={<ScheduledTasksPage />} />
<Route path="/files" element={<FileManagerPage />} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/about" element={<AboutProjectPage />} />
<Route path="/" element={<PageTransition><HomePage /></PageTransition>} />
<Route path="/terminal" element={<PageTransition><TerminalPage /></PageTransition>} />
<Route path="/instances" element={<PageTransition><InstanceManagerPage /></PageTransition>} />
<Route path="/game-deployment" element={<PageTransition><GameDeploymentPage /></PageTransition>} />
<Route path="/scheduled-tasks" element={<PageTransition><ScheduledTasksPage /></PageTransition>} />
<Route path="/files" element={<PageTransition><FileManagerPage /></PageTransition>} />
<Route path="/settings" element={<PageTransition><SettingsPage /></PageTransition>} />
<Route path="/about" element={<PageTransition><AboutProjectPage /></PageTransition>} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</Layout>

View File

@@ -78,7 +78,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
</div>
{/* 导航菜单 */}
<nav className="flex-1 px-4 py-6 space-y-2">
<nav className="flex-1 px-3 py-5 space-y-1.5">
{navigation.map((item) => {
const isActive = location.pathname === item.href
return (
@@ -87,14 +87,15 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
to={item.href}
onClick={() => setSidebarOpen(false)}
className={`
flex items-center space-x-3 px-4 py-3 rounded-lg transition-all duration-200
flex items-center space-x-2.5 px-3 py-2.5 rounded-lg transition-all duration-300 ease-in-out transform
hover:scale-105 hover:shadow-md active:scale-95
${isActive
? 'bg-blue-600/20 text-blue-600 dark:text-blue-400 border border-blue-500/30 shadow-lg'
? 'bg-blue-600/20 text-blue-600 dark:text-blue-400 border border-blue-500/30 shadow-lg scale-105'
: 'text-black dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-white/10 hover:text-black dark:hover:text-white'
}
`}
>
<item.icon className="w-5 h-5" />
<item.icon className="w-4 h-4" />
<span className="font-medium">{item.name}</span>
</Link>
)
@@ -106,12 +107,12 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
{/* 主题切换 */}
<button
onClick={toggleTheme}
className="flex items-center space-x-3 w-full px-4 py-2 text-black dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-white/10 hover:text-black dark:hover:text-white rounded-lg transition-all duration-200"
className="flex items-center space-x-2.5 w-full px-3 py-2.5 text-black dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-white/10 hover:text-black dark:hover:text-white rounded-lg transition-all duration-300 ease-in-out transform hover:scale-105 hover:shadow-md active:scale-95"
>
{theme === 'dark' ? (
<Sun className="w-5 h-5" />
<Sun className="w-4 h-4" />
) : (
<Moon className="w-5 h-5" />
<Moon className="w-4 h-4" />
)}
<span className="font-medium">
{theme === 'dark' ? '浅色模式' : '深色模式'}
@@ -119,9 +120,9 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
</button>
{/* 用户信息 */}
<div className="flex items-center space-x-3 px-4 py-2 bg-gray-100 dark:bg-white/5 rounded-lg">
<div className="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center">
<User className="w-4 h-4 text-white" />
<div className="flex items-center space-x-2.5 px-3 py-2.5 bg-gray-100 dark:bg-white/5 rounded-lg transition-all duration-300 ease-in-out transform hover:scale-105 hover:shadow-md">
<div className="w-7 h-7 bg-blue-600 rounded-full flex items-center justify-center">
<User className="w-3.5 h-3.5 text-white" />
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-black dark:text-white truncate">
@@ -136,9 +137,9 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
{/* 登出按钮 */}
<button
onClick={handleLogout}
className="flex items-center space-x-3 w-full px-4 py-2 text-red-400 hover:bg-red-500/10 hover:text-red-300 rounded-lg transition-all duration-200"
className="flex items-center space-x-2.5 w-full px-3 py-2.5 text-red-400 hover:bg-red-500/10 hover:text-red-300 rounded-lg transition-all duration-300 ease-in-out transform hover:scale-105 hover:shadow-md active:scale-95"
>
<LogOut className="w-5 h-5" />
<LogOut className="w-4 h-4" />
<span className="font-medium"></span>
</button>
</div>
@@ -172,7 +173,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
</div>
{/* 页面内容 */}
<main className="p-6">
<main className="p-6 relative overflow-hidden">
{children}
</main>
</div>

View File

@@ -0,0 +1,54 @@
import React from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { useLocation } from 'react-router-dom'
interface PageTransitionProps {
children: React.ReactNode
}
const pageVariants = {
initial: {
opacity: 0,
y: 20,
scale: 0.95
},
in: {
opacity: 1,
y: 0,
scale: 1
},
out: {
opacity: 0,
y: -20,
scale: 0.95
}
}
const pageTransition = {
type: 'tween',
ease: [0.25, 0.46, 0.45, 0.94],
duration: 0.3
}
const PageTransition: React.FC<PageTransitionProps> = ({ children }) => {
const location = useLocation()
return (
<AnimatePresence mode="wait" initial={false}>
<motion.div
key={location.pathname}
initial="initial"
animate="in"
exit="out"
variants={pageVariants}
transition={pageTransition}
className="w-full h-full min-h-0"
style={{ position: 'relative' }}
>
{children}
</motion.div>
</AnimatePresence>
)
}
export default PageTransition