Day16. 老鼠,老虎傻傻分不清楚?- Mouse(上)

昨天最後说了,我们要帮弹珠台最後加上滑鼠的操控效果,所以今天读的文件会是 Mouse 这个模组。

Mouse 模组相对在实作中较少用到,主要是互动的一些方式都被定义在另一个模组:MouseConstraint,那个模组我们会在明天来看。

今天的Demo
今天的Demo原始码
https://ithelp.ithome.com.tw/upload/images/20211001/20142057pO9DDhttXs.png

为什麽 Mouse 模组不容易用到,还要抽一篇来讲它呢?

一个是虽然他的函式少用,但是相关属性如果有要做 mouse 相关操作最好要有所了解,一个是笔者想一样在这篇走走原始码的部分,走走原始码除了更能了解详细内容,也可能可以从原始码中看到一些作者设计的想法,有机会的话看看这种公用函式库原始码其实是很不错的事。

Mouse 在 Matter.js 的世界中其实是一种物件型别,我们来看一下 Mouse 这个模组总共有多少种方法:

  • Matter.Mouse.create(element)
  • Matter.Mouse.setElement(mouse, element)
  • Matter.Mouse.setOffset(mouse, offset)
  • Matter.Mouse.setScale(mouse, scale)
  • Matter.Mouse.clearSourceEvents(mouse)

先来看到 create 方法的原始码,里面写着如何建构一个 mouse物件。

可以看到 mouse 创建本身会相依於一个 HTMLelement,其实就是定义这个宣告的 mouse 物件的作用范围。可以仅限於 canvas上,若不输入的话会 default 拿 body 当 element的对象。

var mouse = {};
if (!element) {
    Common.log('Mouse.create: element was undefined, defaulting to document.body', 'warn');
}
mouse.element = element || document.body;

再来有一段为 mouse 建立基本属性:

mouse.element = element || document.body;
mouse.absolute = { x: 0, y: 0 };
mouse.position = { x: 0, y: 0 };
mouse.mousedownPosition = { x: 0, y: 0 };
mouse.mouseupPosition = { x: 0, y: 0 };
mouse.offset = { x: 0, y: 0 };
mouse.scale = { x: 1, y: 1 };
mouse.wheelDelta = 0;
mouse.button = -1;
mouse.pixelRatio = parseInt(mouse.element.getAttribute('data-pixel-ratio'), 10) || 1;

这边在 API 文件中并没有一一说明各个属性的套用地方,我们也就先不在这段深究,我们会再後面绑定的 event 中看到一些设置或使用,直观看到的会是像是相对位置、绝对位置,点击位置,偏移、缩放等等属性。

创建的最後是帮 mouse 绑上特定事件的监听:

mouse.mousemove = function(event) { 
    var position = Mouse._getRelativeMousePosition(event, mouse.element, mouse.pixelRatio),
        touches = event.changedTouches;

    if (touches) {
        mouse.button = 0;
        event.preventDefault();
    }

    mouse.absolute.x = position.x;
    mouse.absolute.y = position.y;
    mouse.position.x = mouse.absolute.x * mouse.scale.x + mouse.offset.x;
    mouse.position.y = mouse.absolute.y * mouse.scale.y + mouse.offset.y;
    mouse.sourceEvents.mousemove = event;
};
mouse.mousedown = function(event) {
    var position = Mouse._getRelativeMousePosition(event, mouse.element, mouse.pixelRatio),
        touches = event.changedTouches;

    if (touches) {
        mouse.button = 0;
        event.preventDefault();
    } else {
        mouse.button = event.button;
    }

    mouse.absolute.x = position.x;
    mouse.absolute.y = position.y;
    mouse.position.x = mouse.absolute.x * mouse.scale.x + mouse.offset.x;
    mouse.position.y = mouse.absolute.y * mouse.scale.y + mouse.offset.y;
    mouse.mousedownPosition.x = mouse.position.x;
    mouse.mousedownPosition.y = mouse.position.y;
    mouse.sourceEvents.mousedown = event;
};

mouse.mouseup = function(event) {
    var position = Mouse._getRelativeMousePosition(event, mouse.element, mouse.pixelRatio),
        touches = event.changedTouches;

    if (touches) {
        event.preventDefault();
    }
    
    mouse.button = -1;
    mouse.absolute.x = position.x;
    mouse.absolute.y = position.y;
    mouse.position.x = mouse.absolute.x * mouse.scale.x + mouse.offset.x;
    mouse.position.y = mouse.absolute.y * mouse.scale.y + mouse.offset.y;
    mouse.mouseupPosition.x = mouse.position.x;
    mouse.mouseupPosition.y = mouse.position.y;
    mouse.sourceEvents.mouseup = event;
};

mouse.mousewheel = function(event) {
    mouse.wheelDelta = Math.max(-1, Math.min(1, event.wheelDelta || -event.detail));
    event.preventDefault();
};

总共有四个事件

  • MouseMove
  • MouseDown
  • MouseUp
  • MouseWheel

这边从名字看应该能看出对应行为:滑鼠移动,滑鼠点击、滑鼠点击後放开、滑鼠滚轮。

滑鼠移动的事件就是单纯的纪录位置,touch有一个isTouchChange的逻辑可以参考MDN文件,等等我们会看到,其实这边自订的事件都会跟浏览器原生的一些事件是互有关系的,从这边建构丢入 event 就可以看出一些端倪。

滑鼠点击的两个事件主要是会辨认点击当下的滑鼠位置与透过 preventDefault 来避免预设的事件相冲。 button 看起来像是用来判断是否持续维持点击状态,如果持续点击则为 0,如果放开则为 -1。

滑鼠滚轮的事件看起来会像是去抓取滚轮的差量,也就是这次滚动事件的滚动差距量。

这几个事件的目的都是在原生浏览器滑鼠事件触发的时候,来改动 mouse 这个物件本身的属性。

最後会做一个 setElement,刚好讲到我们的第二个函式。

Mouse.setElement(mouse, mouse.element);

第二个函式会是把 mouse 物件指派到某个 HTMLelement上,同时这个 element 也会等同自身的 element属性。

Mouse.setElement = function(mouse, element) {
    mouse.element = element;

    element.addEventListener('mousemove', mouse.mousemove);
    element.addEventListener('mousedown', mouse.mousedown);
    element.addEventListener('mouseup', mouse.mouseup);
    
    element.addEventListener('mousewheel', mouse.mousewheel);
    element.addEventListener('DOMMouseScroll', mouse.mousewheel);

    element.addEventListener('touchmove', mouse.mousemove);
    element.addEventListener('touchstart', mouse.mousedown);
    element.addEventListener('touchend', mouse.mouseup);
};

在 setElement 这个方法中,我们可以明确地看到其实就是把几个跟滑鼠相关的 event 绑定到浏览起本来定义的几个滑鼠行为,同时将那些滑鼠行为对应到我们上便提到的四个行为中。

也就是只监听指定 HTMLelement 上的滑鼠行为,如果你定义在 canvas 上,可以避免点击页面上除了 canvas 的地方也触发。

第三跟第四个的原始码笔者就不转贴了,可以直接参考原档,基本上就是对 mouse 内的属性作一个对外的 setter,没有特别的逻辑。

第五个 clearSourceEvents 会是把这边 mouse 模组定义的方法都拿掉,也就是不依赖原生的事件来更新 mouse 本身的属性。

Mouse.clearSourceEvents = function(mouse) {
    mouse.sourceEvents.mousemove = null;
    mouse.sourceEvents.mousedown = null;
    mouse.sourceEvents.mouseup = null;
    mouse.sourceEvents.mousewheel = null;
    mouse.wheelDelta = 0;
};

差不多到这里就完整走完我们整个 mouse 物件相关的原始码了,今天的 Demo 笔者在 canvas 上绑了点击就会记录当下 mosue 的状态在 console.log,大家可以试试看在不同地方点击然後观察属性的变化。

另外也让 frameUpdate 事件里当 mouseObject = 0 的时候会写 log,这会是实作上一个侦测点击後依滑鼠位置来做对应处理,如施力/位移等方法的实作思路。

我们今天的内容到此告一段落,明天会带大家来看怎麽利用 mouse 物件来和我们的画面作互动。


<<:  【Day 16】for 回圈

>>:  Day17 requests模组二

Day 07 - Ticks

本篇重点 Ticks 介绍及属性说明 使用 Pandas 将 Ticks 资料转换为 DataFra...

[ 卡卡 DAY 17 ] - React Native 用 Animated 来做简单骨架屏

上一章节讲了 Animated 的使用 我们运用 start() 来做个骨架屏唷! 制作 bann...

[Day12] 为了摆脱菜鸟C#後端 -到底什麽是Delegate?Func<T, TResult>?

测试打了"OrderCreate"(建立订单)服务後, 昨天我们尝试(使用AES...

追求JS小姊姊系列 Day9 -- 如果时间能重来,我不想跟工具人聊天(上)

前情提要 上一集让人等到很崩溃的,终於..郑列终於吹嘘完了 阿物件:我跟你说... 我:... (接...

初次遇见 .NET

安装 .NET SDK 去 .NET官网下载, 可以选择 .NET 5, 或 .NET Core 3...