如何停止 Web 应用程序中的惯性滚动

How to stop inertial scrolling in a web app

我正在为年轻人 children 创建一个网络应用程序,他们可以在其中水平滚动非常宽的图片。年轻人 children 可能会疯狂地滑动触摸屏。我想允许惯性滚动(因为它很有趣)但限制它以便 child 用户不会快速向后和向前滚动到最后,错过中间的所有好东西。

在惯性滚动期间,会生成 mousewheel 事件。我曾希望在此事件中使用 event.preventDefault(),以便在特定点停止惯性滚动。但是,似乎如果您在特定操作的第一个 mousewheel 事件中不 运行 preventDefault(),则以下事件中的 none 将被阻止。

下面的代码片段提供了一个演示。它包含一个可以滚动直到其 scrollLeft 值达到 5000 的元素。JavaScript 包含一个 mousewheel 事件侦听器,当 [=17= 的值达到时显式调用 event.preventDefault() ] 大于 2500。

我曾希望这会中途停止惯性滚动。但是没有。

运行 片段,然后使用鼠标垫(或触摸屏)向左滑动可滚动元素。你会看到:

  1. 如果您提供足够的惯性,该元素将一直滚动到左侧。对 event.preventDefault() 的条件调用无效。
  2. mousewheel 事件将持续发出最多 5 秒(取决于您滑动所用的能量)。即使在元素完全向左滚动后,它们也会继续发射。

但是,如果您向左滚动超过 2500,然后尝试向右滑动,则不会发生任何事情。 first mousewheel 事件的默认操作被阻止,因此惯性滚动永远不会启动。

我的问题是:有没有办法在滚动到达某个点后完全停止发出 mousewheel 事件?奖励问题:有没有办法确定特定滑动手势中有多少惯性?

我有一个限制 scrollLeft 值的变通方法,但这意味着 child 将无法再次滑动,直到发出最后一个 mousewheel 事件。

const output = document.getElementById("output");
const container = document.getElementById("container");
container.addEventListener("mousewheel", mousewheel, false);

let startTime = 0
let timeOut = 0

function mousewheel(event) {
  const time = getTime()
  const scrollLeft = Math.round(container.scrollLeft * 10) / 10;
  output.innerText = time + "s\n" + scrollLeft;

  clearTimeout(timeOut)
  timeOut = setTimeout(() => startTime = 0, 100)

  if (scrollLeft > 2500) {
    event.preventDefault();
    return false;
  }
}

function getTime() {
  const time = + new Date()
  if (!startTime) {
    startTime = time;
  }
  return (time - startTime) % 10000 / 1000
}
body {
  margin: 0;
  height: 100px;
}

div#container {
  width: 500px;
  height: 100%;
  background-color: green;
  overflow: auto;
}

div#content {
  width: 5500px; /* Allows scrollLeft up to 5000 */
  height: 100%;
  background-color: red;
  clip-path: polygon(0 0, 100% 0, 100% 100%)
}
<div id="container">
  <div id="content"></div>
</div>
<pre id="output">0</p>

我认为您无法阻止浏览器发出事件。这就是为什么必须使用 preventDefault 的原因。因此,为了回答您的问题,

  1. 没有。但是,您可以劫持该事件并使用它来满足您的预期行为。
  2. 是的。 mousewheel 事件有一个 wheelDelta 值,惯性越大,该值越大。请务必注意 mousewheel 已弃用。您应该使用 wheel 事件。在这种情况下,该事件将具有您可以使用的 deltaXdeltaY 值。

考虑到这些因素,您可以轻松地建立自己的行为。我简化了代码以强调该策略。简而言之,您设置图片的 center 和该中心周围的 interesting 区域。你总是阻止默认行为(劫持事件)。如果 scrollLeft 属性 在 center 周围的 interesting 区域之外,则根据 deltaX 移动卷轴(可选 multiplier) .如果它在该区域内,您可以通过划分它来使 scrollLeft 中的变化小得多。请注意,在示例中,除数可能太大而无法强调效果。

const output = document.getElementById("output");
const container = document.getElementById("container");
container.addEventListener("wheel", mousewheel, false);

const multiplier = 2;
const center = 2500;
const interesting = 500;

function mousewheel(event) {
  event.preventDefault();
  let dx = event.deltaX * multiplier;
  if (Math.abs(container.scrollLeft - center) < interesting){
    dx /= 25;
  }
  container.scrollLeft += dx;
  
}
body {
  margin: 0;
  height: 100px;
}

div#container {
  width: 500px;
  height: 100%;
  background-color: green;
  overflow: auto;
}

div#content {
  width: 5500px; /* Allows scrollLeft up to 5000 */
  height: 100%;
  background-color: red;
  clip-path: polygon(0 0, 100% 0, 100% 100%)
}
<div id="container">
  <div id="content"></div>
</div>
<pre id="output">0</p>