滑鼠滾輪事件 (WheelEvent)
滑鼠滾輪事件(WheelEvent)
TL;DR用 Math.sign(e.deltaY) 取得方向(+1 下 / -1 上)。需要阻止頁面捲動時必須 { passive: false },否則 preventDefault() 會被忽略。
取方向最簡寫法
const delta = Math.sign(e.deltaY);
// e.deltaY > 0 → +1(向下/向後滾)
// e.deltaY < 0 → -1(向上/向前滾)
// e.deltaY = 0 → 0
為什麼用 Math.sign 不直接用 e.deltaY?不同裝置(滑鼠、觸控板、Magic Mouse)的 deltaY 數值大小差很多(可能 1、3、100),只有方向是穩定的。要做「滾一格 = 縮放一級」這種離散行為,只看方向比較可靠。
WheelEvent 屬性
| 屬性 | 說明 |
|---|---|
deltaX | 水平滾動量(觸控板左右滑) |
deltaY | 垂直滾動量(最常用) |
deltaZ | Z 軸滾動量(極少用) |
deltaMode | 0=像素 / 1=行 / 2=頁(跨瀏覽器差異主要來自這) |
實用範例:縮放功能
let scale = 1;
element.addEventListener('wheel', (e) => {
e.preventDefault(); // 阻止頁面捲動
const direction = Math.sign(e.deltaY);
if (direction > 0) {
scale *= 0.9; // 往下滾:縮小
} else if (direction < 0) {
scale *= 1.1; // 往上滾:放大
}
element.style.transform = `scale(${scale})`;
}, { passive: false }); // ⚠️ 必須 false 才能 preventDefault
passive 陷阱使用 e.preventDefault() 時必須將 passive 設為 false,否則無法阻止預設行為,console 會出現: Unable to preventDefault inside passive event listener invocation.詳見 監聽 passive 屬性。
跨裝置 / 跨瀏覽器差異
deltaMode 差異
element.addEventListener('wheel', (e) => {
let pixels = e.deltaY;
// 正規化:不同 deltaMode 統一成 pixel
if (e.deltaMode === 1) pixels *= 16; // 行 → 16px
else if (e.deltaMode === 2) pixels *= window.innerHeight; // 頁 → 視窗高
// 接著用 pixels 做運算
});
Mac 觸控板的慣性滾動
Mac 觸控板會在你停止滑動後繼續發送 wheel 事件(慣性),deltaY 會逐漸變小。如果做縮放,可以加 throttle 或 debounce 避免縮放過快:
import { throttle } from 'lodash';
const handleZoom = throttle((delta) => {
scale *= delta > 0 ? 0.9 : 1.1;
}, 50);
element.addEventListener('wheel', (e) => {
e.preventDefault();
handleZoom(Math.sign(e.deltaY));
}, { passive: false });
常見使用場景
| 場景 | 做法 |
|---|---|
| 滾動進度追蹤(分析、scroll-driven 動畫) | passive: true,只讀不擋 |
| 自訂縮放(地圖、圖片預覽) | passive: false + preventDefault |
| 水平滾動(把垂直滾動轉成水平) | passive: false + 改 scrollLeft |
| 無限滾動觸發 | passive: true(用 IntersectionObserver 更好) |