// src/components/VantaHaloBackground.jsx 'use client'; import React, { useRef, useEffect, memo } from 'react' import dynamic from 'next/dynamic' import * as THREE from 'three' interface VantaHaloBackgroundProps { onLoaded?: () => void; } // 预加载 Vanta 脚本 const preloadVantaScript = () => { const link = document.createElement('link') link.rel = 'preload' link.as = 'script' link.href = '/lib/vanta.halo.min.js' // 确保路径正确 document.head.appendChild(link) } // 使用 React.memo 来避免不必要的重渲染 const VantaHaloBackground = memo(({ onLoaded }: VantaHaloBackgroundProps) => { const vantaRef = useRef(null) const effectInstance = useRef(null) const frameId = useRef() useEffect(() => { let canceled = false preloadVantaScript() const loadVanta = async () => { try { const VANTA = await import('../lib/vanta.halo.min.js') if (canceled || !vantaRef.current || effectInstance.current) return // 使用 requestAnimationFrame 来控制动画帧率 const animate = () => { if (effectInstance.current) { effectInstance.current.frameRequestId = requestAnimationFrame(animate) } } effectInstance.current = VANTA.default({ el: vantaRef.current, THREE, mouseControls: true, touchControls: true, gyroControls: false, scale: 1.0, scaleMobile: 1.0, amplitudeFactor: 1.5, ringFactor: 1.3, size: 1.2, minHeight: 200.00, minWidth: 200.00, // 优化渲染性能的参数 fps: 30, // 限制帧率 renderCacheSize: 4, // 缓存大小 }) frameId.current = requestAnimationFrame(animate) // 通知加载完成 if (onLoaded) { onLoaded(); } } catch (error) { console.error('Failed to load Vanta effect:', error) } } // 使用 requestIdleCallback 在浏览器空闲时初始化 if ('requestIdleCallback' in window) { requestIdleCallback(() => loadVanta(), { timeout: 2000 }) } else { setTimeout(loadVanta, 100) } return () => { canceled = true if (frameId.current) { cancelAnimationFrame(frameId.current) } if (effectInstance.current) { effectInstance.current.destroy() effectInstance.current = null } } }, [onLoaded]) return (
) }) VantaHaloBackground.displayName = 'VantaHaloBackground' export default VantaHaloBackground