昨天最後說了,我們要幫彈珠台最後加上滑鼠的操控效果,所以今天讀的文件會是 Mouse 這個模組。
Mouse 模組相對在實作中較少用到,主要是互動的一些方式都被定義在另一個模組:MouseConstraint,那個模組我們會在明天來看。
為什麼 Mouse 模組不容易用到,還要抽一篇來講它呢?
一個是雖然他的函式少用,但是相關屬性如果有要做 mouse 相關操作最好要有所了解,一個是筆者想一樣在這篇走走原始碼的部分,走走原始碼除了更能了解詳細內容,也可能可以從原始碼中看到一些作者設計的想法,有機會的話看看這種公用函式庫原始碼其實是很不錯的事。
Mouse 在 Matter.js 的世界中其實是一種物件型別,我們來看一下 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();
};
總共有四個事件
這邊從名字看應該能看出對應行為:滑鼠移動,滑鼠點擊、滑鼠點擊後放開、滑鼠滾輪。
滑鼠移動的事件就是單純的紀錄位置,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 物件來和我們的畫面作互動。