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

如何在Nuxt.js路由切换后重新初始化Matter.js实例?

解决Nuxt.js中Matter.js路由切换后失效的问题

你的问题出在两个核心点:一是DOMContentLoaded事件仅在页面首次加载时触发,路由切换返回时不会再次执行;二是未清理旧的Matter实例,导致新实例与残留资源冲突。按以下步骤修改即可修复:

1. 调整初始化逻辑,移除冗余监听

mounted钩子触发时组件DOM已渲染完成,无需等待DOMContentLoaded,直接调用初始化方法。同时将Matter核心实例(引擎、运行器、动画帧ID等)存入组件数据,方便后续销毁操作。

2. 添加销毁钩子,彻底清理旧实例

在组件销毁前,停止Matter运行器、销毁引擎、取消所有动画帧请求,同时重置元素样式,避免下次进入页面时残留旧状态。

修改后的完整组件代码

export default {
  data() {
    return {
      items: [
        { className: "-i1" },
        { className: "-i2" },
        { className: "-i3" },
        { className: "-i4" },
        { className: "-i5" },
      ],
      // 存储Matter实例,用于销毁阶段清理
      matterInstance: {
        engine: null,
        runner: null,
        animationFrames: [],
      },
    };
  },

  mounted() {
    // 组件挂载后直接初始化,无需等待DOMContentLoaded
    this.startFallbox();
  },

  beforeDestroy() {
    const { engine, runner, animationFrames } = this.matterInstance;
    
    // 停止Matter运行器
    if (runner) Matter.Runner.stop(runner);
    // 销毁引擎,释放资源
    if (engine) Matter.Engine.clear(engine);
    // 取消所有动画帧请求,避免内存泄漏
    animationFrames.forEach(id => cancelAnimationFrame(id));
    // 重置元素样式,恢复初始状态
    this.items.forEach(i => {
      const elem = document.querySelector(`.${i.className}`);
      if (elem) {
        elem.style.opacity = '0';
        elem.style.top = '';
        elem.style.left = '';
        elem.style.transform = '';
      }
    });
  },

  methods: {
    startFallbox() {
      const Engine = Matter.Engine;
      const Render = Matter.Render;
      const Runner = Matter.Runner;
      const Bodies = Matter.Bodies;
      const Body = Matter.Body;
      const Composite = Matter.Composite;
      const MouseConstraint = Matter.MouseConstraint;

      const engine = Engine.create();
      const world = engine.world;
      engine.gravity.y = 1;

      const fallbox = document.querySelector(".fallbox-scene");

      const render = Render.create({
        element: fallbox,
        engine,
        options: {
          width: fallbox.offsetWidth,
          height: fallbox.offsetHeight,
        },
      });

      const runner = Runner.create();
      Runner.run(runner, engine);

      // 将核心实例存入data,供销毁时使用
      this.matterInstance.engine = engine;
      this.matterInstance.runner = runner;
      this.matterInstance.animationFrames = [];

      const itemArray = this.items;
      itemArray.forEach((i) => {
        const get = document.querySelector(`.${i.className}`);
        if (!get) return;

        get.style.opacity = 1;

        const item = {
          w: get.clientWidth,
          h: get.clientHeight,
          body: Bodies.rectangle(
            Math.random() * window.innerWidth,
            Math.random() * -1000,
            get.clientWidth,
            get.clientHeight,
            {
              restitution: 0.5,
              angle: Math.random() * 360,
            }
          ),
          elem: get,
          render() {
            const { x, y } = this.body.position;
            this.elem.style.top = `${y - this.h / 2}px`;
            this.elem.style.left = `${x - this.w / 2}px`;
            this.elem.style.transform = `rotate(${this.body.angle}rad)`;
          },
        };

        Body.rotate(item.body, Math.random() * 360);
        Composite.add(world, [item.body]);

        // 存储动画帧ID,方便销毁时取消
        const rerender = () => {
          item.render();
          const id = requestAnimationFrame(rerender);
          this.matterInstance.animationFrames.push(id);
        };
        rerender();
      });

      const ground = Bodies.rectangle(
        fallbox.offsetWidth / 2,
        fallbox.offsetHeight,
        2000,
        1,
        { isStatic: true }
      );
      const left = Bodies.rectangle(
        0,
        fallbox.offsetHeight / 2,
        1,
        fallbox.offsetHeight,
        { isStatic: true }
      );
      const right = Bodies.rectangle(
        fallbox.offsetWidth,
        fallbox.offsetHeight / 2,
        1,
        fallbox.offsetHeight,
        { isStatic: true }
      );
      Composite.add(world, [ground, left, right]);

      const mouseConstraint = MouseConstraint.create(engine, {
        element: fallbox,
        constraint: { stiffness: 0.2 },
      });
      mouseConstraint.mouse.element.removeEventListener(
        "mousewheel",
        mouseConstraint.mouse.mousewheel
      );
      mouseConstraint.mouse.element.removeEventListener(
        "DOMMouseScroll",
        mouseConstraint.mouse.mousewheel
      );
      Composite.add(world, mouseConstraint);

      Render.lookAt(render, {
        min: { x: 0, y: 0 },
        max: { x: fallbox.offsetWidth, y: fallbox.offsetHeight },
      });
    },
  },
};

关键修改说明

  • 移除DOMContentLoaded监听:mounted阶段组件DOM已就绪,直接初始化即可,避免路由切换后事件不触发的问题。
  • 实例存储与清理:将Matter核心实例存入组件数据,在beforeDestroy中彻底销毁,避免内存泄漏和实例冲突。
  • 元素状态重置:销毁时恢复元素初始样式,确保下次进入页面时是全新状态。
  • 选择器优化:将getElementsByClassName替换为querySelector,简化逻辑并避免集合操作的潜在问题。

内容的提问来源于stack exchange,提问作者Philippe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 19:57:25