我正在实现一项功能,按下 Alt 可以使滚动速度加快 5 倍,类似于 VSCode 中的行为。以下是相关代码片段(完整代码可以在CodePen找到):
/**
* Scrolling speed multiplier when pressing `Alt`.
* @type {number}
*/
const fastScrollSensitivity = 5;
function findScrollableElement(e, vertical = true, plus = true) {
// -- Omitted --
}
/**
* Handle the mousewheel event.
* @param {WheelEvent} e The WheelEvent.
*/
function onWheel(e) {
if (!e.altKey || e.deltaMode !== WheelEvent.DOM_DELTA_PIXEL) return;
e.preventDefault();
e.stopImmediatePropagation();
const { deltaY } = e;
const amplified = deltaY * fastScrollSensitivity;
const [vertical, plus] = [!e.shiftKey, e.deltaY > 0];
const el = findScrollableElement(e, vertical, plus);
Object.assign(lastScroll, { time: e.timeStamp, el, vertical, plus });
el.scrollBy({
top: e.shiftKey ? 0 : amplified,
left: e.shiftKey ? amplified : 0,
behavior: "instant"
});
// TODO: Smooth scrolling
}
/**
* Enable or disable the fast scroll feature.
* @param {boolean} enabled Whether the fast scroll feature is enabled.
*/
function fastScroll(enabled) {
if (enabled) {
document.addEventListener("wheel", onWheel, { capture: false, passive: false });
} else {
document.removeEventListener("wheel", onWheel, { capture: false, passive: false });
}
}
在
behavior: "instant"
中使用 scrollBy
时,该功能可以按预期工作。但是,切换到 behavior: "smooth"
会导致问题。
当我使用
behavior: "smooth"
时就会出现问题。如果在上一个动画结束之前重复调用 scrollBy
,则后续调用似乎会中断正在进行的动画,从而导致行为不一致和滚动速度变慢。
scrollBy
调用而不是取消之前的调用?我更喜欢让浏览器处理流畅的动画,而不是通过 JavaScript 手动计算和制作动画。
将
scrollBy
与 behavior: "smooth"
一起使用
访问
scrollTop
使用
requestAnimationFrame
平滑滚动(已考虑但未实施)
您可以使用
scrollBy
并通过添加每个 scrollTo
来手动跟踪目标坐标,而不是使用 scrollTo
(无论如何,它在旧浏览器上的支持不如 deltaY
那样好)。就像这样(CodePen上的完整编辑示例)
const lastScroll = {
targetX: -1, // keep track of target coordinates
targetY: -1, // keep track of target coordinates
time: 0,
el: document.scrollingElement,
vertical: true,
plus: true,
};
// ...
function onWheel(e) {
// ... as before
let targetX = lastScroll.targetX >= 0 ? lastScroll.targetX : el.scrollLeft;
targetX += e.shiftKey ? amplified : 0;
let targetY = lastScroll.targetY >= 0 ? lastScroll.targetY : el.scrollTop;
targetY += e.shiftKey ? 0 : amplified;
Object.assign(lastScroll, {
targetX,
targetY,
time: e.timeStamp,
el,
vertical,
plus,
});
el.scrollTo({
top: targetY,
left: targetX,
behavior: "smooth",
});
}