跳到主要內容
← 回到學習

學習 · 前端基礎

滑鼠滾輪事件 (WheelEvent)

目錄 · 5 個段落
  1. 01取方向最簡寫法
  2. 02WheelEvent 屬性
  3. 03實用範例:縮放功能
  4. 04跨裝置 / 跨瀏覽器差異
  5. 05常見使用場景

滑鼠滾輪事件(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
typescript
為什麼用 Math.sign 不直接用 e.deltaY?
不同裝置(滑鼠、觸控板、Magic Mouse)的 deltaY 數值大小差很多(可能 1、3、100),只有方向是穩定的。要做「滾一格 = 縮放一級」這種離散行為,只看方向比較可靠。

WheelEvent 屬性

屬性說明
deltaX水平滾動量(觸控板左右滑)
deltaY垂直滾動量(最常用)
deltaZZ 軸滾動量(極少用)
deltaMode0=像素 / 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
javascript
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 做運算
});
javascript

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 });
javascript

常見使用場景

場景做法
滾動進度追蹤(分析、scroll-driven 動畫)passive: true,只讀不擋
自訂縮放(地圖、圖片預覽)passive: false + preventDefault
水平滾動(把垂直滾動轉成水平)passive: false + 改 scrollLeft
無限滾動觸發passive: true(用 IntersectionObserver 更好)

目錄