跨浏览器标准化鼠标滚轮速度

发布于 2024-10-29 02:56:39 字数 2132 浏览 1 评论 0 原文

对于一个不同的问题,我撰写了此答案,包括此示例代码

在该代码中,我使用鼠标滚轮放大/缩小 HTML5 Canvas。我发现了一些代码可以规范 Chrome 和 Firefox 之间的速度差异。然而,Safari 中的缩放处理比其中任何一个都要快得多。

这是我目前拥有的代码:

var handleScroll = function(e){
  var delta = e.wheelDelta ? e.wheelDelta/40 : e.detail ? -e.detail/3 : 0;
  if (delta) ...
  return e.preventDefault() && false;
};
canvas.addEventListener('DOMMouseScroll',handleScroll,false); // For Firefox
canvas.addEventListener('mousewheel',handleScroll,false);     // Everyone else

我可以使用什么代码在 Chrome v10/11、Firefox v4、Safari v5、Opera v11 和 IE9 上滚动相同数量的鼠标滚轮来获得相同的“增量”值?

此问题相关,但有没有好的答案。

编辑:进一步调查显示,一个滚动事件“向上”是:

                  | evt.wheelDelta | evt.detail
------------------+----------------+------------
  Safari v5/Win7  |       120      |      0
  Safari v5/OS X  |       120      |      0
  Safari v7/OS X  |        12      |      0
 Chrome v11/Win7  |       120      |      0
 Chrome v37/Win7  |       120      |      0
 Chrome v11/OS X  |         3 (!)  |      0      (possibly wrong)
 Chrome v37/OS X  |       120      |      0
        IE9/Win7  |       120      |  undefined
  Opera v11/OS X  |        40      |     -1
  Opera v24/OS X  |       120      |      0
  Opera v11/Win7  |       120      |     -3
 Firefox v4/Win7  |    undefined   |     -3
 Firefox v4/OS X  |    undefined   |     -1
Firefox v30/OS X  |    undefined   |     -1

此外,在 OS X 上使用 MacBook 触控板,即使缓慢移动也会产生不同的结果:

  • 在 Safari 和 Chrome 上,鼠标滚轮的 wheelDelta 值为 3,而不是 120。
  • 在 Firefox 上,detail 通常是 2,有时是 1,但是当滚动非常缓慢时根本没有事件处理程序触发

所以问题是:

区分这种行为的最佳方法是什么(最好没有任何用户代理或操作系统嗅探)?

For a different question I composed this answer, including this sample code.

In that code I use the mouse wheel to zoom in/out of an HTML5 Canvas. I found some code that normalizes speed differences between Chrome and Firefox. However, the zoom handling in Safari is much, much faster than in either of those.

Here's the code I currently have:

var handleScroll = function(e){
  var delta = e.wheelDelta ? e.wheelDelta/40 : e.detail ? -e.detail/3 : 0;
  if (delta) ...
  return e.preventDefault() && false;
};
canvas.addEventListener('DOMMouseScroll',handleScroll,false); // For Firefox
canvas.addEventListener('mousewheel',handleScroll,false);     // Everyone else

What code can I use to get the same 'delta' value for the same amount of mouse wheel rolling across Chrome v10/11, Firefox v4, Safari v5, Opera v11 and IE9?

This question is related, but has no good answer.

Edit: Further investigation shows that one scroll event 'up' is:

                  | evt.wheelDelta | evt.detail
------------------+----------------+------------
  Safari v5/Win7  |       120      |      0
  Safari v5/OS X  |       120      |      0
  Safari v7/OS X  |        12      |      0
 Chrome v11/Win7  |       120      |      0
 Chrome v37/Win7  |       120      |      0
 Chrome v11/OS X  |         3 (!)  |      0      (possibly wrong)
 Chrome v37/OS X  |       120      |      0
        IE9/Win7  |       120      |  undefined
  Opera v11/OS X  |        40      |     -1
  Opera v24/OS X  |       120      |      0
  Opera v11/Win7  |       120      |     -3
 Firefox v4/Win7  |    undefined   |     -3
 Firefox v4/OS X  |    undefined   |     -1
Firefox v30/OS X  |    undefined   |     -1

Further, using the MacBook trackpad on OS X gives different results even when moving slowly:

  • On Safari and Chrome, the wheelDelta is a value of 3 instead of 120 for mouse wheel.
  • On Firefox the detail is usually 2, sometimes 1, but when scrolling very slowly NO EVENT HANDLER FIRES AT ALL.

So the question is:

What is the best way to differentiate this behavior (ideally without any user agent or OS sniffing)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

み青杉依旧 2024-11-05 02:56:40

绝对没有简单的方法可以在所有浏览器中的所有操作系统中的所有用户之间进行标准化。

它比你列出的变体更糟糕 - 在我的 WindowsXP+Firefox3.6 设置上,我的鼠标滚轮每一级滚动 6 次 - 可能是因为我忘记在某个地方我加速了鼠标滚轮,无论是在操作系统中还是在大约的某个地方: 然而,

我正在解决类似的问题(顺便说一句,使用类似的应用程序,但非画布),并且通过仅使用 +1 / -1 的 delta 符号并随着时间的推移进行测量,我就遇到了这个问题上次发射时,您将获得加速度,即。如果有人滚动一次在几分钟内滚动多次(我敢打赌谷歌地图就是这样做的)。

这个概念在我的测试中似乎效果很好,只需将任何小于 100 毫秒的时间添加到加速中即可。

There is definitely no simple way to normalize across all users in all OS in all browsers.

It gets worse than your listed variations - on my WindowsXP+Firefox3.6 setup my mousewheel does 6 per one-notch scroll - probably because somewhere I've forgotten I've accelerated the mouse wheel, either in the OS or somewhere in about:config

However I am working on a similar problem (with a similar app btw, but non-canvas) and it occurs to me by just using the delta sign of +1 / -1 and measuring over time the last time it fired, you'll have a rate of acceleration, ie. if someone scrolls once vs several times in a few moments (which I would bet is how google maps does it).

The concept seems to work well in my tests, just make anything less than 100ms add to the acceleration.

冧九 2024-11-05 02:56:40
var onMouseWheel = function(e) {
    e = e.originalEvent;
    var delta = e.wheelDelta>0||e.detail<0?1:-1;
    alert(delta);
}
$("body").bind("mousewheel DOMMouseScroll", onMouseWheel);
var onMouseWheel = function(e) {
    e = e.originalEvent;
    var delta = e.wheelDelta>0||e.detail<0?1:-1;
    alert(delta);
}
$("body").bind("mousewheel DOMMouseScroll", onMouseWheel);
隱形的亼 2024-11-05 02:56:39

编辑 2014 年 9 月

鉴于:

  • OS X 上同一浏览器的不同版本过去会产生不同的值,并且将来也可能会产生不同的值,并且
  • 在 OS X 上使用触控板会产生非常相似的< em>效果使用鼠标滚轮,但给出了非常不同的事件,但是JS无法检测到设备差异

......我只能推荐使用这个简单的,基于符号的-计数代码:

var handleScroll = function(evt){
  if (!evt) evt = event;
  var direction = (evt.detail<0 || evt.wheelDelta>0) ? 1 : -1;
  // Use the value as you will
};
someEl.addEventListener('DOMMouseScroll',handleScroll,false); // for Firefox
someEl.addEventListener('mousewheel',    handleScroll,false); // for everyone else

最初的正确尝试如下。

这是我第一次尝试使用脚本来规范化值。它在 OS X 上有两个缺陷:OS X 上的 Firefox 将生成应有值的 1/3,而 OS X 上的 Chrome 将生成应有值的 1/40。

// Returns +1 for a single wheel roll 'up', -1 for a single roll 'down'
var wheelDistance = function(evt){
  if (!evt) evt = event;
  var w=evt.wheelDelta, d=evt.detail;
  if (d){
    if (w) return w/d/40*d>0?1:-1; // Opera
    else return -d/3;              // Firefox;         TODO: do not /3 for OS X
  } else return w/120;             // IE/Safari/Chrome TODO: /3 for Chrome OS X
};

您可以在自己的浏览器上测试此代码: http://phrogz.net/JS/wheeldelta.html

欢迎提出检测和改进 OS X 上 Firefox 和 Chrome 行为的建议。

编辑:@Tom 的一项建议是将每个事件调用简单地计为单次移动,使用距离的符号来调整它。这不会在 OS X 上的平滑/加速滚动下产生很好的结果,也不会在鼠标滚轮移动非常快时(例如 wheelDelta 为 240)完美处理情况,但这种情况很少发生。由于此处描述的原因,该代码现在是本答案顶部显示的推荐技术。

Edit September 2014

Given that:

  • Different versions of the same browser on OS X have yielded different values in the past, and may do so in the future, and that
  • Using the trackpad on OS X yields very similar effects to using a mouse wheel, yet gives very different event values, and yet the device difference cannot be detected by JS

…I can only recommend using this simple, sign-based-counting code:

var handleScroll = function(evt){
  if (!evt) evt = event;
  var direction = (evt.detail<0 || evt.wheelDelta>0) ? 1 : -1;
  // Use the value as you will
};
someEl.addEventListener('DOMMouseScroll',handleScroll,false); // for Firefox
someEl.addEventListener('mousewheel',    handleScroll,false); // for everyone else

Original attempt to be correct follows.

Here is my first attempt at a script to normalize the values. It has two flaws on OS X: Firefox on OS X will produce values 1/3 what they should be, and Chrome on OS X will produce values 1/40 what they should be.

// Returns +1 for a single wheel roll 'up', -1 for a single roll 'down'
var wheelDistance = function(evt){
  if (!evt) evt = event;
  var w=evt.wheelDelta, d=evt.detail;
  if (d){
    if (w) return w/d/40*d>0?1:-1; // Opera
    else return -d/3;              // Firefox;         TODO: do not /3 for OS X
  } else return w/120;             // IE/Safari/Chrome TODO: /3 for Chrome OS X
};

You can test out this code on your own browser here: http://phrogz.net/JS/wheeldelta.html

Suggestions for detecting and improving the behavior on Firefox and Chrome on OS X are welcome.

Edit: One suggestion from @Tom is to simply count each event call as a single move, using the sign of the distance to adjust it. This will not give great results under smooth/accelerated scrolling on OS X, nor handle perfectly cases when the mouse wheel is moved very fast (e.g. wheelDelta is 240), but these happen infrequently. This code is now the recommended technique shown at the top of this answer, for the reasons described there.

只想待在家 2024-11-05 02:56:39

我们 Facebook 的朋友为这个问题提出了一个很好的解决方案。

我已经在使用 React 构建的数据表上进行了测试,它像黄油一样滚动!

该解决方案适用于各种浏览器、Windows/Mac,并且都使用触控板/鼠标。

// Reasonable defaults
var PIXEL_STEP  = 10;
var LINE_HEIGHT = 40;
var PAGE_HEIGHT = 800;

function normalizeWheel(/*object*/ event) /*object*/ {
  var sX = 0, sY = 0,       // spinX, spinY
      pX = 0, pY = 0;       // pixelX, pixelY

  // Legacy
  if ('detail'      in event) { sY = event.detail; }
  if ('wheelDelta'  in event) { sY = -event.wheelDelta / 120; }
  if ('wheelDeltaY' in event) { sY = -event.wheelDeltaY / 120; }
  if ('wheelDeltaX' in event) { sX = -event.wheelDeltaX / 120; }

  // side scrolling on FF with DOMMouseScroll
  if ( 'axis' in event && event.axis === event.HORIZONTAL_AXIS ) {
    sX = sY;
    sY = 0;
  }

  pX = sX * PIXEL_STEP;
  pY = sY * PIXEL_STEP;

  if ('deltaY' in event) { pY = event.deltaY; }
  if ('deltaX' in event) { pX = event.deltaX; }

  if ((pX || pY) && event.deltaMode) {
    if (event.deltaMode == 1) {          // delta in LINE units
      pX *= LINE_HEIGHT;
      pY *= LINE_HEIGHT;
    } else {                             // delta in PAGE units
      pX *= PAGE_HEIGHT;
      pY *= PAGE_HEIGHT;
    }
  }

  // Fall-back if spin cannot be determined
  if (pX && !sX) { sX = (pX < 1) ? -1 : 1; }
  if (pY && !sY) { sY = (pY < 1) ? -1 : 1; }

  return { spinX  : sX,
           spinY  : sY,
           pixelX : pX,
           pixelY : pY };
}

源代码可以在这里找到:https://github。 com/facebook/fixed-data-table/blob/master/src/vendor_upstream/dom/normalizeWheel.js

Our friends at Facebook put together a great solution to this problem.

I have tested on a data table that I'm building using React and it scrolls like butter!

This solution works on a variety of browsers, on Windows/Mac, and both using trackpad/mouse.

// Reasonable defaults
var PIXEL_STEP  = 10;
var LINE_HEIGHT = 40;
var PAGE_HEIGHT = 800;

function normalizeWheel(/*object*/ event) /*object*/ {
  var sX = 0, sY = 0,       // spinX, spinY
      pX = 0, pY = 0;       // pixelX, pixelY

  // Legacy
  if ('detail'      in event) { sY = event.detail; }
  if ('wheelDelta'  in event) { sY = -event.wheelDelta / 120; }
  if ('wheelDeltaY' in event) { sY = -event.wheelDeltaY / 120; }
  if ('wheelDeltaX' in event) { sX = -event.wheelDeltaX / 120; }

  // side scrolling on FF with DOMMouseScroll
  if ( 'axis' in event && event.axis === event.HORIZONTAL_AXIS ) {
    sX = sY;
    sY = 0;
  }

  pX = sX * PIXEL_STEP;
  pY = sY * PIXEL_STEP;

  if ('deltaY' in event) { pY = event.deltaY; }
  if ('deltaX' in event) { pX = event.deltaX; }

  if ((pX || pY) && event.deltaMode) {
    if (event.deltaMode == 1) {          // delta in LINE units
      pX *= LINE_HEIGHT;
      pY *= LINE_HEIGHT;
    } else {                             // delta in PAGE units
      pX *= PAGE_HEIGHT;
      pY *= PAGE_HEIGHT;
    }
  }

  // Fall-back if spin cannot be determined
  if (pX && !sX) { sX = (pX < 1) ? -1 : 1; }
  if (pY && !sY) { sY = (pY < 1) ? -1 : 1; }

  return { spinX  : sX,
           spinY  : sY,
           pixelX : pX,
           pixelY : pY };
}

The source code can be found here: https://github.com/facebook/fixed-data-table/blob/master/src/vendor_upstream/dom/normalizeWheel.js

最好是你 2024-11-05 02:56:39

这是我疯狂地尝试生成一个跨浏览器连贯且规范化的 delta (-1 <= delta <= 1 ) :

var o = e.originalEvent,
    d = o.detail, w = o.wheelDelta,
    n = 225, n1 = n-1;

// Normalize delta
d = d ? w && (f = w/d) ? d/f : -d/1.35 : w/120;
// Quadratic scale if |d| > 1
d = d < 1 ? d < -1 ? (-Math.pow(d, 2) - n1) / n : d : (Math.pow(d, 2) + n1) / n;
// Delta *should* not be greater than 2...
e.delta = Math.min(Math.max(d / 2, -1), 1);

这完全是经验性的,但在 Safari 6、FF 16、Opera 12 (OS X) 和 IE 上效果很好XP 上为 7

Here is my crazy attempt to produce a cross browser coherent and normalized delta ( -1 <= delta <= 1 ) :

var o = e.originalEvent,
    d = o.detail, w = o.wheelDelta,
    n = 225, n1 = n-1;

// Normalize delta
d = d ? w && (f = w/d) ? d/f : -d/1.35 : w/120;
// Quadratic scale if |d| > 1
d = d < 1 ? d < -1 ? (-Math.pow(d, 2) - n1) / n : d : (Math.pow(d, 2) + n1) / n;
// Delta *should* not be greater than 2...
e.delta = Math.min(Math.max(d / 2, -1), 1);

This is totally empirical but works quite good on Safari 6, FF 16, Opera 12 (OS X) and IE 7 on XP

且行且努力 2024-11-05 02:56:39

我制作了一个表格,其中包含不同事件/浏览器返回的不同值, 考虑到某些浏览器已经支持的 DOM3 wheel 事件(下表)。

基于此,我创建了这个函数来标准化速度:

http://jsfiddle.net/mfe8J/1/

function normalizeWheelSpeed(event) {
    var normalized;
    if (event.wheelDelta) {
        normalized = (event.wheelDelta % 120 - 0) == -0 ? event.wheelDelta / 120 : event.wheelDelta / 12;
    } else {
        var rawAmmount = event.deltaY ? event.deltaY : event.detail;
        normalized = -(rawAmmount % 3 ? rawAmmount * 10 : rawAmmount / 3);
    }
    return normalized;
}

mousewheelwheelDOMMouseScroll 事件表:

| mousewheel        | Chrome (win) | Chrome (mac) | Firefox (win) | Firefox (mac) | Safari 7 (mac) | Opera 22 (mac) | Opera 22 (win) | IE11      | IE 9 & 10   | IE 7 & 8  |
|-------------------|--------------|--------------|---------------|---------------|----------------|----------------|----------------|-----------|-------------|-----------|
| event.detail      | 0            | 0            | -             | -             | 0              | 0              | 0              | 0         | 0           | undefined |
| event.wheelDelta  | 120          | 120          | -             | -             | 12             | 120            | 120            | 120       | 120         | 120       |
| event.wheelDeltaY | 120          | 120          | -             | -             | 12             | 120            | 120            | undefined | undefined   | undefined |
| event.wheelDeltaX | 0            | 0            | -             | -             | 0              | 0              | 0              | undefined | undefined   | undefined |
| event.delta       | undefined    | undefined    | -             | -             | undefined      | undefined      | undefined      | undefined | undefined   | undefined |
| event.deltaY      | -100         | -4           | -             | -             | undefined      | -4             | -100           | undefined | undefined   | undefined |
| event.deltaX      | 0            | 0            | -             | -             | undefined      | 0              | 0              | undefined | undefined   | undefined |
|                   |              |              |               |               |                |                |                |           |             |           |
| wheel             | Chrome (win) | Chrome (mac) | Firefox (win) | Firefox (mac) | Safari 7 (mac) | Opera 22 (mac) | Opera 22 (win) | IE11      | IE 10 & 9   | IE 7 & 8  |
| event.detail      | 0            | 0            | 0             | 0             | -              | 0              | 0              | 0         | 0           | -         |
| event.wheelDelta  | 120          | 120          | undefined     | undefined     | -              | 120            | 120            | undefined | undefined   | -         |
| event.wheelDeltaY | 120          | 120          | undefined     | undefined     | -              | 120            | 120            | undefined | undefined   | -         |
| event.wheelDeltaX | 0            | 0            | undefined     | undefined     | -              | 0              | 0              | undefined | undefined   | -         |
| event.delta       | undefined    | undefined    | undefined     | undefined     | -              | undefined      | undefined      | undefined | undefined   | -         |
| event.deltaY      | -100         | -4           | -3            | -0,1          | -              | -4             | -100           | -99,56    | -68,4 | -53 | -         |
| event.deltaX      | 0            | 0            | 0             | 0             | -              | 0              | 0              | 0         | 0           | -         |
|                   |              |              |               |               |                |                |                |           |             |           |
|                   |              |              |               |               |                |                |                |           |             |           |
| DOMMouseScroll    |              |              | Firefox (win) | Firefox (mac) |                |                |                |           |             |           |
| event.detail      |              |              | -3            | -1            |                |                |                |           |             |           |
| event.wheelDelta  |              |              | undefined     | undefined     |                |                |                |           |             |           |
| event.wheelDeltaY |              |              | undefined     | undefined     |                |                |                |           |             |           |
| event.wheelDeltaX |              |              | undefined     | undefined     |                |                |                |           |             |           |
| event.delta       |              |              | undefined     | undefined     |                |                |                |           |             |           |
| event.deltaY      |              |              | undefined     | undefined     |                |                |                |           |             |           |
| event.deltaX      |              |              | undefined     | undefined     |                |                |                |           |             |           |

I made a table with different values returned by different events/browsers, taking into account the DOM3 wheel event that some browsers already support (table under).

Based on that I made this function to normalize the speed:

http://jsfiddle.net/mfe8J/1/

function normalizeWheelSpeed(event) {
    var normalized;
    if (event.wheelDelta) {
        normalized = (event.wheelDelta % 120 - 0) == -0 ? event.wheelDelta / 120 : event.wheelDelta / 12;
    } else {
        var rawAmmount = event.deltaY ? event.deltaY : event.detail;
        normalized = -(rawAmmount % 3 ? rawAmmount * 10 : rawAmmount / 3);
    }
    return normalized;
}

Table for mousewheel, wheel and DOMMouseScroll events:

| mousewheel        | Chrome (win) | Chrome (mac) | Firefox (win) | Firefox (mac) | Safari 7 (mac) | Opera 22 (mac) | Opera 22 (win) | IE11      | IE 9 & 10   | IE 7 & 8  |
|-------------------|--------------|--------------|---------------|---------------|----------------|----------------|----------------|-----------|-------------|-----------|
| event.detail      | 0            | 0            | -             | -             | 0              | 0              | 0              | 0         | 0           | undefined |
| event.wheelDelta  | 120          | 120          | -             | -             | 12             | 120            | 120            | 120       | 120         | 120       |
| event.wheelDeltaY | 120          | 120          | -             | -             | 12             | 120            | 120            | undefined | undefined   | undefined |
| event.wheelDeltaX | 0            | 0            | -             | -             | 0              | 0              | 0              | undefined | undefined   | undefined |
| event.delta       | undefined    | undefined    | -             | -             | undefined      | undefined      | undefined      | undefined | undefined   | undefined |
| event.deltaY      | -100         | -4           | -             | -             | undefined      | -4             | -100           | undefined | undefined   | undefined |
| event.deltaX      | 0            | 0            | -             | -             | undefined      | 0              | 0              | undefined | undefined   | undefined |
|                   |              |              |               |               |                |                |                |           |             |           |
| wheel             | Chrome (win) | Chrome (mac) | Firefox (win) | Firefox (mac) | Safari 7 (mac) | Opera 22 (mac) | Opera 22 (win) | IE11      | IE 10 & 9   | IE 7 & 8  |
| event.detail      | 0            | 0            | 0             | 0             | -              | 0              | 0              | 0         | 0           | -         |
| event.wheelDelta  | 120          | 120          | undefined     | undefined     | -              | 120            | 120            | undefined | undefined   | -         |
| event.wheelDeltaY | 120          | 120          | undefined     | undefined     | -              | 120            | 120            | undefined | undefined   | -         |
| event.wheelDeltaX | 0            | 0            | undefined     | undefined     | -              | 0              | 0              | undefined | undefined   | -         |
| event.delta       | undefined    | undefined    | undefined     | undefined     | -              | undefined      | undefined      | undefined | undefined   | -         |
| event.deltaY      | -100         | -4           | -3            | -0,1          | -              | -4             | -100           | -99,56    | -68,4 | -53 | -         |
| event.deltaX      | 0            | 0            | 0             | 0             | -              | 0              | 0              | 0         | 0           | -         |
|                   |              |              |               |               |                |                |                |           |             |           |
|                   |              |              |               |               |                |                |                |           |             |           |
| DOMMouseScroll    |              |              | Firefox (win) | Firefox (mac) |                |                |                |           |             |           |
| event.detail      |              |              | -3            | -1            |                |                |                |           |             |           |
| event.wheelDelta  |              |              | undefined     | undefined     |                |                |                |           |             |           |
| event.wheelDeltaY |              |              | undefined     | undefined     |                |                |                |           |             |           |
| event.wheelDeltaX |              |              | undefined     | undefined     |                |                |                |           |             |           |
| event.delta       |              |              | undefined     | undefined     |                |                |                |           |             |           |
| event.deltaY      |              |              | undefined     | undefined     |                |                |                |           |             |           |
| event.deltaX      |              |              | undefined     | undefined     |                |                |                |           |             |           |
夜灵血窟げ 2024-11-05 02:56:39

另一个或多或少的独立解决方案……

这并不需要在事件之间考虑时间。一些浏览器似乎总是用相同的三角洲发射事件,然后快速滚动时更快地发射它们。其他人确实改变了三角洲。可以想象一个需要时间的自适应标准器,但这会在某种程度上参与其中和尴尬。

在这里可用的工作: jsbin/iqafek/2

var normalizeWheelDelta = function() {
  // Keep a distribution of observed values, and scale by the
  // 33rd percentile.
  var distribution = [], done = null, scale = 30;
  return function(n) {
    // Zeroes don't count.
    if (n == 0) return n;
    // After 500 samples, we stop sampling and keep current factor.
    if (done != null) return n * done;
    var abs = Math.abs(n);
    // Insert value (sorted in ascending order).
    outer: do { // Just used for break goto
      for (var i = 0; i < distribution.length; ++i) {
        if (abs <= distribution[i]) {
          distribution.splice(i, 0, abs);
          break outer;
        }
      }
      distribution.push(abs);
    } while (false);
    // Factor is scale divided by 33rd percentile.
    var factor = scale / distribution[Math.floor(distribution.length / 3)];
    if (distribution.length == 500) done = factor;
    return n * factor;
  };
}();

// Usual boilerplate scroll-wheel incompatibility plaster.

var div = document.getElementById("thing");
div.addEventListener("DOMMouseScroll", grabScroll, false);
div.addEventListener("mousewheel", grabScroll, false);

function grabScroll(e) {
  var dx = -(e.wheelDeltaX || 0), dy = -(e.wheelDeltaY || e.wheelDelta || 0);
  if (e.detail != null) {
    if (e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
    else if (e.axis == e.VERTICAL_AXIS) dy = e.detail;
  }
  if (dx) {
    var ndx = Math.round(normalizeWheelDelta(dx));
    if (!ndx) ndx = dx > 0 ? 1 : -1;
    div.scrollLeft += ndx;
  }
  if (dy) {
    var ndy = Math.round(normalizeWheelDelta(dy));
    if (!ndy) ndy = dy > 0 ? 1 : -1;
    div.scrollTop += ndy;
  }
  if (dx || dy) { e.preventDefault(); e.stopPropagation(); }
}

Another more or less self-contained solution...

This doesn't take time between events into account though. Some browsers seem to always fire events with the same delta, and just fire them faster when scrolling quickly. Others do vary the deltas. One can imagine an adaptive normalizer that takes time into account, but that'd get somewhat involved and awkward to use.

Working available here: jsbin/iqafek/2

var normalizeWheelDelta = function() {
  // Keep a distribution of observed values, and scale by the
  // 33rd percentile.
  var distribution = [], done = null, scale = 30;
  return function(n) {
    // Zeroes don't count.
    if (n == 0) return n;
    // After 500 samples, we stop sampling and keep current factor.
    if (done != null) return n * done;
    var abs = Math.abs(n);
    // Insert value (sorted in ascending order).
    outer: do { // Just used for break goto
      for (var i = 0; i < distribution.length; ++i) {
        if (abs <= distribution[i]) {
          distribution.splice(i, 0, abs);
          break outer;
        }
      }
      distribution.push(abs);
    } while (false);
    // Factor is scale divided by 33rd percentile.
    var factor = scale / distribution[Math.floor(distribution.length / 3)];
    if (distribution.length == 500) done = factor;
    return n * factor;
  };
}();

// Usual boilerplate scroll-wheel incompatibility plaster.

var div = document.getElementById("thing");
div.addEventListener("DOMMouseScroll", grabScroll, false);
div.addEventListener("mousewheel", grabScroll, false);

function grabScroll(e) {
  var dx = -(e.wheelDeltaX || 0), dy = -(e.wheelDeltaY || e.wheelDelta || 0);
  if (e.detail != null) {
    if (e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
    else if (e.axis == e.VERTICAL_AXIS) dy = e.detail;
  }
  if (dx) {
    var ndx = Math.round(normalizeWheelDelta(dx));
    if (!ndx) ndx = dx > 0 ? 1 : -1;
    div.scrollLeft += ndx;
  }
  if (dy) {
    var ndy = Math.round(normalizeWheelDelta(dy));
    if (!ndy) ndy = dy > 0 ? 1 : -1;
    div.scrollTop += ndy;
  }
  if (dx || dy) { e.preventDefault(); e.stopPropagation(); }
}
风吹短裙飘 2024-11-05 02:56:39

简单且可行的解决方案:

private normalizeDelta(wheelEvent: WheelEvent):number {
    var delta = 0;
    var wheelDelta = wheelEvent.wheelDelta;
    var deltaY = wheelEvent.deltaY;
    // CHROME WIN/MAC | SAFARI 7 MAC | OPERA WIN/MAC | EDGE
    if (wheelDelta) {
        delta = -wheelDelta / 120; 
    }
    // FIREFOX WIN / MAC | IE
    if(deltaY) {
        deltaY > 0 ? delta = 1 : delta = -1;
    }
    return delta;
}

Simple and working solution:

private normalizeDelta(wheelEvent: WheelEvent):number {
    var delta = 0;
    var wheelDelta = wheelEvent.wheelDelta;
    var deltaY = wheelEvent.deltaY;
    // CHROME WIN/MAC | SAFARI 7 MAC | OPERA WIN/MAC | EDGE
    if (wheelDelta) {
        delta = -wheelDelta / 120; 
    }
    // FIREFOX WIN / MAC | IE
    if(deltaY) {
        deltaY > 0 ? delta = 1 : delta = -1;
    }
    return delta;
}
月棠 2024-11-05 02:56:39

这是我今天困扰了几个小时的问题,而且不是第一次了:(

我一直在尝试通过“滑动”来总结值,并查看不同浏览器如何报告值,它们差异很大,Safari 在几乎所有平台上报告的数量级都更大,Chrome 报告的数量比 firefox 多得多(比如 3 倍),firefox 从长远来看是平衡的,但在小幅变化的平台之间差异很大(在 Ubuntu gnome 上,几乎只 + 3或-3,似乎它总结了较小的事件,然后发送一个大的“+3”)

目前找到的解决方案有三个:

  1. 已经提到的“仅使用符号”,它会杀死任何类型的加速
  2. 嗅探浏览器 Qooxdoo 最近实现
  3. 了一种自适应算法,该算法基本上尝试根据迄今为止收到的最小值和最大值来缩放增量。Qooxdoo

中的想法很好,并且有效,并且是我唯一的解决方案。目前发现跨浏览器完全一致。

不幸的是,它也倾向于重新标准化加速度。如果您尝试(在他们的演示中),并以最大速度上下滚动一段时间,您会发现极快或极慢的滚动基本上会产生几乎相同的移动量。相反,如果您重新加载页面并且仅非常缓慢地滑动,您会发现它会滚动得非常快”。

这对于习惯在触摸板上进行剧烈滚动滑动并期望得到结果的 Mac 用户(例如我)来说是令人沮丧的 。

更重要的是,由于它会根据获得的最大值来降低鼠标速度,因此用户尝试加快速度的次数越多,速度就会减慢得越多,而“缓慢滚动” 用户将体验到相当快的速度。

这使得这个(否则很出色)解决方案成为解决方案 1 的更好的实现。

我将该解决方案移植到了 jquery mousewheel 插件:http://jsfiddle.net/SimoneGianni/pXzVv/

如果您使用它一段时间,您会发现您将开始获得相当均匀的结果,但是您会还注意到它倾向于很快地+1/-1值,

我现在正在努力增强它以更好地检测峰值,这样它们就不会发送“超出范围”的所有内容。最好还获得 0 到 1 之间的浮点值作为增量值,以便有一致的输出。

This is a problem I've been fighting with for some hours today, and not for the first time :(

I've been trying to sum up values over a "swipe" and see how different browsers report values, and they vary a lot, with Safari reporting order of magnitude bigger numbers on almost all platforms, Chrome reporting quite more (like 3 times more) than firefox, firefox being balanced on the long run but quite different among platforms on small movements (on Ubuntu gnome, nearly only +3 or -3, seems like it sums up smaller events and then send a big "+3")

The current solutions found right now are three :

  1. The already mentioned "use only the sign" which kills any kind of acceleration
  2. Sniff the browser up to minor version and platform, and adjust properly
  3. Qooxdoo recently implemented a self adapting algorithm, which basically tries to scale the delta based on minimum and maximum value received so far.

The idea in Qooxdoo is good, and works, and is the only solution I've currently found to be completely consistent cross browser.

Unfortunately it tends to renormalize also the acceleration. If you try it (in their demos), and scroll up and down at maximum speed for a while, you'll notice that scrolling extremely fast or extremely slow basically produce nearly the same amount of movement. On the opposite if you reload the page and only swipe very slowly, you'll notice that it will scroll quite fast".

This is frustrating for a Mac user (like me) used to give vigorous scroll swipes on the touchpad and expecting to get to the top or bottom of the scrolled thing.

Even more, since it scales down the mouse speed based on the maximum value obtained, the more your user tries to speed it up, the more it will slow down, while a "slow scrolling" user will experience quite fast speeds.

This makes this (otherwise brilliant) solution a slightly better implementation of solution 1.

I ported the solution to the jquery mousewheel plugin : http://jsfiddle.net/SimoneGianni/pXzVv/

If you play with it for a while, You'll see that you'll start getting quite homogeneous results, but you'll also notice that it tend to +1/-1 values quite fast.

I'm now working on enhancing it to detect peaks better, so that they don't send everything "out of scale". It would also be nice to also obtain a float value between 0 and 1 as the delta value, so that there is a coherent output.

執念 2024-11-05 02:56:39

要在触摸设备上支持缩放,请注册gesturestart、gesturechange和gestureend事件并使用event.scale属性。您可以看到 示例代码

对于 Firefox 17,桌面版和移动版计划支持 onwheel 事件(根据 onwheel 上的 MDN 文档)。另外,对于 Firefox,也许 Gecko 特定的 MozMousePixelScroll 事件很有用(尽管现在可能已弃用该事件,因为 DOMMouseWheel 事件现已在 Firefox 中弃用)。

对于 Windows,驱动程序本身似乎会生成 WM_MOUSEWHEEL、WM_MOUSEHWHEEL 事件(也许还生成用于触摸板平移的 WM_GESTURE 事件?)。这可以解释为什么 Windows 或浏览器似乎没有标准化鼠标滚轮事件值本身(并且可能意味着您无法编写可靠的代码来标准化这些值)。

对于onwheel不是 onmousewheel)事件在 Internet Explorer 中支持 IE9 和 IE10,您还可以使用 W3C 标准 onwheel 事件。然而,一个档位可以是不同于 120 的值(例如,在我的鼠标上,一个档位变为 111(而不是 -120)使用此测试页面)。我写了另一篇文章,其中包含可能相关的其他详细轮子事件。

基本上,在我自己对滚轮事件的测试中(我试图标准化滚动的值),我发现我得到了不同的操作系统、浏览器供应商、浏览器版本、事件类型和设备(Microsoft 倾斜滚轮鼠标、笔记本电脑触摸板手势)的值、带滚动区的笔记本电脑触摸板、Apple 魔术鼠标、Apple 强大的鼠标滚动球、Mac 触摸板等)。

并且必须忽略浏览器配置(例如 Firefox mousewheel.enable_pixel_scrolling、chrome --scroll-pixels=150)、驱动程序设置(例如 Synaptics 触摸板)和操作系统配置(Windows 鼠标设置、OSX 鼠标首选项、 X.org 按钮设置)。

For zoom support on touch devices, register for the gesturestart, gesturechange and gestureend events and use the event.scale property. You can see example code for this.

For Firefox 17 the onwheel event is planned to be supported by desktop and mobile versions (as per MDN docs on onwheel). Also for Firefox maybe the Gecko specific MozMousePixelScroll event is useful (although presumably this is now deprecated since the DOMMouseWheel event is now deprecated in Firefox).

For Windows, the driver itself seems to generate the WM_MOUSEWHEEL, WM_MOUSEHWHEEL events (and maybe the WM_GESTURE event for touchpad panning?). That would explain why Windows or the browser doesn't seem to normalise the mousewheel event values itself (and might mean you cannot write reliable code to normalise the values).

For onwheel (not onmousewheel) event support in Internet Explorer for IE9 and IE10, you can also use the W3C standard onwheel event. However one notch can be a value different from 120 (e.g. a single notch becomes 111 (instead of -120) on my mouse using this test page). I wrote another article with other details wheel events that might be relevant.

Basically in my own testing for wheel events (I am trying to normalise the values for scrolling), I have found that I get varying values for OS, browser vendor, browser version, event type, and device (Microsoft tiltwheel mouse, laptop touchpad gestures, laptop touchpad with scrollzone, Apple magic mouse, Apple mighty mouse scrollball, Mac touchpad, etc etc).

And have to ignore a variety of side-effects from browser configuration (e.g. Firefox mousewheel.enable_pixel_scrolling, chrome --scroll-pixels=150), driver settings (e.g. Synaptics touchpad), and OS configuration (Windows mouse settings, OSX Mouse preferences, X.org button settings).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文