Подтвердить что ты не робот

Какова высота "линии" в событии колеса? (deltaMode = DOM_DELTA_LINE)

Событие wheel в Firefox >= 17 имеет свойство deltaMode. При использовании OS/mouse я использую значение 1 (или DOM_DELTA_LINE). Этот параметр означает, что значения событий deltaX и deltaY измеряются в строках, а не в пикселях. Конечно, если я притворяюсь, что дельта - пиксели, скорость прокрутки намного медленнее, чем обычно в Firefox.

В Chrome 31 используется deltaMode 0 (или DOM_DELTA_PIXEL), что позволяет мне моделировать прокрутку с нормальной скоростью.

Если бы я смог преобразовать значения строк в значения пикселей, я бы установил все. Но я не могу найти отрывок документации о том, что такое "линия". Я попытался изменить font-size и line-height в Firefox, что не изменило поведение прокрутки.

Кто-нибудь знает, как определяется "строка"? W3C просто говорит: "Это касается многих элементов управления формой".

W3C deltaMode

MDN WheelEvent

MDN wheel

Изменить: здесь скрипку, чтобы продемонстрировать странность. Когда Firefox находится в режиме DOM_DELTA_LINE, нет постоянного отношения между пикселями и линиями - это повсюду. И когда я переключаюсь на использование трекпада вместо мыши, вызывая переход Firefox в режим DOM_DELTA_PIXEL, также нет согласованного отношения. С другой стороны, в Chrome 31 отношение почти всегда очень близко к 1:1 в режиме DOM_DELTA_PIXEL.

Проблема с хромом: выполнить событие колеса DOM3

Ошибка Bugzilla: выполнить событие колеса DOM3

Обновление:. Прокрутка одним тиком колесика мыши в Firefox, где deltaMode - DOM_DELTA_LINE, дельта пикселя зависит от CSS font-size, но не от line-height. См. эту скрипку для демонстрации. Такое поведение наблюдается только при очень медленном нажатии колеса. Со скоростью или импульсом отношение линии к пикселю не предсказуемо в любом конкретном случае или в совокупности. Насколько я могу судить, не существует способа эмулировать поведение прокрутки Firefox с использованием дельта-измерений, предоставляемых в режиме DOM_DELTA_LINE.

В режиме DOM_DELTA_PIXEL поведение почти идеально соответствует пикселю. То есть отношение между фактическими пикселями, прокручиваемыми и сообщенным значением дельта пикселя, составляет почти ровно 1, что показано в той же самой скрипке.

I подал ошибку с Mozilla, утверждая, что поведение события wheel в DOM_DELTA_LINE не полезно, поскольку оно не предсказуемо ( т.е. это уравнение, где и единица, и величина являются переменными). Проблема была отмечена как недопустимая, поскольку ожидаемое поведение для события wheel должно проходить через собственные дельта, предоставляемые ОС, несмотря на то, что сам Firefox не соблюдает эти дельта.

Я оставлю этот вопрос открытым, надеясь, что DOM_DELTA_LINE будет определен спецификацией где-нибудь. Насколько я знаю, зависимость от font-size (а не line-height) еще нигде не описана.

4b9b3361

Ответ 1

Обзор:

В то время как значение прокрутки, полученное в результате DOM_DELTA_LINE, может быть не определено конкретно ни одной спецификацией, на основе следующего комментария от трекера Chromium и моих собственных наблюдений, похоже, что Firefox в настоящее время является единственным браузером, который будет сообщать о событиях колес с чем-либо, кроме deltaMode, как DOM_DELTA_PIXEL (0).

Комментарий из проблемы Chromium Реализовать событие колеса DOM3:

Мы закончили работу над тем, что делает IE, и сообщаем точное количество пикселей, которые мы прокручиваем.

Следующие утверждения всегда верны (учитывая, что элемент может быть прокрутка).

element.scrollTop = 0;
element.addEventListener('wheel', function(e) {
  assert(e.deltaMode === MouseEvent. DOM_DELTA_PIXEL);
  assert(element.scrollTop === e.deltaY);
});
// scroll

Таким образом вы точно знаете, сколько прокрутки в любое время.

Мы никогда не устанавливаем deltaMode на что-либо еще, кроме DOM_DELTA_PIXEL.

Согласно этому комментарию, Chromium соответствует только дельтам IE-пикселя. Хотя это и не охвачено, это почти наверняка распространяется непосредственно на современную Опера и Сафари. Мои собственные наблюдения от тестирования на Window, Mac и Linux с помощью нескольких устройств ввода не опровергли это.

Так как Firefox является единственным браузером, который сообщит deltaMode из DOM_DELTA_LINE (1) или DOM_DELTA_PAGE (2), пока что все равно, нам нужно знать только то, что вычисляет Firefox эти значения как. Что ж, когда ситуация становится немного сложной, но через поиск через источник Firefox и некоторые пробные версии и ошибки я определил, что он напрямую соответствует шрифту по умолчанию и размеру шрифта по умолчанию. В частности, они настроены в следующих настройках, игнорируя любой CSS на странице.

Параметры настроек шрифтов Firefox

Это означает, что для правильного определения высоты строки, используемой при прокрутке, у вас должен быть полностью нестроенный встроенный элемент для определения вычисленной высоты рендеринга. Поскольку CSS на странице почти наверняка будет мешать, мы должны создать новое и чистое окно в iframe для обнаружения.

Обнаружение высоты строки, используемой DOM_DELTA_LINE событиями с прокруткой:

Для этой цели я создал следующую небольшую функцию для синхронного обнаружения чистой неизменной высоты строки.

function getScrollLineHeight() {
    var r;
    var iframe = document.createElement('iframe');
    iframe.src = '#';
    document.body.appendChild(iframe);
    var iwin = iframe.contentWindow;
    var idoc = iwin.document;
    idoc.open();
    idoc.write('<!DOCTYPE html><html><head></head><body><span>a</span></body></html>');
    idoc.close();
    var span = idoc.body.firstElementChild;
    r = span.offsetHeight;
    document.body.removeChild(iframe);
    return r;
}

// Get the native croll line height.
console.log(getScrollLineHeight());

Итак, теперь мы знаем, сколько дельта треугольника линии должно перевести в пиксели. Однако в Window есть еще одна вещь, которую вам, возможно, потребуется принять во внимание. В Windows скорость прокрутки по умолчанию переопределяется, чтобы по умолчанию она была 2x быстрее, но только для корневого элемента.

Переопределить систему скорости прокрутки системы:

Мы предоставляем механизм переопределения скорости прокрутки системы, поскольку скорость прокрутки по умолчанию в системе Windows медленнее, чем скорость прокрутки WebKit. Это было предложено для альтернативного способа системы ускорения (см. Следующий раздел).

Это включено в настройках по умолчанию только в Windows. mousewheel.system_scroll_override_on_root_content.enabled может переключать его.

В Windows, только если настройки скорости прокрутки системы не настроены пользователем или драйвером мыши, это отменяет скорость прокрутки. В остальных это всегда отменяет скорость.

Отношение может быть указано с помощью скрытых префов. mousewheel.system_scroll_override_on_root_content.vertical.factor предназначен для события вертикальной прокрутки. mousewheel.system_scroll_override_on_root_content.horizontal.factor предназначен для горизонтального прокрутки. Значения используются как 1/100 (то есть значение 200 по умолчанию означает 2.0).

nsEventStateManager умножает скорость прокрутки на коэффициент, когда он выполняется, чтобы прокрутить прокручиваемый вид документа root. Таким образом, значение дельта-значения DOMMouseScroll никогда не было перезаписано этим.

См. также ошибка 513817.

Это применимо по умолчанию к корню документа, прокрутка элементов внутри документа, например, textarea, не затрагивается (кроме, возможно, iframes?). Также нет способа получить указанное значение, если значение по умолчанию 2x переконфигурировано, поэтому, если вы считаете это значение важным, вам придется прибегать к обнюханию операционной системы с помощью пользовательского агента, возможно, с технически нестандартным, но популярным navigator.platform.

Как насчет DOM_DELTA_PAGE?:

В Windows также есть альтернативная настройка для вертикальной прокрутки, где вместо прокрутки по количеству строк она прокручивается на почти полной странице. Чтобы уточнить, это диалог с настройкой "Один экран за раз", управляющий этим в Windows 7.

Настройки колеса Windows 7

Когда это будет активировано, один клик будет прокручивать почти полную страницу, здесь это означает высоту панели прокрутки, за вычетом полос прокрутки. Я говорю почти, потому что Firefox учитывает некоторые другие вещи, чтобы уменьшить количество прокрутки. А именно, сокращение на некоторые строки, процент и как-то вычитание заголовков и нижних колонтитулов фиксированной позиции. См. Следующий блок кода для некоторой логики.

Выдержка из layout/generic/nsGfxScrollFrame.cpp:

nsSize
ScrollFrameHelper::GetPageScrollAmount() const
{
  nsSize lineScrollAmount = GetLineScrollAmount();
  nsSize effectiveScrollPortSize;
  if (mIsRoot) {
    // Reduce effective scrollport height by the height of any fixed-pos
    // headers or footers
    nsIFrame* root = mOuter->PresContext()->PresShell()->GetRootFrame();
    effectiveScrollPortSize =
      GetScrollPortSizeExcludingHeadersAndFooters(root, mScrollPort);
  } else {
    effectiveScrollPortSize = mScrollPort.Size();
  }
  // The page increment is the size of the page, minus the smaller of
  // 10% of the size or 2 lines.
  return nsSize(
    effectiveScrollPortSize.width -
      std::min(effectiveScrollPortSize.width/10, 2*lineScrollAmount.width),
    effectiveScrollPortSize.height -
      std::min(effectiveScrollPortSize.height/10, 2*lineScrollAmount.height));
}

Я не уверен на 100%, что точно определено как элемент заголовка и нижнего колонтитула фиксированной позиции, а что нет, но это должно дать довольно хороший обзор того, как работает режим DOM_DELTA_PAGE, в дополнение к DOM_DELTA_LINE, о котором спрашивает этот вопрос.

Ответ 2

Я попробовал вашу скрипку с Firefox 25 (в Linux) - и расширил ее для отслеживания scrollTop только на событиях с колесами. В вашей скрипке прокручивающаяся дельта рассчитывается в событии прокрутки, которое срабатывает гораздо чаще, чем событие колеса. Кроме того, прокрутка событий в режиме мягкой прокрутки кажется отсталой по сравнению с событиями колес. (I.e. если вы измеряете, пока мягкая прокрутка еще не закончена, тогда вы получите "промежуточные" значения для scrollTop().) Я думаю, что это причины, по которым вы не получаете хорошей корреляции между прокруткой delta и колесными событиями. (Примечание. Событие колеса, по-видимому, обрабатывается перед связанными событиями прокрутки.)

Если мягкая прокрутка выключена, тогда на событие прокрутки есть хотя бы одно событие колеса. Ваша скрипка дает совершенно постоянную "px/DOM_DELTA_LINE: 18" во время "поворота" для меня. Если я добавлю div с двумя пролетами <span id="lineN">N</span> и a <br/> между и измерением ($("#line2").position().top - $("#line1").position().top), тогда я получу ровно 18.

Сводка: Событие колеса позволяет рассчитать расстояние прокрутки в пикселях - по крайней мере, в моей среде. Надеюсь, это верно и для вас.

Вот код JavaScript моей расширенной скрипки для вашей скрипки с моими расширениями:

var DeltaModes = {
    0: "DOM_DELTA_PIXEL",
    1: "DOM_DELTA_LINE",
    2: "DOM_DELTA_PAGE"
};
var statsPane = $(".stats-pane");
var scrollTop = $(window).scrollTop();
var scrollDelta = 0;
var wheelEvents = 0;
var scrollEvents = 0;
var scrollTopOnWheel = 0;
$(window).scroll(function(){
    scrollEvents++;
    var newScrollTop = $(window).scrollTop();
    scrollDelta = scrollTop - newScrollTop;
    scrollTop = newScrollTop;
});
$(window).on("wheel", function(e){
    wheelEvents++;
    var wheelScrollTop = $(window).scrollTop();
    var wheelScrollDelta = wheelScrollTop - scrollTopOnWheel;
    e = e.originalEvent;
    var pxPerDeltaUnit = Math.abs(scrollDelta) / Math.abs(e.deltaY);
    var deltaMode = DeltaModes[e.deltaMode];
    var stats = [deltaMode + ": " + e.deltaY];
    stats.push("px delta: " + scrollDelta);
    stats.push("px/" + deltaMode + ": " + pxPerDeltaUnit);
    stats.push("wheel scroll top (prev): " + scrollTopOnWheel);
    stats.push("wheel scroll top: " + wheelScrollTop);
    stats.push("wheel scroll delta: " + wheelScrollDelta);
    stats.push("line height: " + ($("#line2").position().top - $("#line1").position().top));
    stats.push("wheel event#: " + wheelEvents);
    stats.push("scroll event#: " + scrollEvents);
    statsPane.html('<div>' + stats.join('</div><div>') + '</div>');
    scrollTopOnWheel = wheelScrollTop;
    //scrollTop = newScrollTop;
});

И HTML:

<div class="stats-pane"></div>
<div>Hey.</div>
<div>Hi.</div>
<div>Hello.</div>
<div>
    <span id="line1">1</span><br/>
    <span id="line2">2</span><br/>
</div>

Ответ 3

Теперь есть немного более простой способ получить высоту строки прокрутки (размер шрифта браузера по умолчанию) без использования фреймов с помощью font-size: initial.

function getScrollLineHeight() {
  const el = document.createElement('div');
  el.style.fontSize = 'initial';
  el.style.display = 'none';
  document.body.appendChild(el);
  const fontSize = window.getComputedStyle(el).fontSize;
  document.body.removeChild(el);
  return fontSize ? window.parseInt(fontSize) : undefined;
}

// Get the native scroll line height
console.log(getScrollLineHeight());

Я проверял это на последних версиях Chrome, Firefox, Safari (протестировано 9 и 12), Edge и IE11 на странице, которая переопределяет размер шрифта элементов html и body. Кажется, это работает надежно во всех других протестированных браузерах, кроме IE11. В IE11 он возвращает унаследованный размер шрифта из body, поскольку IE11 не поддерживает ключевое слово CSS initial.

Так что, если вам больше не нужна поддержка IE11, это должно сработать.

Ответ 4

Я не смотрел ваш код, но именно так я решил свою проблему, которая привела меня сюда.

Вы можете попробовать переопределить прокрутку браузера с помощью своего пользовательского.

window.addEventListener("wheel", function(e){
 if(e.deltaY > 0)
   window.scroll(0,20);
 else
   window.scroll(0,-20)

 e.preventDefault();
});

Таким образом, вы всегда будете уверены в количестве прокручиваемых пикселей.

Ответ 5

Я обнаружил, что использование следующего фрагмента разрешает проблему. Он нормализует deltaX и deltaY. Попробуйте это.

function onWheel(e) {
    var deltaX = Math.max(-1, Math.min(1, (e.deltaX)));
    var deltaY = Math.max(-1, Math.min(1, (e.deltaY)));
    console.log('deltaX: ' + deltaX + ', deltaY: ' + deltaY);
}

Где e находится WheelEvent объект.