web-dev-qa-db-ja.com

ホイールイベントの「ライン」の高さはどれくらいですか? (deltaMode = DOM_DELTA_LINE)

Firefox> = 17のwheelイベントには、deltaModeプロパティがあります。私が使用しているOS /マウスでは、1(またはDOM_DELTA_LINE)に設定されています。この設定は、deltaXおよびdeltaYイベント値がピクセルではなく線で測定されることを意味します。確かに、デルタがピクセルであると偽った場合、スクロール速度はFirefoxの通常よりもはるかに遅くなります。

対照的に、Chrome31は0(またはDOM_DELTA_PIXEL)のdeltaModeを使用します。これにより、通常の速度でスクロールをシミュレートできます。

ライン値をピクセル値に変換できれば、すべて設定されます。しかし、「線」とは何かについてのドキュメントのスクラップを見つけることができません。 Firefoxでfont-sizeline-heightを変更してみましたが、スクロール動作は変わりませんでした。

「線」がどのように定義されているか知っている人はいますか? W3Cは、「これは多くのフォームコントロールに当てはまります」と言っています。

W3C deltaMode

MDN WheelEvent

MDN wheel

編集:ここに フィドル 奇妙さを示します。 FirefoxがDOM_DELTA_LINEモードの場合、ピクセルとラインの間に一貫した比率はありません-それはいたるところにあります。また、マウスの代わりにトラックパッドを使用するように切り替えて、FirefoxをDOM_DELTA_PIXELモードに切り替えると、一貫した比率もありません。一方、Chrome 31では、比率はDOM_DELTA_PIXELモードではほとんどの場合1:1に非常に近くなります。

Chromiumの問題:DOM3ホイールイベントの実装

Bugzillaのバグ:DOM3ホイールイベントを実装

更新:Firefoxでマウスホイールを1ティッククリックすると、deltaModeDOM_DELTA_LINEになり、ピクセルデルタはCSS font-sizeですが、line-heightにはありません。デモについては このフィドル を参照してください。この動作は、ホイールを非常にゆっくりと刻むときにのみ当てはまります。速度または勢いがある場合、ラインとピクセルの比率は、特定のインスタンスで、または全体として予測できません。私の知る限り、DOM_DELTA_LINEモードで提供されるデルタ測定を使用してFirefoxのスクロール動作をエミュレートする方法はありません。

DOM_DELTA_PIXELモードでは、動作はほぼピクセルパーフェクトです。つまり、スクロールされた実際のピクセルと報告されたピクセルデルタ値の比率はほぼ正確に1であり、これは 同じフィドル で示されています。

I Mozillaにバグを提出DOM_DELTA_LINEモードでのwheelイベントの動作は、予測できないため(つまり、方程式であるため)役に立たないと主張します。ここで、単位と大きさの両方が変数です)。 Firefox自体がこれらのデルタを尊重しないという事実にもかかわらず、予想される動作はwheelイベントがOSによって提供されるネイティブデルタを通過することであるため、この問題は無効とマークされています。

DOM_DELTA_LINEがどこかの仕様で定義されることを期待して、この質問は開いたままにしておきます。私の知る限り、font-sizeへの依存(line-heightではない)はまだどこにも記述されていません。

40
JKS

概要:

Chromium Issue Trackerからの次のコメントと私自身の観察に基づくと、DOM_DELTA_LINEの結果のスクロール値は仕様によって具体的に定義されていない可能性がありますが、Firefoxは現在deltaMode以外のホイールイベントを報告する唯一のブラウザーのようです。 DOM_DELTA_PIXEL0)として。

コメント Chromiumの問題から DOM3ホイールイベントを実装

最終的にIEが行うことを実行し、要素をスクロールする正確なピクセル数を報告しました。

次のアサートは常に真です(要素をスクロールできる場合)。

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

このようにして、常にスクロールする量を正確に知ることができます。

deltaModeDOM_DELTA_PIXEL以外に設定することはありません。

そのコメントによると、ChromiumはIEの唯一のピクセルデルタと一致します。カバーされていませんが、これはほぼ確実に最新のOperaおよびSafariに拡張されます。複数の入力デバイスを使用したWindow、Mac、およびLinuxでのテストからの私自身の観察は、これを否定しませんでした。

したがって、FirefoxはDOM_DELTA_LINE1)またはDOM_DELTA_PAGE2)のdeltaModeを報告する唯一のブラウザであるため、とりあえず、Firefoxがこれらの値をどのように計算するかを知る必要があるだけです。さて、ここで少し注意が必要ですが、Firefoxのソースを検索し、試行錯誤を繰り返した結果、デフォルトのフォントとデフォルトのフォントサイズに直接対応していることがわかりました。具体的には、ページ上のCSSを無視して、次の設定で構成されたもの。

Firefox fonts preferences options

つまり、スクロールで使用される行の高さを正しく検出するには、計算されたレンダリングの高さを検出するための完全にスタイル設定されていないインライン要素が必要です。ページ上の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では、考慮しなければならないことがもう1つあります。 Windowsでは、デフォルトのスクロール速度がオーバーライドされ、デフォルトで2倍速くなりますが、ルート要素に対してのみです。

システムのスクロール速度のシステムをオーバーライド:

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.horizo​​ntal.factorは水平スクロールイベント用です。値は1/100として使用されます(つまり、デフォルト値200は2.0を意味します)。

nsEventStateManagerは、ドキュメントのrootスクロール可能ビューをスクロールするために実行するときに、スクロール速度に比率を掛けます。したがって、DOMMouseScrollイベントのデルタ値がこれによって上書きされることはありません。

bug 513817 も参照してください。

これはデフォルトでドキュメントのルートにのみ適用され、textareaのようなドキュメント内のスクロール要素は影響を受けません(おそらくiframeを除く?)。デフォルトの2xが再構成されている場合、指定された値を取得する方法もありません。したがって、この値が重要であると考える場合は、おそらく技術的に非標準でありながら人気のあるnavigator.platformを使用して、ユーザーエージェントOSスニッフィングに頼る必要があります。

DOM_DELTA_PAGEはどうですか?:

Windowsには、行数でスクロールする代わりに、ほぼフルページでスクロールする垂直スクロールの代替設定もあります。明確にするために、これはWindows7でこれを制御する「一度に1つの画面」設定のダイアログです。

Windows 7 wheel settings

これがアクティブになっている場合、シングルクリックでほぼページ全体がスクロールされます。ここでは、スクロールパネルの高さから、スクロールバーを差し引いたものを意味します。 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モードもどのように機能するかについての概要がわかります。約。

22

私はFirefox25(Linux上)であなたのフィドルを試しました-そしてホイールイベントでのみscrollTopを追跡するためにそれを拡張しました。フィドルでは、スクロールデルタは、ホイールイベントよりもはるかに頻繁にトリガーされるスクロールイベントで計算されます。また、ソフトスクロールモードでのスクロールイベントは、ホイールイベントに比べて遅れているようです。 (つまり、ソフトスクロールがまだ終了していないときに測定すると、scrollTop()の「中間」値が得られます。)これらが、スクロールデルタとホイールイベントの間に良好な相関関係が得られない理由だと思います。 (注:wheelイベントは、関連するスクロールイベントの前に処理されるようです。)

ソフトスクロールがオフになっている場合、スクロールイベントごとに少なくとも1つのホイールイベントがあります。あなたのフィドルは、私にとって「ホイール」の間、完全に一定の「px/DOM_DELTA_LINE:18」を与えます。 2つのスパンを持つdivを追加した場合<span id="lineN">N</span><br/>間と測定($("#line2").position().top - $("#line1").position().top)それから私はちょうど18を得る。

概要:wheelイベントを使用すると、少なくとも私の環境では、スクロール距離をピクセル単位で計算できます。これがあなたにも当てはまることを願っています。

これが私の拡張機能を備えたあなたのフィドルのための私の拡張フィドルの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>
4
halfbit

font-size: initialを使用して、iframeを使用せずにスクロールラインの高さ(ブラウザのデフォルトのフォントサイズ)を取得する簡単な方法があります。

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());

html要素とbody要素のフォントサイズを上書きするページで、Chrome、Firefox、Safari(9と12をテスト)、Edge、IE11の最新バージョンでこれをテストしました。これは、IE11を除く他のすべてのテスト済みブラウザーで確実に機能するようです。 IE11ではCSSキーワード body をサポートしていないため、IE11ではinitialから継承されたフォントサイズが返されます。

したがって、IE11をサポートする必要がなくなった場合、これは機能するはずです。

2
Haprog