You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matter.js元素在Next.js页面重载后无限坠落问题求助

解决Next.js中Matter.js复用组件物体无限坠落问题

问题根源

核心问题是Matter.js的引擎、物体实例未在组件卸载/复用阶段彻底清理,导致旧实例持续运行,新实例初始化时可能继承旧状态(比如物体位置),同时地面约束未正确绑定到新的物理环境中。

具体修复步骤

1. 组件卸载时彻底销毁Matter.js实例

在Technology组件的useEffect清理函数中,不仅清空refs,还要完整销毁引擎、渲染器和所有物体:

useEffect(() => {
  // 初始化Matter.js的代码...

  return () => {
    // 销毁所有物体并释放资源
    Matter.Composite.clear(engine.world, true);
    // 停止并清空引擎
    Matter.Engine.clear(engine);
    // 销毁渲染器
    if (renderer) {
      Matter.Render.stop(renderer);
      renderer.canvas.remove();
      renderer.canvas = null;
      renderer.context = null;
      renderer.textures = {};
    }
    // 重置refs
    canvasRef.current = null;
  };
}, []);

2. 每次挂载都创建全新物理环境

禁止复用全局或外部的引擎/物体实例,每次组件挂载时都创建全新的Engine、World和Render实例,确保地面约束和物体都绑定到当前实例的世界:

const initMatter = () => {
  // 创建全新引擎
  const engine = Matter.Engine.create();
  // 创建全新渲染器
  const renderer = Matter.Render.create({
    element: canvasRef.current,
    engine: engine,
    options: {
      width: window.innerWidth,
      height: window.innerHeight,
      wireframes: false,
    },
  });
  // 创建地面并添加到当前世界
  const ground = Matter.Bodies.rectangle(
    window.innerWidth / 2,
    window.innerHeight - 50,
    window.innerWidth,
    100,
    { isStatic: true }
  );
  Matter.World.add(engine.world, [ground]);

  // 添加其他动态物体...

  Matter.Engine.run(engine);
  Matter.Render.run(renderer);

  return { engine, renderer };
};

3. 视口触发时重置物体位置

若使用视口启动动画,在组件进入视口时,不仅启动动画,还要重置所有动态物体的初始位置,确保从正确起点开始运动:

const handleIntersection = (entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting && engine) {
      // 重置所有非静态物体的位置和速度
      Matter.Composite.allBodies(engine.world).forEach(body => {
        if (!body.isStatic) {
          Matter.Body.setPosition(body, {
            x: Math.random() * window.innerWidth,
            y: -50, // 从顶部上方初始位置开始
          });
          Matter.Body.setVelocity(body, { x: 0, y: 0 });
        }
      });
    }
  });
};

4. 路由切换时强制清理实例

在Next.js中监听路由变化,切换页面时手动触发Matter.js实例的销毁:

import { useRouter } from 'next/router';

const Technology = () => {
  const router = useRouter();
  const [engine, setEngine] = useState(null);
  const [renderer, setRenderer] = useState(null);

  useEffect(() => {
    const handleRouteChange = () => {
      // 路由变化时清理现有实例
      if (engine) {
        Matter.Composite.clear(engine.world, true);
        Matter.Engine.clear(engine);
      }
      if (renderer) {
        Matter.Render.stop(renderer);
        renderer.canvas.remove();
      }
    };

    router.events.on('routeChangeStart', handleRouteChange);
    return () => {
      router.events.off('routeChangeStart', handleRouteChange);
    };
  }, [router, engine, renderer]);

  // 其他组件逻辑...
};

关键注意点

  • 每个组件实例必须拥有独立的Matter.js物理环境,禁止复用引擎或世界实例
  • 清理时必须调用Composite.clear和Engine.clear,确保所有物理计算和资源都被释放
  • 视口触发动画时必须重置物体状态,避免旧位置干扰新的动画周期

内容的提问来源于stack exchange,提问作者Наталья Семешенко

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 11:21:15