首页 > 解决方案 > 用javascript检测鼠标滚轮的类型(平滑与缺口)

问题描述

我一直在从事一个网络项目,该项目使用鼠标滚轮对视频进行不同的操作。在某些时候,我必须为应该滚动deltaY的帧数和帧数之间的关系建立一个系数。deltaY所以不同类型的鼠标返回非常不同deltaY,特别是平滑滚动的。

在小提琴中,我在下面提供了以下内容:

targetOffset = targetOffset + (e.deltaY/1000); // 16000 aprox for smooth scroll mice

并且1000是适用于 Notched Scroll Wheel 普通鼠标的系数。但是,如果我将该系数与 Smooth Scroll Touch“滚轮”一起使用,就像那些 mac 计算机(实际上没有滚轮)一样,该系数“太多了”,就像 16 倍“太多”。

是否可以采取一些措施来检测这一点或以某种方式校准系数?

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:

我已经更新了代码,以便对系数应用一个简单的条件,但这并不能完全解决问题,因为由于浏览器/平台/鼠标的原因,许多变体都是可能的。某种校准鼠标的方法可以工作吗?

编辑2:

@Kaiido 的答案转向解决 Firefox 和 Chrome 的差异。Firefox 返回linesdeltaMode而 Chrome 返回pixels。我编辑了片段以考虑这一点。

但问题仍然存在于“平滑滚动”鼠标上。更让我困惑的是,鼠标需要一个与 的系数相反的系数lines,它需要一个更大而不是更小的系数。

标签: javascriptmacosscrollmousewheel

解决方案


见最后更新!


我原来的答案:

我没有mac也没有“平滑”鼠标,但我已经在 Windows 和 Linux 机器上的Chrome 和 Firefox 上测试代码片段

在WindowsLinux上的Chrome上运行良好,但是......

看起来这个系数不是Firefox正确系数......它在200.

还有一件事:

您是否在 Windows 上测试过mac花式鼠标,反之亦然?会不会是mac相关的问题?

更新:

其他答案很好,但我对你的问题感到困惑,并从代码和其他答案指出的内容中学到了很多东西,但有些东西像错误一样留在我的脑海中。

搜索这个主题,我发现这个问题非常有用。它在此答案中包含一个可能的鼠标滚动校准脚本和一个getScrollLineHeight用于检测 DOM_DELTA_LINE 触发的滚动事件使用的行高的函数。

为了完整起见,我在片段中复制了这个函数,但最后我认为不需要它。我已经注释掉了调用的行,getScrollLineHeight因为出于安全原因它在这个站点中不起作用,但在这个 fiddle中起作用。

我的困惑是像往常一样考虑滚动,就页面上的像素而言。但是你的代码真的不在乎这个。我的意思是,不关心鼠标滚轮event.deltaY的大小。只有当它是积极的或消极的,并考虑在视频时间线上向前或向后迈出一步。

所以这并不能解决“触敏滚动鼠标”的问题,但它确实可以轻松解决 Firefox/Chrome 和任何 Pixel/Line/Page 的问题deltaMode。现在它可以在 Chrome 和 Firefox 中顺利运行。由于 WEBM 视频格式,我无法在其他浏览器上进行测试,而且我无法创建任何有效格式的视频(最后看看我的 PD)。

因此,每次调用都只是一个步骤:-1 或 1。尽管似乎只有 Firefox 返回的不是deltaMode. 我用这个小提琴来测试......现在你可以专注于那个平滑滚动的鼠标,看看它发送每个调用的速度有多快,这在这种特殊情况下才是真正重要的(请注意,许多 Mac 都有平滑滚动软件或反向滚动)。

我已经评论了您代码的每一行以及我为自己所做的修改,但可能对其他人有用。

// detect if browser firefox as it appears to be the only
//  browser that return deltaModes different than DOM_DELTA_PIXEL
//  Ref: https://stackoverflow.com/a/37474225/4146962
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>

PD 我有一个问题(太具体,无法在其他地方解释)...我用自己的视频进行了测试,结果非常糟糕...这是为什么呢?与特定的视频编码设置有关吗?您是否知道将 FFMPEG 转换为 WEBM 格式(如您的示例中使用的视频)需要哪种编码 cmd?


推荐阅读