如何*累积*连续的`scrollBy`调用`behavior: "smooth"`来实现平滑滚动?

问题描述 投票:0回答:1

背景

我正在实现一项功能,按下 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
,则后续调用似乎会中断正在进行的动画,从而导致行为不一致和滚动速度变慢。

  1. 是否可以让浏览器累积连续的
    scrollBy
    调用而不是取消之前的调用?
  2. 如果没有,我如何修改我的代码以实现平滑滚动而不出现这些问题?

我更喜欢让浏览器处理流畅的动画,而不是通过 JavaScript 手动计算和制作动画。


尝试

  1. scrollBy
    behavior: "smooth"

    一起使用
    • 问题:连续调用会中断之前的动画。
  2. 访问

    scrollTop

    • 问题:动画结束前访问时,返回中间值,导致计算不可靠。
  3. 使用

    requestAnimationFrame
    平滑滚动(已考虑但未实施)

    • 如果可能的话,我想避免手动处理平滑的动画。
javascript html browser scroll smooth-scrolling
1个回答
0
投票

您可以使用

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",
  });
}

© www.soinside.com 2019 - 2024. All rights reserved.