[javascript] 브라우저에서 마우스 휠 속도 정상화

대한 다른 질문 나는 구성 이 답변 을 포함하여, 이 샘플 코드를 .

이 코드에서 마우스 휠을 사용하여 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
------------------ + ---------------- + ------------
  사파리 v5 / Win7 | 120 | 0
  Safari v5 / OS X | 120 | 0
  Safari v7 / OS X | 12 | 0
 크롬 v11 / Win7 | 120 | 0
 크롬 v37 / Win7 | 120 | 0
 Chrome v11 / OS X | 3 (!) | 0 (아마도 틀렸다)
 Chrome v37 / OS X | 120 | 0
        IE9 / Win7 | 120 | 찾으시는 주소가 없습니다
  오페라 v11 / OS X | 40 | -1
  오페라 v24 / OS X | 120 | 0
  오페라 v11 / Win7 | 120 | -삼
 Firefox v4 / Win7 | 미정의 | -삼
 Firefox v4 / OS X | 미정의 | -1
Firefox v30 / OS X | 미정의 | -1

또한 OS X에서 MacBook 트랙 패드를 사용하면 느리게 움직일 때도 다른 결과를 얻을 수 있습니다.

  • Safari 및 Chrome에서 wheelDelta마우스 휠의 값은 120이 아니라 3입니다.
  • 파이어 폭스에서는 detail보통 2때로 사용 1하지만 때로는 매우 느리게 스크롤 할 때 이벤트 핸들러가 전혀 발생하지 않습니다 .

따라서 질문은 다음과 같습니다.

이 동작을 차별화하는 가장 좋은 방법은 무엇입니까 (이상적으로 사용자 에이전트 또는 OS 스니핑 없음)?



답변

2014 년 9 월 수정

을 고려하면:

  • OS X에서 동일한 브라우저의 다른 버전은 과거에 다른 값을 산출했으며 앞으로 그렇게 할 수도 있습니다.
  • OS X에서 트랙 패드 를 사용하면 마우스 휠을 사용하는 것과 매우 유사한 효과 를 얻을 수 있지만 매우 다른 이벤트 값을 제공 하지만 장치 차이는 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/4의 값을 생성합니다.

// 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에서 부드럽게 / 가속 스크롤 할 때 큰 결과를 얻지 못하고 마우스 휠이 매우 빠르게 움직일 때 (예 : wheelDelta240) 완벽하게 처리 할 수는 없지만 드물게 발생합니다. 이 코드는 이제 여기에 설명 된 이유로이 답변의 맨 위에 표시되는 권장 기술입니다.


답변

크로스 브라우저 일관성 있고 표준화 된 델타 (-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);

이것은 완전히 경험적이지만 XP의 Safari 6, FF 16, Opera 12 (OS X) 및 IE 7에서 잘 작동합니다.


답변

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 에서 찾을 수 있습니다.


답변

일부 브라우저에서 이미 지원 하는 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;
}

mousewheel, wheelDOMMouseScroll이벤트 :

| 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     |                |                |                |           |             |           |


답변

어느 정도 독립적 인 솔루션 …

그러나 이벤트 사이에는 시간이 걸리지 않습니다. 일부 브라우저는 항상 동일한 델타로 이벤트를 발생시키는 것으로 보이며 빠르게 스크롤 할 때 더 빠르게 작동합니다. 다른 사람들은 델타를 변경합니다. 시간을 고려한 적응 형 노멀 라이저를 상상할 수 있지만 다소 복잡하고 사용하기 어려울 수 있습니다.

여기서 작업 가능 : 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(); }
}


답변

간단하고 효과적인 솔루션 :

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


답변

터치 장치에서 확대 / 축소를 지원하려면 동작 시작, 동작 변경 및 동작 종료 이벤트를 등록하고 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과 다른 값일 수 있습니다 (예 : 이 테스트 페이지를 사용하여 마우스에서 단일 노치가 마우스에서 -120 대신 111 이 됨 ). 관련된 다른 휠 이벤트와 함께 다른 기사 를 썼습니다 .

기본적으로 휠 이벤트에 대한 자체 테스트 (스크롤 값을 정규화하려고합니다)에서 OS, 브라우저 공급 업체, 브라우저 버전, 이벤트 유형 및 장치 (Microsoft 틸트 휠 마우스, 랩톱 터치 패드 제스처)에 대해 다양한 값을 얻습니다. 스크롤 영역이있는 노트북 터치 패드, Apple 매직 마우스, Apple 강력한 마우스 스크롤 볼, Mac 터치 패드 등).

브라우저 구성 (예 : Firefox mousewheel.enable_pixel_scrolling, chrome –scroll-pixels = 150), 드라이버 설정 (예 : Synaptics 터치 패드) 및 OS 구성 (Windows 마우스 설정, OSX 마우스 환경 설정, X.org 버튼 설정).