在该代码中,我使用鼠标滚轮来放大/缩小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 | 细节 ------------------ + ---------------- + ------------ 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(可能是错误的) Chrome v37 / OS X | 120 | 0 IE9 / Win7 | 120 | 未定义 Opera v11 / OS X | 40 | -1 Opera v24 / OS X | 120 | 0 Opera v11 / Win7 | 120 | -3 Firefox v4 / Win7 | 未定义 -3 Firefox v4 / OS X | 未定义 -1 Firefox v30 / OS X | 未定义 -1
此外,即使在缓慢移动时,在OS X上使用MacBook触控板也会产生不同的结果:
- 在Safari和Chrome上,
wheelDelta
鼠标滚轮的值为3而不是120。 - 在Firefox上,这
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/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)的情况,但是这种情况很少发生。由于此处描述的原因,现在该代码是此答案顶部显示的推荐技术。
这是我疯狂的尝试,以产生一个跨浏览器一致且标准化的增量(-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
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 | | | | | | |
另一个或多或少的独立解决方案...
不过,这并不需要考虑事件之间的时间。某些浏览器似乎总是以相同的增量触发事件,而在快速滚动时只会更快地触发事件。其他人确实会改变三角洲。可以想象一种自适应归一化器,它会考虑时间,但是会涉及到一些麻烦且难以使用。
可在此处工作: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
事件可能很有用(尽管由于Firefox现在不推荐使用DOMMouseWheel事件,所以现在不推荐使用此事件)。
对于Windows,驱动程序本身似乎会生成WM_MOUSEWHEEL,WM_MOUSEHWHEEL事件(以及可能是用于触摸板平移的WM_GESTURE事件?)。这就可以解释为什么Windows或浏览器似乎没有对鼠标滚轮事件值本身进行规范化(并且可能意味着您无法编写可靠的代码来规范化这些值)。
对于IE9和IE10的Internet Explorer中的onwheel
(不是onmousewheel)事件支持,您还可以使用W3C标准 onwheel
事件。但是一个凹口的值可以不同于120(例如,使用此测试页,单个凹口在我的鼠标上变为111(而不是-120))。我写了另一篇文章,其中包含可能相关的其他详细信息。
基本上,在我自己的轮事件测试中(我试图对滚动值进行标准化),我发现我获得了操作系统,浏览器供应商,浏览器版本,事件类型和设备(Microsoft斜轮鼠标,笔记本电脑触摸板手势)的不同值,带有滚动区的笔记本电脑触摸板,Apple魔术鼠标,Apple强大的鼠标滚动球,Mac触摸板等)。
并且必须忽略浏览器配置(例如Firefox mousewheel.enable_pixel_scrolling,chrome --scroll-pixels = 150),驱动程序设置(例如Synaptics触摸板)和操作系统配置(Windows鼠标设置,OSX鼠标首选项, X.org按钮设置)。
今天,这个问题我已经解决了几个小时,而不是第一次:(
我一直在尝试通过“滑动”来汇总值,看看不同的浏览器如何报告值,并且它们之间的差异很大,Safari几乎在所有平台上报告的数量级都更大,Chrome的报告数量更多(例如3倍多)。 )与firefox相比,firefox从长远来看是平衡的,但是在小动作上的平台之间却大不相同(在Ubuntu gnome上,几乎只有+3或-3,看起来像是汇总了较小的事件,然后发送一个大的“ +3”)
现在找到的当前解决方案是三种:
- 已经提到的“仅使用符号”会扼杀任何形式的加速
- 嗅探浏览器直至次要版本和平台,并进行适当调整
- Qooxdoo最近实现了一种自适应算法,该算法基本上试图根据到目前为止收到的最小值和最大值来缩放增量。
Qooxdoo中的想法很好,并且可行,并且是我目前发现的唯一完全一致的跨浏览器解决方案。
不幸的是,它还会使加速度重新归一化。如果尝试一下(在他们的演示中),并以最大速度上下滚动一会儿,您会注意到,极快或极慢的滚动基本上会产生几乎相同的移动量。相反,如果您重新加载页面并且仅非常缓慢地滑动,则会注意到它会快速滚动。”
对于习惯在触摸板上进行剧烈滚动滑动并期望到达滚动对象顶部或底部的Mac用户(如我)而言,这令人沮丧。
更重要的是,由于它会根据获得的最大值降低鼠标速度,因此您的用户尝试加快速度的次数越多,速度下降的速度就越大,而“慢速滚动”用户的速度会非常快。
这使此解决方案(否则才是出色的解决方案)成为解决方案1的更好实现。
我将解决方案移植到了jquery mousewheel插件上:http : //jsfiddle.net/SimoneGianni/pXzVv/
如果您玩了一会儿,您会发现您将开始获得相当均匀的结果,但是您还会注意到,它趋向于非常快地获得+ 1 / -1值。
我现在正在努力对其进行增强,以更好地检测峰,以使它们不会“超出范围”发送所有信息。同时获得0到1之间的浮点值作为增量值也很不错,这样就可以获得连贯的输出。
绝对没有简单的方法可以在所有浏览器中的所有操作系统中的所有用户之间进行标准化。
它变得比您列出的版本更糟-在我的WindowsXP + Firefox3.6设置中,鼠标滚轮每一次滚动滚动6次-可能是因为我忘记了某个地方,无论是在操作系统中还是在某个地方,我都加速了鼠标滚轮:配置
但是我正在解决一个类似的问题(使用类似的应用程序顺便说一句,但是没有画布),并且我只是通过使用+1 / -1的增量符号并随着时间的推移测量它上一次触发的时间,有加速度,即 如果有人滚动一次VS在几分钟几次(我敢打赌是谷歌如何映射做的话)。
这个概念在我的测试中似乎运作良好,只要使加速少于100毫秒即可。
var onMouseWheel = function(e) {
e = e.originalEvent;
var delta = e.wheelDelta>0||e.detail<0?1:-1;
alert(delta);
}
$("body").bind("mousewheel DOMMouseScroll", onMouseWheel);
文章标签:javascript , mousewheel
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!
评论已关闭!