滚轮事件兼容与研究

Facebook 兼容原文
鼠标事件(以及双指手势的触摸板) 在前端的支持糟透了奥. 这是一件复杂的事情, 因此这篇文档会很长并且 (希望)我讲的足够细致,能解决你的困惑。

如果你希望能让这个事件变得可控和可预测, 这份代码是你最好的朋友.拥抱~~

至今, 已经有 4 种 DOM event 你能监听到:
‘wheel’ – Chrome(31+), FF(17+), IE(9+)
‘mousewheel’ – Chrome, IE(6+), Opera, Safari
‘MozMousePixelScroll’ – FF(3.5 only!) (2010-2013) – don’t bother!
‘DOMMouseScroll’ – FF(0.9.7+) since 2003

那我 tm 应该怎么做呢? 最好的方法是:

normalizeWheel.getEventType();

在你的回调中调用上面这段代码,用他去智能的屏蔽这些琐碎的细节. 这段代码将返回一个对象内含四个值:

spinX – normalized spin speed (use for zoom) - x plane
spinY – “ - y plane
pixelX – normalized distance (to pixels) - x plane
pixelY – “ - y plane

在你用鼠标滚动浏览器页面,或者触摸板,触摸屏挪动像素的时候,上述的所有的数值由浏览器提供。数值是会变化的
显而,在不同的浏览器和平台中, 你可能滑动的速度不同. 一些设备 (like trackpads) 会反馈出更小的增量在触摸板上, 还有一些这些数值会变化的更加平滑或者更强烈.

这份代码会做更通常的方式去让你使用:

===以下兼容不翻译====。

  • spin is trying to normalize how far the wheel was spun (or trackpad
    dragged). This is super useful for zoom support where you want to
    throw away the chunky scroll steps on the PC and make those equal to
    the slow and smooth tiny steps on the Mac. Key data: This code tries to
    resolve a single slow step on a wheel to 1.
  • pixel is normalizing the desired scroll delta in pixel units. You’ll
    get the crazy differences between browsers, but at least it’ll be in
    pixels!
  • positive value indicates scrolling DOWN/RIGHT, negative UP/LEFT. This
    should translate to positive value zooming IN, negative zooming OUT.
    This matches the newer ‘wheel’ event.

Why are there spinX, spinY (or pixels)?

  • spinX is a 2-finger side drag on the trackpad, and a shift + wheel turn
    with a mouse. It results in side-scrolling in the browser by default.
  • spinY is what you expect – it’s the classic axis of a mouse wheel.
  • I dropped spinZ/pixelZ. It is supported by the DOM 3 ‘wheel’ event and
    probably is by browsers in conjunction with fancy 3D controllers .. but
    you know.

Implementation info:

Examples of ‘wheel’ event if you scroll slowly (down) by one step with an
average mouse:

OS X + Chrome (mouse) - 4 pixel delta (wheelDelta -120)
OS X + Safari (mouse) - N/A pixel delta (wheelDelta -12)
OS X + Firefox (mouse) - 0.1 line delta (wheelDelta N/A)
Win8 + Chrome (mouse) - 100 pixel delta (wheelDelta -120)
Win8 + Firefox (mouse) - 3 line delta (wheelDelta -120)

On the trackpad:

OS X + Chrome (trackpad) - 2 pixel delta (wheelDelta -6)
OS X + Firefox (trackpad) - 1 pixel delta (wheelDelta N/A)

On other/older browsers.. it’s more complicated as there can be multiple and
also missing delta values.

The ‘wheel’ event is more standard:

http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents

The basics is that it includes a unit, deltaMode (pixels, lines, pages), and
deltaX, deltaY and deltaZ. Some browsers provide other values to maintain
backward compatibility with older events. Those other values help us
better normalize spin speed. Example of what the browsers provide:

| event.wheelDelta | event.detail
——————+——————+————–
Safari v5/OS X | -120 | 0
Safari v5/Win7 | -120 | 0
Chrome v17/OS X | -120 | 0
Chrome v17/Win7 | -120 | 0
IE9/Win7 | -120 | undefined
Firefox v4/OS X | undefined | 1
Firefox v4/Win7 | undefined | 3

之前我一直使用 wheel 事件,现在发现并不严谨在读了 Element 的兼容写法后,我找到了上面的这个文章。

Element ui 在此基础上的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import normalizeWheel from "normalize-wheel";
const isFirefox =
typeof navigator !== "undefined" &&
navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
const mousewheel = function (element, callback) {
if (element && element.addEventListener) {
element.addEventListener(
isFirefox ? "DOMMouseScroll" : "mousewheel",
function (event) {
const normalized = normalizeWheel(event);
callback && callback.apply(this, [event, normalized]);
}
);
}
};
export default {
bind(el, binding) {
mousewheel(el, binding.value);
},
};

我们发现,Element UI 没注意到其实 normaliz-wheel 做了这件事情。
由于没看源码,没注意到:
这个 normalizeWheel.getEventType(); 这个 api
只看见了 readme。。。

1
2
3
4
5
import normalizeWheel from "normalize-wheel";
document.addEventListener("mousewheel", function (event) {
const normalized = normalizeWheel(event);
console.log(normalized.pixelX, normalized.pixelY);
});

那我们应该如何改进呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
const mousewheel = function(element, callback) {
const mouseEvent = normalizeWheel.getEventType();
element.addEventListener(mouseEvent, function(event) {
const normalized = normalizeWheel(event);
callback && callback.apply(this, [event, normalized]);
});
}
};
export default {
bind(el, binding) {
mousewheel(el, binding.value);
}
};

为什么要这样做?
请阅读源码。