概述:
虽然由DOM_DELTA_LINE
产生的滚动值可能没有被任何规范明确定义,但基于来自Chromium问题跟踪器的以下评论和我的观察,似乎Firefox是目前唯一报告带有除deltaMode
以外的其他内容的滚轮事件的浏览器,这些内容为DOM_DELTA_PIXEL
(0
)。
来自Chromium问题实现DOM3滚轮事件的评论:
We ended up doing what IE does and report the exact number of pixels we would scroll the element.
The following asserts are always true (given that the element can be
scrolled).
element.scrollTop = 0;
element.addEventListener('wheel', function(e) {
assert(e.deltaMode === MouseEvent. DOM_DELTA_PIXEL);
assert(element.scrollTop === e.deltaY);
});
This way you know exactly how much to scroll at all times.
We never set the deltaMode
to anything else but DOM_DELTA_PIXEL
.
根据该评论,Chromium 与 IE 的像素差异相匹配。虽然没有涵盖到其他浏览器,但这几乎肯定直接适用于现代版的 Opera 和 Safari。在我使用多种输入设备测试 Windows、Mac 和 Linux 平台时,我的观察并未证明这一点有误。
因此,由于 Firefox 是唯一会报告
deltaMode
为
DOM_DELTA_LINE
(
1
) 或
DOM_DELTA_PAGE
(
2
) 的浏览器(目前),我们只需要知道 Firefox 如何计算这些值。尽管这有点棘手,但通过搜索 Firefox 源代码和一些试错,我已经确定它直接对应默认字体和默认字体大小,具体取决于以下首选项的配置,忽略页面上的任何 CSS。
![Firefox fonts preferences options](https://istack.dev59.com/KhWxn.webp)
这意味着要正确检测滚动中使用的行高,您必须拥有完全未样式化的内联元素以检测计算渲染高度。由于页面上的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());
现在我们知道行高差应该转换成多少像素了。然而,在Windows上,还有一件事情需要考虑。在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.horizontal.factor 用于水平滚动事件。这些值被用作1/100(即默认值200表示2.0)。
当nsEventStateManager执行文档的根可滚动视图的滚动时,它将滚动速度乘以比例。因此,DOMMouseScroll事件的delta值从未被此覆盖。
另请参见
bug 513817。
这仅适用于文档的根默认滚动元素,例如
textarea
内部的滚动元素不受影响(除非是iframes?)。如果默认的2倍被重新配置,则也没有办法获取指定的值,因此,如果您认为这个值很重要,您将不得不采取用户代理操作系统嗅探的方法,可能使用技术上非标准但流行的
navigator.platform
。
DOM_DELTA_PAGE
呢?
Windows还有一个垂直滚动的备选设置,其中它不是按行数滚动,而是滚动一个
几乎完整的页面。澄清一下,在Windows 7中,“每次一个屏幕”设置控制着这个对话框。
![Windows 7 wheel settings](https://istack.dev59.com/3AtUg.webp)
当此功能被激活时,单击一次将滚动几乎整个页面,这里指滚动面板的高度减去滚动条。我说几乎,因为Firefox会考虑其他一些因素来减少滚动量。即通过减少一些行、百分比,并以某种方式减去固定位置的页眉和页脚。请参见以下代码块中的一些逻辑。
摘自
layout/generic/nsGfxScrollFrame.cpp
:
nsSize
ScrollFrameHelper::GetPageScrollAmount() const
{
nsSize lineScrollAmount = GetLineScrollAmount();
nsSize effectiveScrollPortSize;
if (mIsRoot) {
nsIFrame* root = mOuter->PresContext()->PresShell()->GetRootFrame();
effectiveScrollPortSize =
GetScrollPortSizeExcludingHeadersAndFooters(root, mScrollPort);
} else {
effectiveScrollPortSize = mScrollPort.Size();
}
return nsSize(
effectiveScrollPortSize.width -
std::min(effectiveScrollPortSize.width/10, 2*lineScrollAmount.width),
effectiveScrollPortSize.height -
std::min(effectiveScrollPortSize.height/10, 2*lineScrollAmount.height));
}
我不完全确定哪些被检测为固定位置的页眉和页脚元素,这应该可以很好地概述
DOM_DELTA_PAGE
模式的工作方式,除了这个问题所询问的
DOM_DELTA_LINE
。请注意保留HTML标签。