讲师中心 微信公众号

工具推荐

视频效率加速
剧云
剧云

中文短剧剧本制作平台

Seko
Seko

AI短剧一键生成成片

立刻MV
立刻MV

低成本歌曲 MV、视频生成

updream

一键生成高质量推广成片

在 Matter.js 中实现精确的鼠标交互控制

云芳小哥_8983

云芳小哥_8983

发布时间:2025-12-08 19:30:52

|

521人浏览过

|

来源于php中文网

原创

在 Matter.js 中实现精确的鼠标交互控制

本教程详细介绍了如何在 matter.js 物理引擎中集成鼠标控制功能,以实现对物理体的拖拽与交互。文章着重解决了在处理高dpi屏幕时常见的鼠标坐标缩放问题,通过正确配置 `matter.mouseconstraint` 和运用 `matter.mouse.setscale` 方法,确保鼠标与物理世界的精确对应,从而提供流畅的用户体验。

引言:Matter.js 交互式物理模拟

Matter.js 是一个功能强大且易于使用的 2D 物理引擎,广泛应用于网页游戏、交互式动画和模拟等场景。为了让用户能够直接与物理世界中的物体进行互动,例如拖拽、点击等,集成鼠标控制功能是必不可少的一步。本教程将引导您如何在 Matter.js 项目中正确设置鼠标控制,并特别关注在现代高DPI显示器上可能出现的坐标缩放问题。

Matter.js 鼠标控制核心组件

Matter.js 提供了两个核心模块来处理鼠标交互:

  1. Matter.Mouse: 这个模块用于创建和管理鼠标实例,它负责捕获鼠标在指定 HTML 元素(通常是 )上的位置和状态(按下、释放等)。
  2. Matter.MouseConstraint: 这是一个约束(Constraint)模块,它将 Matter.Mouse 实例与物理引擎的世界连接起来。当鼠标在物理世界中按下并拖动一个物体时,MouseConstraint 会创建一个临时的弹性约束,使得该物体能够跟随鼠标移动。

实现步骤与代码示例

我们将基于一个 Matter.js 的基本设置,逐步添加鼠标控制功能。

1. 初始化 Matter.js 环境

首先,我们需要设置 Matter.js 引擎、世界、以及一些基本的物理体(例如地面和两个方块)。

<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<canvas id="canvasM" data-pixel-ratio="2" style="position:relative; z-index:0;"></canvas>
<script>
    // 模块别名
    var Engine = Matter.Engine,
        Render = Matter.Render, // 尽管这里使用自定义渲染,但通常会用到
        Runner = Matter.Runner, // 如果不使用自定义渲染,Runner会自动更新引擎和渲染
        Bodies = Matter.Bodies,
        Composite = Matter.Composite,
        World = Matter.World;

    // 创建物理引擎
    var engine = Engine.create();
    var world = engine.world;

    // 获取窗口尺寸
    var w = window.innerWidth;
    var h = window.innerHeight;

    // 创建两个方块和一个地面
    var boxA = Bodies.rectangle(0.5 * w + 30, 0.7 * h, 80, 80);
    var boxB = Bodies.rectangle(0.5 * w + 60, 50, 80, 80);
    var ground = Bodies.rectangle(0.5 * w - 1, 0.888 * h + 0.05 * h - 30 + 1.5, w, 0.1 * h, { isStatic: true });

    // 将所有物体添加到世界中
    Composite.add(world, [boxA, boxB, ground]);

    // 获取并设置 Canvas
    var canvas = document.getElementById('canvasM');
    var context = canvas.getContext('2d');
    canvas.width = window.innerWidth - 130;
    canvas.height = 0.888 * window.innerHeight;
</script>

2. 创建自定义渲染循环并更新引擎

在某些场景下,开发者可能需要自定义渲染逻辑,而不是使用 Matter.js 内置的 Render 模块。在这种情况下,必须在自定义的 requestAnimationFrame 循环中手动调用 Matter.Engine.update(engine) 来推进物理世界的模拟。

<script>
    // ... (接上面的初始化代码) ...

    (function render() {
        var bodies = Composite.allBodies(engine.world);

        window.requestAnimationFrame(render);

        context.beginPath();

        for (var i = 0; i < bodies.length; i += 1) {
            var vertices = bodies[i].vertices;

            context.moveTo(vertices[0].x, vertices[0].y);

            for (var j = 1; j < vertices.length; j += 1) {
                context.lineTo(vertices[j].x, vertices[j].y);
            }

            context.lineTo(vertices[0].x, vertices[0].y);
        }

        context.lineWidth = 3;
        context.fillStyle = '#fff'; // 使用 fillStyle 而不是 fill
        context.strokeStyle = '#000';
        context.fill(); // 填充图形
        context.stroke(); // 描边图形

        // 关键:手动更新物理引擎
        Matter.Engine.update(engine);
    })();
</script>

注意: 在上述代码中,我们移除了 Runner.run(runner, engine);,并添加了 Matter.Engine.update(engine); 到自定义的 render 循环中。这意味着物理引擎的更新现在由我们的 requestAnimationFrame 循环驱动。

DeepBI DHTML 页面生成器
DeepBI DHTML 页面生成器

生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur

下载

3. 集成鼠标交互

现在,我们将添加鼠标控制的核心逻辑。这包括创建鼠标实例、处理缩放,以及创建鼠标约束。

<script>
    // ... (接上面的渲染循环代码) ...

    // 创建鼠标实例,并将其绑定到 Canvas 元素
    var canvmouse = Matter.Mouse.create(document.getElementById('canvasM'));

    // 处理高DPI屏幕缩放问题
    // 如果 Canvas 元素有 data-pixel-ratio="2" 属性,通常意味着其内部渲染尺寸是CSS尺寸的两倍
    // 因此,鼠标坐标也需要相应缩放,以匹配物理世界坐标
    Matter.Mouse.setScale(canvmouse, { x: 2, y: 2 });

    // 创建鼠标约束
    // mouseControl 将负责捕获鼠标事件,并将其转换为物理世界中的交互
    var mouseControl = Matter.MouseConstraint.create(engine, {
        mouse: canvmouse,
        constraint: {
            stiffness: 0.2, // 约束的刚度
            render: {
                visible: false // 不渲染鼠标约束的连接线
            }
        }
    });

    // 将鼠标约束添加到物理世界中
    Composite.add(world, mouseControl);

    // render.mouse = mouse; // 这行代码在Matter.js中没有实际作用,可以移除
</script>

完整示例代码

将以上所有代码片段组合起来,就得到了一个功能完整的 Matter.js 鼠标控制示例:

<!DOCTYPE html>
<html>
<head>
    <title>Matter.js 鼠标控制示例</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; background-color: #f0f0f0; border: 1px solid #ccc; }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
</head>
<body>
    <canvas id="canvasM" data-pixel-ratio="2" style="position:relative; z-index:0;"></canvas>
    <script>
        // 模块别名
        var Engine = Matter.Engine,
            Render = Matter.Render,
            Runner = Matter.Runner,
            Bodies = Matter.Bodies,
            Composite = Matter.Composite,
            World = Matter.World,
            Mouse = Matter.Mouse,
            MouseConstraint = Matter.MouseConstraint;

        // 创建物理引擎
        var engine = Engine.create();
        var world = engine.world;

        // 获取窗口尺寸
        var w = window.innerWidth;
        var h = window.innerHeight;

        // 创建两个方块和一个地面
        var boxA = Bodies.rectangle(0.5 * w + 30, 0.7 * h, 80, 80);
        var boxB = Bodies.rectangle(0.5 * w + 60, 50, 80, 80);
        var ground = Bodies.rectangle(0.5 * w - 1, 0.888 * h + 0.05 * h - 30 + 1.5, w, 0.1 * h, { isStatic: true });

        // 将所有物体添加到世界中
        Composite.add(world, [boxA, boxB, ground]);

        // 获取并设置 Canvas
        var canvas = document.getElementById('canvasM');
        var context = canvas.getContext('2d');
        canvas.width = window.innerWidth - 130;
        canvas.height = 0.888 * window.innerHeight;

        // 自定义渲染循环
        (function render() {
            var bodies = Composite.allBodies(engine.world);

            window.requestAnimationFrame(render);

            context.clearRect(0, 0, canvas.width, canvas.height); // 清除上一帧内容
            context.beginPath();

            for (var i = 0; i < bodies.length; i += 1) {
                var vertices = bodies[i].vertices;

                context.moveTo(vertices[0].x, vertices[0].y);

                for (var j = 1; j < vertices.length; j += 1) {
                    context.lineTo(vertices[j].x, vertices[j].y);
                }

                context.lineTo(vertices[0].x, vertices[0].y);
            }

            context.lineWidth = 3;
            context.fillStyle = '#fff';
            context.strokeStyle = '#000';
            context.fill();
            context.stroke();

            // 关键:手动更新物理引擎
            Matter.Engine.update(engine);
        })();

        // 创建鼠标实例,并将其绑定到 Canvas 元素
        var canvmouse = Mouse.create(canvas);

        // 处理高DPI屏幕缩放问题
        // 根据 data-pixel-ratio 属性设置鼠标坐标的缩放比例
        // 在此示例中,data-pixel-ratio="2" 表示像素比为2
        Mouse.setScale(canvmouse, { x: 2, y: 2 });

        // 创建鼠标约束
        var mouseControl = MouseConstraint.create(engine, {
            mouse: canvmouse,
            constraint: {
                stiffness: 0.2,
                render: {
                    visible: false
                }
            }
        });

        // 将鼠标约束添加到物理世界中
        Composite.add(world, mouseControl);
    </script>
</body>
</html>

注意事项

  1. 高DPI屏幕适配 (Matter.Mouse.setScale): 这是实现精确鼠标控制的关键。现代显示器(如 Retina 屏)通常具有较高的像素密度。如果 Canvas 元素通过 CSS 设置了较小的尺寸,但其内部渲染尺寸(canvas.width, canvas.height)被设置为 CSS 尺寸的倍数(例如,通过 data-pixel-ratio="2" 或 JavaScript 手动设置),那么鼠标事件报告的坐标(相对于 CSS 尺寸)将与 Canvas 内部渲染的物理坐标不匹配。Matter.Mouse.setScale(mouseInstance, {x: scale, y: scale}) 方法能够校正这一差异,确保鼠标点击或拖拽的物理坐标与 Matter.js 世界中的坐标一致。在本例中,data-pixel-ratio="2" 意味着需要将鼠标坐标放大两倍。

  2. 引擎更新机制:

    • 如果您使用 Matter.Runner.run(runner, engine),Runner 会自动在每个动画帧更新物理引擎和(如果配置了)渲染器。
    • 如果您像本教程一样,使用自定义的 requestAnimationFrame 循环来渲染,那么必须在循环内部手动调用 Matter.Engine.update(engine) 来推进物理模拟。否则,物体将不会移动或响应重力。
  3. 组件实例化位置: Matter.Mouse 和 Matter.MouseConstraint 应该在主程序初始化阶段创建一次,并添加到世界中。不应在 render 循环内部重复创建它们,这会导致性能问题和不可预测的行为。

总结

通过本教程,您应该已经掌握了在 Matter.js 中实现鼠标控制的完整流程。关键在于正确地实例化 Matter.Mouse 和 Matter.MouseConstraint,并特别注意通过 Matter.Mouse.setScale 方法来解决高DPI屏幕带来的坐标缩放问题。理解引擎更新机制(手动更新或使用 Runner)也对于构建流畅的物理模拟至关重要。遵循这些步骤,您将能够为您的 Matter.js 应用添加直观且响应迅速的交互体验。

热门AI工具

更多
码上飞
码上飞 Hot

一款基于自然语言的AI应用开发平台,可根据用户描述自动生成应用程序,帮助个人和团队快速完成应用原型与开发。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

创客贴AI

创客贴AI是一款AI图像与设计工具,AI辅助的智能在线设计工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

超级麦吉

一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

相关专题

更多
js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

5036

2023.06.20

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1455

2023.07.28

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1938

2023.08.03

js是什么意思
js是什么意思

JS是JavaScript的缩写,它是一种广泛应用于网页开发的脚本语言。JavaScript是一种解释性的、基于对象和事件驱动的编程语言,通常用于为网页增加交互性和动态性。它可以在网页上实现复杂的功能和效果,如表单验证、页面元素操作、动画效果、数据交互等。

10563

2023.08.17

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

1060

2023.09.01

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

3204

2023.09.04

Js中concat和push的区别
Js中concat和push的区别

Js中concat和push的区别:1、concat用于将两个或多个数组合并成一个新数组,并返回这个新数组,而push用于向数组的末尾添加一个或多个元素,并返回修改后的数组的新长度;2、concat不会修改原始数组,是创建新的数组,而push会修改原数组,将新元素添加到原数组的末尾等等。本专题为大家提供concat和push相关的文章、下载、课程内容,供大家免费下载体验。

1689

2023.09.14

js截取字符串的方法介绍
js截取字符串的方法介绍

JavaScript字符串截取方法,包括substring、slice、substr、charAt和split方法。这些方法可以根据具体需求,灵活地截取字符串的不同部分。在实际开发中,根据具体情况选择合适的方法进行字符串截取,能够提高代码的效率和可读性 。

5509

2023.09.21

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

40

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn