Skip to content

Commit 8bb9d08

Browse files
committed
feat: move StarryBackground to layout and add parallax effect on mouse movement
1 parent 4604f93 commit 8bb9d08

File tree

4 files changed

+73
-33
lines changed

4 files changed

+73
-33
lines changed

src/app/layout.jsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Footer from '../components/Footer'
99
import 'nextra-theme-docs/style.css'
1010
import { GoogleAnalytics } from '@next/third-parties/google'
1111
import Image from 'next/image'
12+
import StarryBackground from '../components/StarryBackground'
1213

1314
export const openGraphImage = 'https://storage.googleapis.com/webstatics.ii.inc/TLE_OGImage.png'
1415

@@ -78,19 +79,22 @@ export default async function RootLayout({ children }) {
7879
<html lang="en" dir="ltr" suppressHydrationWarning>
7980
<Head/>
8081
<body className={`${montserrat.variable} ${nunito_sans.variable}`}>
81-
<Layout
82-
// banner={<Banner storageKey="Nextra 2">Nextra 2 Alpha</Banner>}
83-
navbar={navbar}
84-
editLink="Edit this page on GitHub"
85-
docsRepositoryBase="https://github.com/Intelligent-Internet/Symbioism-Nextra"
86-
sidebar={{ defaultMenuCollapseLevel: 1 }}
87-
pageMap={pageMap}
88-
nextThemes={{ forcedTheme: 'dark' }}
89-
darkMode={false}
90-
>
91-
<main>{children}</main>
92-
</Layout>
93-
<Footer />
82+
<StarryBackground starCount={200} />
83+
<div className="relative z-10">
84+
<Layout
85+
// banner={<Banner storageKey="Nextra 2">Nextra 2 Alpha</Banner>}
86+
navbar={navbar}
87+
editLink="Edit this page on GitHub"
88+
docsRepositoryBase="https://github.com/Intelligent-Internet/Symbioism-Nextra"
89+
sidebar={{ defaultMenuCollapseLevel: 1 }}
90+
pageMap={pageMap}
91+
nextThemes={{ forcedTheme: 'dark' }}
92+
darkMode={false}
93+
>
94+
<main>{children}</main>
95+
</Layout>
96+
<Footer />
97+
</div>
9498
<GoogleAnalytics gaId="G-00VHQ8C00W" />
9599
</body>
96100
</html>

src/app/page.jsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,11 @@ import { Podcast, CloudDownload, BookOpen, Github } from 'lucide-react';
55
import TripodGraphic from '../components/TripodGraphic';
66
import FlywheelGraphic from '../components/FlywheelGraphic';
77
import QuoteBlock from '../components/QuoteBlock';
8-
import StarryBackground from '../components/StarryBackground';
98

109
export default function IndexPage() {
1110
return (
1211
<div className="relative">
13-
{/* Main Background */}
14-
<div className="absolute inset-0 z-0">
15-
<StarryBackground starCount={200} />
16-
</div>
12+
{/* Background is globally rendered in layout.jsx */}
1713

1814

1915
<div className="relative z-10">

src/app/symbioism/page.jsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,11 @@ import { Brain, Users, Zap, DraftingCompass, Layers, Layers2, Bot, User, GitComm
55
import TripodGraphic from '../../components/TripodGraphic';
66
import FlywheelGraphic from '../../components/FlywheelGraphic';
77
import QuoteBlock from '../../components/QuoteBlock';
8-
import StarryBackground from '../../components/StarryBackground';
98

109
export default function IndexPage() {
1110
return (
1211
<div className="relative">
13-
{/* Main Background */}
14-
<div className="absolute inset-0 z-0">
15-
<StarryBackground starCount={240} />
16-
</div>
12+
{/* Background is globally rendered in layout.jsx */}
1713

1814
{/* Hero Section */}
1915
<div className="relative z-10 pt-32 sm:pt-32">

src/components/StarryBackground.jsx

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1-
'use client';
1+
"use client";
2+
import React, { useEffect, useState, useRef } from 'react';
3+
import { usePathname } from 'next/navigation';
24

3-
import React, { useEffect, useState } from 'react';
5+
function StarryBackground({ starCount = 240 }) {
6+
const pathname = usePathname();
7+
// 在 docs 路由下不渲染背景
8+
if (pathname && pathname.startsWith('/docs')) {
9+
return null;
10+
}
411

5-
const StarryBackground = ({ starCount = 240 }) => {
612
const [stars, setStars] = useState([]);
13+
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
714
const [isMobile, setIsMobile] = useState(false);
15+
const containerRef = useRef(null);
816

917
// 检测是否为移动端
1018
useEffect(() => {
@@ -28,7 +36,7 @@ const StarryBackground = ({ starCount = 240 }) => {
2836
newStars.push({
2937
id: i,
3038
x: Math.random() * 100, // 0-100%
31-
y: Math.pow(Math.random(), 2) * 100, // Skew distribution towards the top
39+
y: Math.random() * 100, // 0-100%
3240
layer: Math.floor(Math.random() * 3) + 1, // 1, 2, 3 三层
3341
});
3442
}
@@ -39,6 +47,42 @@ const StarryBackground = ({ starCount = 240 }) => {
3947
generateStars();
4048
}, [starCount, isMobile]); // 添加isMobile作为依赖项
4149

50+
// 监听鼠标移动
51+
useEffect(() => {
52+
const handleMouseMove = (e) => {
53+
if (containerRef.current) {
54+
const rect = containerRef.current.getBoundingClientRect();
55+
const centerX = rect.width / 2;
56+
const centerY = rect.height / 2;
57+
58+
// 计算鼠标相对于容器中心的位置
59+
const x = (e.clientX - rect.left - centerX) / centerX;
60+
const y = (e.clientY - rect.top - centerY) / centerY;
61+
62+
setMousePosition({ x, y });
63+
}
64+
};
65+
66+
window.addEventListener('mousemove', handleMouseMove);
67+
return () => window.removeEventListener('mousemove', handleMouseMove);
68+
}, []);
69+
70+
// 计算星星的位移
71+
const getStarTransform = (star) => {
72+
// 不同层级的鼠标位移系数(与鼠标移动方向相反)
73+
const mouseLayerMultipliers = {
74+
1: 1, // 最上层,位移最小
75+
2: 2, // 中间层
76+
3: 4, // 最下层,位移最大
77+
};
78+
const mouseMultiplier = mouseLayerMultipliers[star.layer] ?? 1;
79+
80+
const offsetX = -mousePosition.x * mouseMultiplier;
81+
const offsetY = -mousePosition.y * mouseMultiplier;
82+
83+
return `translate(${offsetX}px, ${offsetY}px)`;
84+
};
85+
4286
// 根据层级获取星星的透明度和大小
4387
const getStarStyle = (star) => {
4488
const layerStyles = {
@@ -47,21 +91,21 @@ const StarryBackground = ({ starCount = 240 }) => {
4791
3: { opacity: 1, size: '4px' }, // 最下层,最亮
4892
};
4993

50-
const style = layerStyles[star.layer];
94+
const style = layerStyles[star.layer] ?? layerStyles[1];
5195

5296
return {
5397
opacity: style.opacity,
5498
width: style.size,
5599
height: style.size,
56-
// 保持静止:不使用 transform/transition
57-
transform: 'none',
58-
transition: 'none',
100+
transform: getStarTransform(star),
101+
transition: 'transform 0.1s ease-out',
59102
};
60103
};
61104

62105
return (
63106
<div
64-
className="absolute top-0 left-0 w-full h-full z-0 overflow-hidden bg-[#191E1B]"
107+
ref={containerRef}
108+
className="fixed top-0 left-0 w-full h-full z-0 pointer-events-none overflow-hidden bg-[#191E1B]"
65109
>
66110
{stars.map((star) => (
67111
<div
@@ -76,6 +120,6 @@ const StarryBackground = ({ starCount = 240 }) => {
76120
))}
77121
</div>
78122
);
79-
};
123+
}
80124

81125
export default StarryBackground;

0 commit comments

Comments
 (0)