问题描述
我正在为年幼的孩子创建一个网络应用程序,他们可以在其中水平滚动非常宽的图片。年幼的孩子可能会疯狂地滑动触摸屏。我想允许惯性滚动(因为它很有趣)但要限制它,以便儿童用户不会快速向后和向前滚动到最后,错过中间的所有好东西。
在惯性滚动期间,会生成 mousewheel
事件。我曾希望在此事件中使用 event.preventDefault()
,以便在特定点停止惯性滚动。但是,似乎如果您不在特定操作的第一个 preventDefault()
事件上运行 mousewheel
,以下事件都不会被阻止。
下面的代码片段提供了一个演示。它包含一个可以滚动的元素,直到其 scrollLeft
值达到 5000。JavaScript 包含一个 mousewheel
事件侦听器,当 event.preventDefault()
的值大于2500。
我曾希望这会在中途停止惯性滚动。但是没有。
运行代码段,然后使用鼠标垫(或触摸屏)将可滚动元素向左滑动。你会看到:
- 如果您提供足够的惯性,元素将一直向左滚动。对
scrollLeft
的条件调用无效。 -
event.preventDefault()
事件持续发出最多 5 秒(取决于您用于滑动的能量)。即使在元素完全向左滚动后,它们也会继续发出。
但是,如果您向左滚动超过 2500,然后尝试向右滑动,则什么也不会发生。 first mousewheel
事件的默认操作被阻止,因此惯性滚动永远不会开始。
我的问题是:在滚动到达某个点后,有没有办法完全停止发出 mousewheel
事件?额外问题:有没有办法确定特定滑动手势的惯性有多大?
我有一个解决方法来限制 mousewheel
的值,但这意味着在发出最后一个 scrollLeft
事件之前,孩子将无法再次滑动。
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%)
}
解决方法
我认为您无法阻止浏览器发出事件。这就是为什么必须使用 preventDefault
。因此,要回答您的问题,
- 没有。但是,您可以劫持事件并使用它来适应您的预期行为。
- 是的。
mousewheel
事件的wheelDelta
值越大,惯性越大。请务必注意mousewheel
已弃用。您应该使用wheel
事件。在这种情况下,事件将具有您可以使用的deltaX
和deltaY
值。
考虑到这些因素,您可以轻松建立自己的行为。我简化了代码以强调策略。简而言之,您设置图片的 center
和围绕该中心的 interesting
区域。您始终阻止默认行为(劫持事件)。如果 scrollLeft
属性在 interesting
周围的 center
区域之外,则根据 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>