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

NextJS中集成Matter.js无法运行动画问题求助

NextJS中Matter.js无动画问题修复

问题描述

我是Matter.js新手,在NextJS中使用Matter.js时遇到问题。代码是参考示例转换为NextJS组件的,页面能显示元素但无动画效果,已卡在此问题一天。

原代码

import React, { useEffect, useRef } from 'react';
import Matter from 'matter-js';
const Skills = () => {
    const boxRef = useRef(null);
    const canvasRef = useRef(null);
    useEffect(() => {
        let Engine = Matter.Engine;
        let Render = Matter.Render;
        let World = Matter.World;
        let Bodies = Matter.Bodies;
        let engine = Engine.create();
        let render = Render.create({
            element: boxRef.current,
            engine: engine,
            canvas: canvasRef.current,
            options: {
                width: 300,
                height: 300,
                background: 'rgba(255, 0, 0, 0.5)',
                wireframes: false,
            },
        });
        const floor = Bodies.rectangle(150, 300, 300, 20, {
            isStatic: true,
            render: {
                fillStyle: 'blue',
            },
        });
        const ball = Bodies.circle(150, 10, 10, {
            restitution: 0.9,
            render: {
                fillStyle: 'yellow',
            },
        });
        World.add(engine.world, ball);
        Matter.Runner.run(engine);
        Render.run(render);
    }, []);

    return (
        <div ref={boxRef} style={{ width: 300, height: 300,}}>
            <canvas ref={canvasRef} />
        </div>
    );
};

export default Skills

问题分析

  1. 未将创建的floor添加到物理世界,球没有碰撞边界(虽不是动画失效核心,但会导致球直接掉落)。
  2. Runner使用方式错误:直接调用Matter.Runner.run(engine)在React组件环境中无法正确启动物理模拟循环。
  3. 缺少组件卸载时的清理逻辑,可能引发内存泄漏或运行异常。

修复后的代码

import React, { useEffect, useRef } from 'react';
import Matter from 'matter-js';

const Skills = () => {
    const boxRef = useRef(null);
    const canvasRef = useRef(null);
    const engineRef = useRef(null);
    const runnerRef = useRef(null);
    const renderRef = useRef(null);

    useEffect(() => {
        const { Engine, Render, World, Bodies, Runner } = Matter;

        // 初始化引擎
        const engine = Engine.create();
        engineRef.current = engine;

        // 初始化渲染器
        const render = Render.create({
            element: boxRef.current,
            engine: engine,
            canvas: canvasRef.current,
            options: {
                width: 300,
                height: 300,
                background: 'rgba(255, 0, 0, 0.5)',
                wireframes: false,
            },
        });
        renderRef.current = render;

        // 创建物理实体
        const floor = Bodies.rectangle(150, 290, 300, 20, {
            isStatic: true,
            render: {
                fillStyle: 'blue',
            },
        });
        const ball = Bodies.circle(150, 10, 10, {
            restitution: 0.9,
            render: {
                fillStyle: 'yellow',
            },
        });

        // 将实体添加到物理世界
        World.add(engine.world, [floor, ball]);

        // 创建并启动Runner(驱动物理模拟)
        const runner = Runner.create();
        runnerRef.current = runner;
        Runner.run(runner, engine);

        // 启动渲染器
        Render.run(render);

        // 组件卸载时清理资源
        return () => {
            Runner.stop(runner);
            Render.stop(render);
            World.clear(engine.world);
            Engine.clear(engine);
        };
    }, []);

    return (
        <div ref={boxRef} style={{ width: 300, height: 300, position: 'relative' }}>
            <canvas ref={canvasRef} style={{ position: 'absolute', top: 0, left: 0 }} />
        </div>
    );
};

export default Skills;

关键修复点

  • 添加地面到物理世界:让球有碰撞反弹的边界,同时确保物理模拟有交互对象。
  • 正确初始化Runner:创建Runner实例并绑定引擎,这是Matter.js启动物理循环的标准方式,解决动画不运行的核心问题。
  • 添加清理逻辑:组件卸载时停止Runner和渲染器,清理物理世界和引擎,避免内存泄漏。
  • 调整地面位置:将地面y轴坐标改为290,使其显示在容器内部(原300会让地面位于容器底部外侧)。
  • canvas定位:给canvas添加绝对定位,确保其完全填充父容器,避免渲染区域偏移。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 05:30:11