"use client"; import { useEffect, useRef, useState } from "react"; import { useTheme } from "next-themes"; import Matter from "matter-js"; /** * Antigravity Background Component * 使用 Matter.js 实现完整的 Google Antigravity 物理效果 */ export function AntigravityBackground() { const sceneRef = useRef(null); const engineRef = useRef(null); const renderRef = useRef(null); const mouseConstraintRef = useRef(null); const { resolvedTheme } = useTheme(); const [mounted, setMounted] = useState(false); useEffect(() => { setMounted(true); }, []); useEffect(() => { if (!mounted || !sceneRef.current) return; const safeRemoveCanvas = (render: Matter.Render | null) => { const canvas = render?.canvas; if (canvas?.parentNode) { canvas.parentNode.removeChild(canvas); } }; // 清理旧实例 if (engineRef.current) { Matter.Engine.clear(engineRef.current); } if (renderRef.current) { Matter.Render.stop(renderRef.current); safeRemoveCanvas(renderRef.current); } // 创建物理引擎 const engine = Matter.Engine.create({ gravity: { x: 0, y: 1, scale: 0.001 }, }); engineRef.current = engine; // 创建渲染器 const render = Matter.Render.create({ element: sceneRef.current, engine: engine, options: { width: window.innerWidth, height: window.innerHeight, wireframes: false, background: "transparent", pixelRatio: window.devicePixelRatio, }, }); renderRef.current = render; // 创建墙壁(边界) const wallOptions: Matter.IBodyDefinition = { isStatic: true, render: { fillStyle: "transparent", strokeStyle: "transparent", }, friction: 0.3, restitution: 0.6, }; const walls = [ // 底部 Matter.Bodies.rectangle( window.innerWidth / 2, window.innerHeight + 25, window.innerWidth, 50, wallOptions ), // 顶部 Matter.Bodies.rectangle( window.innerWidth / 2, -25, window.innerWidth, 50, wallOptions ), // 左侧 Matter.Bodies.rectangle( -25, window.innerHeight / 2, 50, window.innerHeight, wallOptions ), // 右侧 Matter.Bodies.rectangle( window.innerWidth + 25, window.innerHeight / 2, 50, window.innerHeight, wallOptions ), ]; Matter.Composite.add(engine.world, walls); // 创建可拖动的物体 const emojis = [ { emoji: "🤖", x: 200, y: 100, size: 40, color: "#667eea" }, { emoji: "💬", x: 400, y: 150, size: 35, color: "#764ba2" }, { emoji: "✨", x: 600, y: 200, size: 38, color: "#f093fb" }, { emoji: "🚀", x: 800, y: 120, size: 36, color: "#4facfe" }, { emoji: "🎨", x: 300, y: 400, size: 34, color: "#43e97b" }, { emoji: "⚡", x: 700, y: 350, size: 37, color: "#fa709a" }, ]; const bodies = emojis.map((item) => { const body = Matter.Bodies.circle(item.x, item.y, item.size / 2, { restitution: 0.8, friction: 0.05, density: 0.001, render: { fillStyle: item.color + "40", strokeStyle: item.color + "80", lineWidth: 2, }, }); // 存储 emoji 信息到 body 上 (body as any).emoji = item.emoji; (body as any).size = item.size; return body; }); Matter.Composite.add(engine.world, bodies); // 添加鼠标控制 const mouse = Matter.Mouse.create(render.canvas); const mouseConstraint = Matter.MouseConstraint.create(engine, { mouse: mouse, constraint: { stiffness: 0.2, render: { visible: false, }, }, }); mouseConstraintRef.current = mouseConstraint; Matter.Composite.add(engine.world, mouseConstraint); // 保持鼠标与渲染器同步 render.mouse = mouse; // 自定义渲染函数(绘制 emoji) Matter.Events.on(render, "afterRender", () => { const context = render.context; bodies.forEach((body) => { const emoji = (body as any).emoji; const size = (body as any).size; if (!emoji) return; context.save(); context.translate(body.position.x, body.position.y); context.rotate(body.angle); // 绘制半透明背景圆 context.beginPath(); context.arc(0, 0, size / 2, 0, Math.PI * 2); context.fillStyle = body.render.fillStyle || "#667eea40"; context.fill(); // 绘制 emoji context.font = `${size * 0.8}px Arial`; context.textAlign = "center"; context.textBaseline = "middle"; context.fillText(emoji, 0, 0); context.restore(); }); }); // 启动引擎和渲染器 const runner = Matter.Runner.create(); Matter.Runner.run(runner, engine); Matter.Render.run(render); // 窗口大小调整 const handleResize = () => { render.canvas.width = window.innerWidth; render.canvas.height = window.innerHeight; render.options.width = window.innerWidth; render.options.height = window.innerHeight; // 更新墙壁位置 Matter.Body.setPosition(walls[0], { x: window.innerWidth / 2, y: window.innerHeight + 25, }); Matter.Body.setPosition(walls[1], { x: window.innerWidth / 2, y: -25, }); Matter.Body.setPosition(walls[2], { x: -25, y: window.innerHeight / 2, }); Matter.Body.setPosition(walls[3], { x: window.innerWidth + 25, y: window.innerHeight / 2, }); // 更新墙壁尺寸 Matter.Body.scale(walls[0], window.innerWidth / walls[0].bounds.max.x, 1); Matter.Body.scale(walls[1], window.innerWidth / walls[1].bounds.max.x, 1); Matter.Body.scale(walls[2], 1, window.innerHeight / walls[2].bounds.max.y); Matter.Body.scale(walls[3], 1, window.innerHeight / walls[3].bounds.max.y); }; window.addEventListener("resize", handleResize); // 清理函数 return () => { window.removeEventListener("resize", handleResize); if (mouseConstraintRef.current) { Matter.Composite.remove(engine.world, mouseConstraintRef.current); mouseConstraintRef.current = null; } if (renderRef.current) { Matter.Render.stop(renderRef.current); safeRemoveCanvas(renderRef.current); renderRef.current = null; } if (engineRef.current) { Matter.Engine.clear(engineRef.current); engineRef.current = null; } Matter.Runner.stop(runner); }; }, [mounted, resolvedTheme]); if (!mounted) return null; return (
); }