From daba1bc1132cfda38dcb8237140f0bfd9cdf1d52 Mon Sep 17 00:00:00 2001 From: Ember <197652334@qq.com> Date: Sat, 1 Nov 2025 23:45:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E8=90=BD=E5=9C=B0=E9=A1=B5?= =?UTF-8?q?=E2=80=9C=E5=90=AF=E5=8A=A8=E8=BE=93=E5=87=BA=E2=80=9D=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E6=89=93=E5=AD=97=E6=9C=BA=E6=95=88=E6=9E=9C=EF=BC=88?= =?UTF-8?q?Typewriter=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/LandingPage.tsx | 22 +++++++---- web/src/components/Typewriter.tsx | 63 ++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 web/src/components/Typewriter.tsx diff --git a/web/src/components/LandingPage.tsx b/web/src/components/LandingPage.tsx index ee8064f3..0e8a2d4c 100644 --- a/web/src/components/LandingPage.tsx +++ b/web/src/components/LandingPage.tsx @@ -15,6 +15,7 @@ import { Cpu, } from 'lucide-react' import { CryptoFeatureCard } from './CryptoFeatureCard' +import Typewriter from './Typewriter' // Animation variants const fadeInUp = { @@ -515,14 +516,19 @@ export function LandingPage() { boxShadow: '0 20px 60px rgba(240, 185, 11, 0.2)', }} > -
-                  {`$ git clone https://github.com/...
-$ docker compose up -d
-🚀 NOFX 已启动
-✓ AI 代理运行中
-✓ 交易所已连接
-✓ 策略已激活`}
-                
+ diff --git a/web/src/components/Typewriter.tsx b/web/src/components/Typewriter.tsx new file mode 100644 index 00000000..e8e33349 --- /dev/null +++ b/web/src/components/Typewriter.tsx @@ -0,0 +1,63 @@ +import { useEffect, useRef, useState } from 'react' + +interface TypewriterProps { + lines: string[] + typingSpeed?: number // 毫秒/字符 + lineDelay?: number // 每行结束的额外等待 + className?: string +} + +export default function Typewriter({ + lines, + typingSpeed = 25, + lineDelay = 400, + className, +}: TypewriterProps) { + const [text, setText] = useState('') + const [showCursor, setShowCursor] = useState(true) + const lineIndexRef = useRef(0) + const charIndexRef = useRef(0) + const timerRef = useRef(null) + const blinkRef = useRef(null) + + useEffect(() => { + function typeNext() { + const currentLine = lines[lineIndexRef.current] ?? '' + if (charIndexRef.current < currentLine.length) { + setText((t) => t + currentLine[charIndexRef.current]) + charIndexRef.current += 1 + timerRef.current = window.setTimeout(typeNext, typingSpeed) + } else { + // 行结束 + if (lineIndexRef.current < lines.length - 1) { + setText((t) => t + '\n') + lineIndexRef.current += 1 + charIndexRef.current = 0 + timerRef.current = window.setTimeout(typeNext, lineDelay) + } else { + // 最后一行输入完毕 + timerRef.current = null + } + } + } + + typeNext() + + // 光标闪烁 + blinkRef.current = window.setInterval(() => { + setShowCursor((v) => !v) + }, 500) + + return () => { + if (timerRef.current) window.clearTimeout(timerRef.current) + if (blinkRef.current) window.clearInterval(blinkRef.current) + } + }, [lines, typingSpeed, lineDelay]) + + return ( +
+      {text}
+      
+    
+ ) +}