使用 javascript 检测鼠标滚轮的类型(平滑与缺口)
Detecting type of mouse scroll wheel (Smooth vs Notched) with javascript
我一直在一个 Web 项目中工作,该项目使用鼠标滚轮在视频上执行不同的操作。在某些时候,我必须为 deltaY
和 deltaY
应该滚动的帧数之间的关系建立一个系数。所以不同类型的鼠标 return 非常不同 deltaY
,特别是 平滑滚动。
在 fiddle 中,我在下方提供了此操作:
targetOffset = targetOffset + (e.deltaY/1000); // 16000 aprox for smooth scroll mice
而1000
是适用于Notched Scroll Wheel普通鼠标的系数。但是,如果我将该系数与 Smooth Scroll Touch "wheel" 一起使用,就像那些 mac 计算机(实际上没有滚轮)那样,该系数是 "just too much",例如 16 倍 "too much".
是否可以做些什么来检测这个或以某种方式校准系数?
var FF = !(window.mozInnerScreenX == null); // is firefox?
var vid = document.getElementById("v");
var canvas = document.getElementById("c");
var context = canvas.getContext('2d');
var targetFrame = document.getElementById('t');
var cw = 200;
var ch = Math.round(cw/1.7777);
canvas.width = cw;
canvas.height = ch;
var directionScroll = 0;
var targetOffset = 0;
var coefficient = 1000;
var modes = ['pixels', 'lines', 'page'];
vid.pause();
vid.addEventListener('seeked', function() {
context.drawImage(vid, 0, 0, cw, ch);
});
window.addEventListener('wheel', function(e) {
e.preventDefault();
// Normally scrolling this should be a substraction
// not a sum but "I like it like this!"
// Changed this with help of @Kaiido 's answer as partially solves the discrepancies between Firefox and Chrome
// alert(modes[e.deltaMode]);
if (modes[e.deltaMode]=='pixels') coefficient = 1000;
else if (modes[e.deltaMode]=='lines') coefficient = 30; // This should correspond to line-height??
else return false; // Disable page scrolling, modes[e.deltaMode]=='page'
targetOffset = targetOffset + (e.deltaY/coefficient); // e.deltaY is the thing!!
if (e.deltaY < 0) directionScroll = 1;
if (e.deltaY > 0) directionScroll = -1;
targetFrame.value = targetOffset;
return false;
});
var renderLoop = function(){
requestAnimationFrame( function(){
context.drawImage(vid,0,0,cw,ch);
if (vid.paused || vid.ended) {
targetOffset = targetOffset*0.9;
targetFrame.value=Math.round(targetOffset*100)/100;
var vct = vid.currentTime-targetOffset;
if (vct<0) {
vct = vid.duration + vct;
} else if (vct>vid.duration) {
vct = vct - vid.duration;
}
vid.currentTime = vct;
}
renderLoop();
});
};
renderLoop();
.column {
float: left;
width: 50%;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
#c {
border:1px solid black;
}
<h3>
scroll up is forward
</h3>
<div class="row">
<div class="column">
<div>
Video element:
</div>
<video controls height="120" id="v" tabindex="-1" autobuffer="auto" preload="auto">
<source type="video/webm" src="https://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.webm"></source>
</video>
</div>
<div class="column">
<div>
Canvas element:
</div>
<canvas id="c"></canvas>
<div>
Momentum: <input type=text id="t">
</div>
</div>
</div>
感谢任何帮助。
编辑 1:
我已经更新了代码,以便对系数应用一个简单的条件,但这并不能完全解决问题,因为 browser/plattform/mouse 可能存在许多变体。某种校准鼠标的方法可行吗?
编辑 2:
@Kaiido 的回答转向解决 Firefox 和 Chrome 的差异。 Firefox returns lines
作为 deltaMode
而 Chrome returns pixels
。我编辑了片段以考虑到这一点。
但是 'smooth scroll' 鼠标问题依然存在。更让我困惑的是,那只老鼠需要一个与lines
相反的系数,它需要一个更大而不是更小的系数。
为什么不直接检测增量是否大于某个阈值,如果是,则除以 16
。我会这样想:
if (Math.abs(e.deltaY) > 400) slowdown = 16;
else if (Math.abs(e.deltaY) < 15) slowdown = 1;
targetOffset = targetOffset + (e.deltaY/1000/slowdown);
会成功的。
var vid = document.getElementById("v");
var canvas = document.getElementById("c");
var context = canvas.getContext('2d');
var targetFrame = document.getElementById('t');
var cw = 200;
var ch = Math.round(cw/1.7777);
canvas.width = cw;
canvas.height = ch;
var directionScroll = 0;
var targetOffset = 0;
var maxDelta = 0;
// vid.pause();
vid.addEventListener('seeked', function() {
context.drawImage(vid, 0, 0, cw, ch);
});
var slowdown = 1;
window.addEventListener('wheel', function(e) {
e.preventDefault();
// Normally scrolling this should be a substraction
// not a sum but "I like it like this!"
if (Math.abs(e.deltaY) > 400) slowdown = 16;
else if (Math.abs(e.deltaY) < 15) slowdown = 1;
targetOffset = targetOffset + (e.deltaY/1000/slowdown); // e.deltaY is the thing!!
if (e.deltaY < 0) directionScroll = 1;
if (e.deltaY > 0) directionScroll = -1;
targetFrame.value = targetOffset;
return false;
});
var renderLoop = function(){
requestAnimationFrame( function(){
context.drawImage(vid,0,0,cw,ch);
if (vid.paused || vid.ended) {
// setTimeout(function(){
targetOffset = targetOffset*0.9;
targetFrame.value=Math.round(targetOffset*100)/100;
var vct = vid.currentTime-targetOffset;
if (vct<0) {
vct = vid.duration + vct;
} else if (vct>vid.duration) {
vct = vct - vid.duration;
}
vid.currentTime = vct;
// }, 0);
}
renderLoop();
});
};
renderLoop();
.column {
float: left;
width: 50%;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
#c {
border:1px solid black;
}
<h3>
scroll up is forward
</h3>
<div class="row">
<div class="column">
<div>
Video element:
</div>
<video controls height="120" id="v" tabindex="-1" autobuffer="auto" preload="auto">
<source type="video/webm" src="https://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.webm"></source>
</video>
</div>
<div class="column">
<div>
Canvas element:
</div>
<canvas id="c"></canvas>
<div>
Momentum: <input type=text id="t">
</div>
</div>
</div>
更新见文末!
我原来的回答:
我没有 mac 也没有 'smooth' 鼠标,但我已经在 [ 上测试了你的代码片段=103=] 和 Firefox 都在 Windows 和 Linux箱子。
在 Chrome 和 Windows 和 Linux 上效果很好] 但是...
看起来系数不适合 Firefox...它工作得更好(不如 Chrome) 与 200
.
还有一件事:
您是否在 windows 上测试过 mac 花式鼠标,反之亦然?会不会是mac相关的问题?
更新:
其他答案很好,但我对您的问题感到困惑,并通过代码和其他答案指出的内容学到了很多东西,但有些东西像错误一样一直留在我的脑海中。
搜索此主题时我找到了 this question very informative. It included a possible mouse scroll calibration script in this answer and a function getScrollLineHeight
for Detecting the line-height used by DOM_DELTA_LINE triggered scroll events。
为了完整起见,我在代码片段中复制了这个函数,但最后我认为不需要它。我已经注释掉了调用 getScrollLineHeight
的行,因为出于安全原因它在该站点中不起作用,但在 this fiddle.
中起作用
我的困惑是像往常一样以页面上的像素来考虑滚动。但是你的代码真的不关心这个。我的意思是,不关心鼠标滚轮 event.deltaY
的大小。仅当它是正面或负面时,才考虑在视频时间轴中向前或向后迈出一步。
所以这并没有解决 "touch sensitive scroll mice" 的问题,但它确实很容易解决 Firefox/Chrome 和任何 Pixel/Line/Page deltaMode
的问题。现在它在 Chrome 和 Firefox 中都能流畅运行。由于 WEBM 视频格式,我无法在其他浏览器上进行测试,而且我无法创建任何可用格式的视频(看看我的 P.D。最后)。
因此,每个调用都只是一个步骤:-1 或 1。虽然对于 deltaMode
,似乎只有 Firefox returns 而不是 "pixels"。我用这个 fiddle 来测试...现在你可以专注于那个平滑滚动的鼠标,看看 它发送每个呼叫的速度有多快 ,这在这个特殊情况下才是真正重要的(注意很多mac有平滑滚动软件或者反向滚动)。
我已经对你的每一行代码和我自己的修改进行了评论,但可能对其他人有用。
// detect if browser firefox as it appears to be the only
// browser that return deltaModes different than DOM_DELTA_PIXEL
// Ref:
var FF = !(window.mozInnerScreenX == null);
// Function grabbed from the reference above
// It tries to read current line-height of document (for 'lines' deltaMode)
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;
}
// html5 elements
var vid = document.getElementById("v"); // HTML5 video element
var canvas = document.getElementById("c"); // HTML5 canvas element
var context = canvas.getContext('2d'); // Canvas context
var momentum = document.getElementById('m'); // Current momentum display
var delta = document.getElementById('d'); // Current deltaMode display
var lineheight = document.getElementById('l'); // Current deltaMode display
// global variables
var ch = 120; // canvas with (could be window.innerHeight)
var cw = Math.round(ch * (16 / 9)); // 16/9 proportion width
var targetOffset = 0; // Video offset target position when scrolling
// deltaY to FPS coefficients (for fine tuning)
// Possible mouse scroll wheel 'event.deltaMode'
// modes are: 0:'pixels', 1:'lines', 2:'page'
var pc = 1000; // 'pixels' deltaY coefficient
var lh = "disabled"; //getScrollLineHeight(); // get line-height of deltaMode 'lines'
lineheight.value = lh; // display current document line height
coefficient = 30;
var deltaModes = ['pixels', 'lines', 'page']; // For deltaMode display
// Sets canvas dimensions
canvas.width = cw;
canvas.height = ch;
// Pauses video (this also starts to load the video)
vid.pause();
// Listens video changes time position
vid.addEventListener('seeked', function() {
// Updates canvas with current video frame
context.drawImage(vid, 0, 0, cw, ch);
});
// Listens mouse scroll wheel
window.addEventListener('wheel', function(e) {
// Don't do what scroll wheel normally does
e.preventDefault();
// You don't need an amount, just positive or negative value: -1, 1
var deltabs = 1;
if (e.deltaY<0) deltabs = -1;
// Disable page scrolling, modes[e.deltaMode]=='page'
if (e.deltaMode>1) return false;
delta.value = deltaModes[e.deltaMode];
// Normally scrolling this should be a subtraction
// not a sum but "I like it like this!"
// targetOffset = targetOffset + (e.deltaY / coefficient); // e.deltaY is the thing!!
targetOffset = targetOffset + (deltabs/coefficient);
// Shows current momentum
momentum.value = targetOffset;
return false;
});
// Updates canvas on a loop (both for play or pause state)
var renderLoop = function() {
requestAnimationFrame(function() {
// This parts updates canvas when video is paused
// Needs 'seeked' listener above
if (vid.paused || vid.ended) {
// Reduce target offset gradually
targetOffset = targetOffset * 0.9;
// Show current momentum
momentum.value = Math.round(targetOffset * 100) / 100;
// this part joins start and end of video when scrolling
// forward & backwards
var vct = vid.currentTime - targetOffset;
if (vct < 0) {
vct = vid.duration + vct;
} else if (vct > vid.duration) {
vct = vct - vid.duration;
}
vid.currentTime = vct;
// This parts updates canvas when video is playing
} else {
// update canvas with current video frame
context.drawImage(vid, 0, 0, cw, ch);
}
renderLoop(); // Recursive call to loop
});
};
renderLoop(); // Initial call to loop
input {
width: 50px;
}
.column {
float: left;
width: 50%;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
<h3>
mouse scroll video
</h3>
<div class="row">
<div class="column">
<div>
Video element:
</div>
<video controls height="120" id="v" tabindex="-1" autobuffer="auto" preload="auto">
<source type="video/webm" src="https://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.webm"/>
</video>
</div>
<div class="column">
<div>
Canvas element:
</div>
<canvas id="c"></canvas>
<div>
Momentum: <input type=text id="m">
</div>
<div>
deltaMode: <input type=text id="d">
</div>
<div>
lineHeight: <input type=text id="l">
</div>
</div>
</div>
P.D。我有一个问题(太具体了,无法在其他地方解释)...我用自己的视频进行了测试,结果很糟糕...这是为什么呢?与特定的视频编码设置有关?您知道将 FFMPEG 转换为 WEBM 格式(如示例中使用的视频)需要哪种编码命令吗?
我在 Firefox 里试过了,和 Chrome "performance" 没法比,所以你可以试试加 this.
var vid = document.getElementById("v");
var canvas = document.getElementById("c");
var context = canvas.getContext('2d');
var targetFrame = document.getElementById('t');
var cw = 200;
var ch = Math.round(cw/1.7777);
canvas.width = cw;
canvas.height = ch;
var directionScroll = 0;
var targetOffset = 0;
// vid.pause();
vid.addEventListener('seeked', function() {
context.drawImage(vid, 0, 0, cw, ch);
});
function normalizeDelta(wheelEvent) {
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;
}
window.addEventListener('wheel', function(e) {
e.preventDefault();
// Normally scrolling this should be a substraction
// not a sum but "I like it like this!"
targetOffset = targetOffset + normalizeDelta(e); // e.deltaY is the thing!!
if (e.deltaY < 0) directionScroll = 1;
if (e.deltaY > 0) directionScroll = -1;
targetFrame.value = targetOffset;
return false;
});
var renderLoop = function(){
requestAnimationFrame( function(){
context.drawImage(vid,0,0,cw,ch);
if (vid.paused || vid.ended) {
// setTimeout(function(){
targetOffset = targetOffset*0.9;
targetFrame.value=Math.round(targetOffset*100)/100;
var vct = vid.currentTime-targetOffset;
if (vct<0) {
vct = vid.duration + vct;
} else if (vct>vid.duration) {
vct = vct - vid.duration;
}
vid.currentTime = vct;
// }, 0);
}
renderLoop();
});
};
renderLoop();
.column {
float: left;
width: 50%;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
#c {
border:1px solid black;
}
<h3>
scroll up is forward
</h3>
<div class="row">
<div class="column">
<div>
Video element:
</div>
<video controls height="120" id="v" tabindex="-1" autobuffer="auto" preload="auto">
<source type="video/webm" src="https://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.webm"></source>
</video>
</div>
<div class="column">
<div>
Canvas element:
</div>
<canvas id="c"></canvas>
<div>
Momentum: <input type=text id="t">
</div>
</div>
</div>
参考:
如果问题被删除,答案是这样的:
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;
}
这是一个疯狂的猜测,因为我没有这样的 Notched 鼠标 来测试,但是这个 16 倍的因子听起来确实像你的增量值没有设置在相同模式。
事实上,车轮事件有 3 种可能的模式:
0
=> 像素(可能是平滑滚动/苹果鼠标)
1
=> 线条(可能是 Notched Mice)
2
=> 页面(键盘?)
因此,您可能需要检查车轮事件的 deltaMode
属性 才能做出相应的反应。
onwheel = e => {
var modes = ['pixels', 'lines', 'page'];
console.log('scrolled by %s %s', e.deltaY, modes[e.deltaMode]);
}
<h1> scroll </h1>
我一直在一个 Web 项目中工作,该项目使用鼠标滚轮在视频上执行不同的操作。在某些时候,我必须为 deltaY
和 deltaY
应该滚动的帧数之间的关系建立一个系数。所以不同类型的鼠标 return 非常不同 deltaY
,特别是 平滑滚动。
在 fiddle 中,我在下方提供了此操作:
targetOffset = targetOffset + (e.deltaY/1000); // 16000 aprox for smooth scroll mice
而1000
是适用于Notched Scroll Wheel普通鼠标的系数。但是,如果我将该系数与 Smooth Scroll Touch "wheel" 一起使用,就像那些 mac 计算机(实际上没有滚轮)那样,该系数是 "just too much",例如 16 倍 "too much".
是否可以做些什么来检测这个或以某种方式校准系数?
var FF = !(window.mozInnerScreenX == null); // is firefox?
var vid = document.getElementById("v");
var canvas = document.getElementById("c");
var context = canvas.getContext('2d');
var targetFrame = document.getElementById('t');
var cw = 200;
var ch = Math.round(cw/1.7777);
canvas.width = cw;
canvas.height = ch;
var directionScroll = 0;
var targetOffset = 0;
var coefficient = 1000;
var modes = ['pixels', 'lines', 'page'];
vid.pause();
vid.addEventListener('seeked', function() {
context.drawImage(vid, 0, 0, cw, ch);
});
window.addEventListener('wheel', function(e) {
e.preventDefault();
// Normally scrolling this should be a substraction
// not a sum but "I like it like this!"
// Changed this with help of @Kaiido 's answer as partially solves the discrepancies between Firefox and Chrome
// alert(modes[e.deltaMode]);
if (modes[e.deltaMode]=='pixels') coefficient = 1000;
else if (modes[e.deltaMode]=='lines') coefficient = 30; // This should correspond to line-height??
else return false; // Disable page scrolling, modes[e.deltaMode]=='page'
targetOffset = targetOffset + (e.deltaY/coefficient); // e.deltaY is the thing!!
if (e.deltaY < 0) directionScroll = 1;
if (e.deltaY > 0) directionScroll = -1;
targetFrame.value = targetOffset;
return false;
});
var renderLoop = function(){
requestAnimationFrame( function(){
context.drawImage(vid,0,0,cw,ch);
if (vid.paused || vid.ended) {
targetOffset = targetOffset*0.9;
targetFrame.value=Math.round(targetOffset*100)/100;
var vct = vid.currentTime-targetOffset;
if (vct<0) {
vct = vid.duration + vct;
} else if (vct>vid.duration) {
vct = vct - vid.duration;
}
vid.currentTime = vct;
}
renderLoop();
});
};
renderLoop();
.column {
float: left;
width: 50%;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
#c {
border:1px solid black;
}
<h3>
scroll up is forward
</h3>
<div class="row">
<div class="column">
<div>
Video element:
</div>
<video controls height="120" id="v" tabindex="-1" autobuffer="auto" preload="auto">
<source type="video/webm" src="https://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.webm"></source>
</video>
</div>
<div class="column">
<div>
Canvas element:
</div>
<canvas id="c"></canvas>
<div>
Momentum: <input type=text id="t">
</div>
</div>
</div>
感谢任何帮助。
编辑 1:
我已经更新了代码,以便对系数应用一个简单的条件,但这并不能完全解决问题,因为 browser/plattform/mouse 可能存在许多变体。某种校准鼠标的方法可行吗?
编辑 2:
@Kaiido 的回答转向解决 Firefox 和 Chrome 的差异。 Firefox returns lines
作为 deltaMode
而 Chrome returns pixels
。我编辑了片段以考虑到这一点。
但是 'smooth scroll' 鼠标问题依然存在。更让我困惑的是,那只老鼠需要一个与lines
相反的系数,它需要一个更大而不是更小的系数。
为什么不直接检测增量是否大于某个阈值,如果是,则除以 16
。我会这样想:
if (Math.abs(e.deltaY) > 400) slowdown = 16;
else if (Math.abs(e.deltaY) < 15) slowdown = 1;
targetOffset = targetOffset + (e.deltaY/1000/slowdown);
会成功的。
var vid = document.getElementById("v");
var canvas = document.getElementById("c");
var context = canvas.getContext('2d');
var targetFrame = document.getElementById('t');
var cw = 200;
var ch = Math.round(cw/1.7777);
canvas.width = cw;
canvas.height = ch;
var directionScroll = 0;
var targetOffset = 0;
var maxDelta = 0;
// vid.pause();
vid.addEventListener('seeked', function() {
context.drawImage(vid, 0, 0, cw, ch);
});
var slowdown = 1;
window.addEventListener('wheel', function(e) {
e.preventDefault();
// Normally scrolling this should be a substraction
// not a sum but "I like it like this!"
if (Math.abs(e.deltaY) > 400) slowdown = 16;
else if (Math.abs(e.deltaY) < 15) slowdown = 1;
targetOffset = targetOffset + (e.deltaY/1000/slowdown); // e.deltaY is the thing!!
if (e.deltaY < 0) directionScroll = 1;
if (e.deltaY > 0) directionScroll = -1;
targetFrame.value = targetOffset;
return false;
});
var renderLoop = function(){
requestAnimationFrame( function(){
context.drawImage(vid,0,0,cw,ch);
if (vid.paused || vid.ended) {
// setTimeout(function(){
targetOffset = targetOffset*0.9;
targetFrame.value=Math.round(targetOffset*100)/100;
var vct = vid.currentTime-targetOffset;
if (vct<0) {
vct = vid.duration + vct;
} else if (vct>vid.duration) {
vct = vct - vid.duration;
}
vid.currentTime = vct;
// }, 0);
}
renderLoop();
});
};
renderLoop();
.column {
float: left;
width: 50%;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
#c {
border:1px solid black;
}
<h3>
scroll up is forward
</h3>
<div class="row">
<div class="column">
<div>
Video element:
</div>
<video controls height="120" id="v" tabindex="-1" autobuffer="auto" preload="auto">
<source type="video/webm" src="https://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.webm"></source>
</video>
</div>
<div class="column">
<div>
Canvas element:
</div>
<canvas id="c"></canvas>
<div>
Momentum: <input type=text id="t">
</div>
</div>
</div>
更新见文末!
我原来的回答:
我没有 mac 也没有 'smooth' 鼠标,但我已经在 [ 上测试了你的代码片段=103=] 和 Firefox 都在 Windows 和 Linux箱子。
在 Chrome 和 Windows 和 Linux 上效果很好] 但是...
看起来系数不适合 Firefox...它工作得更好(不如 Chrome) 与 200
.
还有一件事:
您是否在 windows 上测试过 mac 花式鼠标,反之亦然?会不会是mac相关的问题?
更新:
其他答案很好,但我对您的问题感到困惑,并通过代码和其他答案指出的内容学到了很多东西,但有些东西像错误一样一直留在我的脑海中。
搜索此主题时我找到了 this question very informative. It included a possible mouse scroll calibration script in this answer and a function getScrollLineHeight
for Detecting the line-height used by DOM_DELTA_LINE triggered scroll events。
为了完整起见,我在代码片段中复制了这个函数,但最后我认为不需要它。我已经注释掉了调用 getScrollLineHeight
的行,因为出于安全原因它在该站点中不起作用,但在 this fiddle.
我的困惑是像往常一样以页面上的像素来考虑滚动。但是你的代码真的不关心这个。我的意思是,不关心鼠标滚轮 event.deltaY
的大小。仅当它是正面或负面时,才考虑在视频时间轴中向前或向后迈出一步。
所以这并没有解决 "touch sensitive scroll mice" 的问题,但它确实很容易解决 Firefox/Chrome 和任何 Pixel/Line/Page deltaMode
的问题。现在它在 Chrome 和 Firefox 中都能流畅运行。由于 WEBM 视频格式,我无法在其他浏览器上进行测试,而且我无法创建任何可用格式的视频(看看我的 P.D。最后)。
因此,每个调用都只是一个步骤:-1 或 1。虽然对于 deltaMode
,似乎只有 Firefox returns 而不是 "pixels"。我用这个 fiddle 来测试...现在你可以专注于那个平滑滚动的鼠标,看看 它发送每个呼叫的速度有多快 ,这在这个特殊情况下才是真正重要的(注意很多mac有平滑滚动软件或者反向滚动)。
我已经对你的每一行代码和我自己的修改进行了评论,但可能对其他人有用。
// detect if browser firefox as it appears to be the only
// browser that return deltaModes different than DOM_DELTA_PIXEL
// Ref:
var FF = !(window.mozInnerScreenX == null);
// Function grabbed from the reference above
// It tries to read current line-height of document (for 'lines' deltaMode)
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;
}
// html5 elements
var vid = document.getElementById("v"); // HTML5 video element
var canvas = document.getElementById("c"); // HTML5 canvas element
var context = canvas.getContext('2d'); // Canvas context
var momentum = document.getElementById('m'); // Current momentum display
var delta = document.getElementById('d'); // Current deltaMode display
var lineheight = document.getElementById('l'); // Current deltaMode display
// global variables
var ch = 120; // canvas with (could be window.innerHeight)
var cw = Math.round(ch * (16 / 9)); // 16/9 proportion width
var targetOffset = 0; // Video offset target position when scrolling
// deltaY to FPS coefficients (for fine tuning)
// Possible mouse scroll wheel 'event.deltaMode'
// modes are: 0:'pixels', 1:'lines', 2:'page'
var pc = 1000; // 'pixels' deltaY coefficient
var lh = "disabled"; //getScrollLineHeight(); // get line-height of deltaMode 'lines'
lineheight.value = lh; // display current document line height
coefficient = 30;
var deltaModes = ['pixels', 'lines', 'page']; // For deltaMode display
// Sets canvas dimensions
canvas.width = cw;
canvas.height = ch;
// Pauses video (this also starts to load the video)
vid.pause();
// Listens video changes time position
vid.addEventListener('seeked', function() {
// Updates canvas with current video frame
context.drawImage(vid, 0, 0, cw, ch);
});
// Listens mouse scroll wheel
window.addEventListener('wheel', function(e) {
// Don't do what scroll wheel normally does
e.preventDefault();
// You don't need an amount, just positive or negative value: -1, 1
var deltabs = 1;
if (e.deltaY<0) deltabs = -1;
// Disable page scrolling, modes[e.deltaMode]=='page'
if (e.deltaMode>1) return false;
delta.value = deltaModes[e.deltaMode];
// Normally scrolling this should be a subtraction
// not a sum but "I like it like this!"
// targetOffset = targetOffset + (e.deltaY / coefficient); // e.deltaY is the thing!!
targetOffset = targetOffset + (deltabs/coefficient);
// Shows current momentum
momentum.value = targetOffset;
return false;
});
// Updates canvas on a loop (both for play or pause state)
var renderLoop = function() {
requestAnimationFrame(function() {
// This parts updates canvas when video is paused
// Needs 'seeked' listener above
if (vid.paused || vid.ended) {
// Reduce target offset gradually
targetOffset = targetOffset * 0.9;
// Show current momentum
momentum.value = Math.round(targetOffset * 100) / 100;
// this part joins start and end of video when scrolling
// forward & backwards
var vct = vid.currentTime - targetOffset;
if (vct < 0) {
vct = vid.duration + vct;
} else if (vct > vid.duration) {
vct = vct - vid.duration;
}
vid.currentTime = vct;
// This parts updates canvas when video is playing
} else {
// update canvas with current video frame
context.drawImage(vid, 0, 0, cw, ch);
}
renderLoop(); // Recursive call to loop
});
};
renderLoop(); // Initial call to loop
input {
width: 50px;
}
.column {
float: left;
width: 50%;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
<h3>
mouse scroll video
</h3>
<div class="row">
<div class="column">
<div>
Video element:
</div>
<video controls height="120" id="v" tabindex="-1" autobuffer="auto" preload="auto">
<source type="video/webm" src="https://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.webm"/>
</video>
</div>
<div class="column">
<div>
Canvas element:
</div>
<canvas id="c"></canvas>
<div>
Momentum: <input type=text id="m">
</div>
<div>
deltaMode: <input type=text id="d">
</div>
<div>
lineHeight: <input type=text id="l">
</div>
</div>
</div>
P.D。我有一个问题(太具体了,无法在其他地方解释)...我用自己的视频进行了测试,结果很糟糕...这是为什么呢?与特定的视频编码设置有关?您知道将 FFMPEG 转换为 WEBM 格式(如示例中使用的视频)需要哪种编码命令吗?
我在 Firefox 里试过了,和 Chrome "performance" 没法比,所以你可以试试加 this.
var vid = document.getElementById("v");
var canvas = document.getElementById("c");
var context = canvas.getContext('2d');
var targetFrame = document.getElementById('t');
var cw = 200;
var ch = Math.round(cw/1.7777);
canvas.width = cw;
canvas.height = ch;
var directionScroll = 0;
var targetOffset = 0;
// vid.pause();
vid.addEventListener('seeked', function() {
context.drawImage(vid, 0, 0, cw, ch);
});
function normalizeDelta(wheelEvent) {
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;
}
window.addEventListener('wheel', function(e) {
e.preventDefault();
// Normally scrolling this should be a substraction
// not a sum but "I like it like this!"
targetOffset = targetOffset + normalizeDelta(e); // e.deltaY is the thing!!
if (e.deltaY < 0) directionScroll = 1;
if (e.deltaY > 0) directionScroll = -1;
targetFrame.value = targetOffset;
return false;
});
var renderLoop = function(){
requestAnimationFrame( function(){
context.drawImage(vid,0,0,cw,ch);
if (vid.paused || vid.ended) {
// setTimeout(function(){
targetOffset = targetOffset*0.9;
targetFrame.value=Math.round(targetOffset*100)/100;
var vct = vid.currentTime-targetOffset;
if (vct<0) {
vct = vid.duration + vct;
} else if (vct>vid.duration) {
vct = vct - vid.duration;
}
vid.currentTime = vct;
// }, 0);
}
renderLoop();
});
};
renderLoop();
.column {
float: left;
width: 50%;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
#c {
border:1px solid black;
}
<h3>
scroll up is forward
</h3>
<div class="row">
<div class="column">
<div>
Video element:
</div>
<video controls height="120" id="v" tabindex="-1" autobuffer="auto" preload="auto">
<source type="video/webm" src="https://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.webm"></source>
</video>
</div>
<div class="column">
<div>
Canvas element:
</div>
<canvas id="c"></canvas>
<div>
Momentum: <input type=text id="t">
</div>
</div>
</div>
参考:
如果问题被删除,答案是这样的:
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;
}
这是一个疯狂的猜测,因为我没有这样的 Notched 鼠标 来测试,但是这个 16 倍的因子听起来确实像你的增量值没有设置在相同模式。
事实上,车轮事件有 3 种可能的模式:
0
=> 像素(可能是平滑滚动/苹果鼠标)1
=> 线条(可能是 Notched Mice)2
=> 页面(键盘?)
因此,您可能需要检查车轮事件的 deltaMode
属性 才能做出相应的反应。
onwheel = e => {
var modes = ['pixels', 'lines', 'page'];
console.log('scrolled by %s %s', e.deltaY, modes[e.deltaMode]);
}
<h1> scroll </h1>