Обнаружение типа колесика прокрутки мыши (Smooth vs Notched) с помощью JavaScript - PullRequest
0 голосов
/ 26 апреля 2018

Я работал в веб-проекте, который использует колесо прокрутки мыши для различных действий над видео.В какой-то момент я должен установить коэффициент для отношения между deltaY и количеством кадров, которые deltaY должен перевернуть.Таким образом, разные типы мышей возвращают очень разные deltaY, особенно гладкие прокрутки единицы.

В скрипте, которую я предоставляю ниже, это сделано в:

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

И1000 - это коэффициент, который хорошо работает для обычной мыши с зубчатым колесом прокрутки.Но если я использую этот коэффициент с «колесом» с плавной прокруткой, как у компьютеров 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:

@ Ответ Кайидо повернулся, чтобы устранить различия в Firefox и Chrome.Firefox возвращает lines как deltaMode, а Chrome возвращает pixels.Я отредактировал фрагмент, чтобы учесть это.

Но проблема все еще остается с мышью «плавной прокрутки».Чтобы озадачить меня еще больше, этой мыши нужен коэффициент, противоположный коэффициенту lines, ей нужен коэффициент больше, а не меньше.

Ответы [ 4 ]

0 голосов
/ 26 апреля 2018

Это дикое предположение, потому что у меня нет такой мыши с надрезом для тестирования, но этот 16-кратный коэффициент действительно звучит так, как будто значения дельты не установлены в одном и том же режиме.

Действительно, событие wheel имеет 3 возможных режима:

  • 0 => пикселей (вероятно, плавная прокрутка / мыши Apple)
  • 1 => линии (возможно зубчатых мышей )
  • 2 => страниц (клавиатура?)

Так что вам, возможно, придется проверить событие своего колеса deltaMode свойство реагировать соответственно.

onwheel = e => {
  var modes = ['pixels', 'lines', 'page'];
  console.log('scrolled by %s %s', e.deltaY, modes[e.deltaMode]);
}
<h1> scroll </h1>
0 голосов
/ 26 апреля 2018

См. ОБНОВЛЕНИЕ в конце!


Мой оригинальный ответ:

У меня нет mac и не «гладкая» мышь, но я проверил ваш фрагмент как на Chrome и Firefox на Windows и Linux коробки.

Отлично работает на Chrome как на Windows и Linux , но ...

выглядит как коэффициентне подходит для Firefox ... он работает лучше (не так хорошо, как в Chrome ) с 200.

Еще одна вещь:

Тестировали ли вы модную мышь mac на Windows и наоборот?Может ли это быть mac проблема?

ОБНОВЛЕНИЕ:

Другие ответы - это здорово, но я был озадачен вашим вопросом и многому научился скод и то, на что указывали другие ответы, но что-то запомнилось мне как ошибка.

В поисках этой темы я нашел этот вопрос очень информативным.В этот ответ включен возможный скрипт калибровки прокрутки мыши и функция getScrollLineHeight для Определение высоты строки, используемой событиями прокрутки DOM_DELTA_LINE .

Я скопировал эту функцию в фрагмент для полноты, но в конце она не нужна для того, что я думал.Я закомментировал строку, которая вызывает getScrollLineHeight, потому что она не работает на этом сайте по соображениям безопасности, но работает в этой скрипке .

Моя путаница заключалась в том, что прокруткаЯ обычно делаю, с точки зрения пикселей на странице.Но ваш код действительно не заботится об этом.Я имею в виду, не волнует колесо прокрутки мыши 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 У меня есть вопрос (слишком конкретный для объяснения в другом месте) ... Я протестировал свои собственные видео и получил очень плохие результаты... это почему?Что-то связанное с конкретными настройками кодирования видео?Знаете ли вы, какой кодировочный cmd понадобится для преобразования FFMPEG в формат WEBM, как видео, использованное в вашем примере?

0 голосов
/ 26 апреля 2018

Я пробовал это в Firefox и не сопоставим с «производительностью» Chrome, поэтому вы можете попробовать добавить 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>

Ссылка: https://stackoverflow.com/a/49095733/9648076

В случае удаленного вопроса, ответом является:

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;
}
0 голосов
/ 26 апреля 2018

Почему бы просто не определить, превышает ли дельта какой-либо порог, и если это так, разделить на 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>
...