video-flow-b/components/vanta-halo-background.tsx
2025-06-30 12:29:35 +08:00

139 lines
3.8 KiB
TypeScript

// 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;
}
// Add VANTA to the window type
declare global {
interface Window {
VANTA: any;
}
}
// 预加载 Vanta 脚本
const preloadVantaScript = () => {
const link = document.createElement('link')
link.rel = 'preload'
link.as = 'script'
link.href = '/js/three.min.js'
document.head.appendChild(link)
const link2 = document.createElement('link')
link2.rel = 'preload'
link2.as = 'script'
link2.href = '/lib/vanta.halo.min.js' // 确保路径正确
document.head.appendChild(link2)
}
// 使用 React.memo 来避免不必要的重渲染
const VantaHaloBackground = memo(({ onLoaded }: VantaHaloBackgroundProps) => {
const vantaRef = useRef<HTMLDivElement>(null)
const effectInstance = useRef<any>(null)
const frameId = useRef<number>()
useEffect(() => {
let canceled = false
preloadVantaScript()
const loadVanta = async () => {
try {
// Dynamically load the script instead of importing
const threeScript = document.createElement('script')
threeScript.src = '/js/three.min.js'
document.body.appendChild(threeScript)
threeScript.onload = () => {
const vantaScript = document.createElement('script')
vantaScript.src = '/lib/vanta.halo.min.js'
document.body.appendChild(vantaScript)
vantaScript.onload = () => {
if (canceled || !vantaRef.current || effectInstance.current) return
// 使用 requestAnimationFrame 来控制动画帧率
const animate = () => {
if (effectInstance.current) {
effectInstance.current.frameRequestId = requestAnimationFrame(animate)
}
}
// Access VANTA from the window object after script loads
if (window.VANTA && window.VANTA.HALO) {
effectInstance.current = window.VANTA.HALO({
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 (
<div
ref={vantaRef}
style={{
width: '61.8vw',
height: '100vh',
position: 'fixed',
top: 0,
left: 0,
zIndex: -1,
willChange: 'transform', // 优化图层合成
transform: 'translateZ(0)', // 启用硬件加速
}}
/>
)
})
VantaHaloBackground.displayName = 'VantaHaloBackground'
export default VantaHaloBackground