js如何控制移动端overflow:scroll容器滑动的最大速度?

<div class="box">
    <div class="main">
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
    </div>
</div>
.box {
  width: 400px;
  height: 60px;
  border: 2px solid blue;
  overflow: auto;
}
.main {
  width: 1000px;
  height: 100%;
  background: yellow;
  display: flex;
  align-items: center
}
.main div {
    flex: 1;
    text-align: center;
}

demo在这:https://jsrun.net/gBtKp
在移动端滑动一下这个.box区域,稍微一用力就到头了,如果我想用js去控制他滑动的最大速度该怎么去处理?

阅读 682
avatarAI BotBETA

在JavaScript中,控制移动端 overflow: scroll 容器的滑动速度通常不是直接通过CSS或原生JavaScript属性来实现的。这是因为滚动行为通常由浏览器的默认行为处理,而这些行为在不同的设备和浏览器上可能会有所不同。

然而,你可以通过监听滚动事件和触摸事件(如 touchstarttouchmovetouchend),然后手动计算并限制滚动速度来实现这一点。以下是一个基本的示例,展示了如何通过JavaScript来限制滚动速度:

### 回答

你可以使用以下JavaScript代码来控制移动端 `.box` 区域的滑动最大速度:

document.addEventListener('DOMContentLoaded', function () {

const box = document.querySelector('.box');

let startX, startY, scrollTop, scrollLeft, time;

box.addEventListener('touchstart', function (e) {
    const touch = e.touches[0];
    startX = touch.clientX;
    startY = touch.clientY;
    scrollTop = box.scrollTop;
    scrollLeft = box.scrollLeft;
    time = Date.now();
});

box.addEventListener('touchmove', function (e) {
    const touch = e.touches[0];
    const deltaX = touch.clientX - startX;
    const deltaY = touch.clientY - startY;
    const currentTime = Date.now();
    const timeDiff = currentTime - time;

    // 计算速度
    const velocityX = deltaX / timeDiff;
    const velocityY = deltaY / timeDiff;

    // 设置最大速度(例如,每秒最多滚动100像素)
    const maxVelocity = 100;

    // 限制速度
    const limitedVelocityX = Math.sign(velocityX) * Math.min(Math.abs(velocityX), maxVelocity);
    const limitedVelocityY = Math.sign(velocityY) * Math.min(Math.abs(velocityY), maxVelocity);

    // 计算新的滚动位置
    const newScrollTop = scrollTop - limitedVelocityY * (timeDiff / 1000);
    const newScrollLeft = scrollLeft - limitedVelocityX * (timeDiff / 1000);

    // 应用新的滚动位置(注意:这里直接设置scrollTop和scrollLeft可能会导致跳动,
    // 实际应用中可能需要使用平滑滚动或请求动画帧)
    box.scrollTop = Math.min(Math.max(newScrollTop, 0), box.scrollHeight - box.clientHeight);
    box.scrollLeft = Math.min(Math.max(newScrollLeft, 0), box.scrollWidth - box.clientWidth);

    // 更新起始点和时间
    startX = touch.clientX;
    startY = touch.clientY;
    time = currentTime;
});

});


**注意**:

1. 上面的代码直接修改了 `scrollTop` 和 `scrollLeft`,这可能会导致滚动行为看起来不那么平滑。在实际应用中,你可能需要使用平滑滚动技术或 `requestAnimationFrame` 来改进这一点。
2. 滚动速度的限制是通过计算手指移动的速度并将其限制在一个最大速度来实现的。这里使用了一个简单的线性速度计算,实际应用中可能需要更复杂的算法来处理不同的滚动情况(如惯性滚动)。
3. 由于不同设备和浏览器的触摸事件处理可能会有所不同,因此上述代码可能需要在不同的设备和浏览器上进行测试和调整。
4 个回答

1736407840845.png

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        .box {
            width: 400px;
            height: 60px;
            border: 2px solid blue;
            overflow: hidden; 
            position: relative;
        }
        .main {
            width: 1000px;
            height: 100%;
            background: yellow;
            display: flex;
            align-items: center;
            position: absolute; 
            left: 0;
            top: 0;
            transform: translateX(0);
            transition: transform 0.3s; 
        }
        .main div {
            flex: 1;
            text-align: center;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="main">
            <div>1</div>
            <div>2</div>
            <div>3</div>
            <div>4</div>
            <div>5</div>
        </div>
    </div>

    <script>
        const box = document.querySelector('.box');
        const main = document.querySelector('.main');
        let startX = 0;
        let scrollLeft = 0;
        let isDragging = false;
        let lastX = 0;
        let velocity = 0;
        let maxVelocity = 15; // 控制最大滑动速度
        let animationId;

        function handleTouchStart(e) {
            isDragging = true;
            startX = e.touches[0].pageX - main.offsetLeft;
            lastX = e.touches[0].pageX;
            
            cancelAnimationFrame(animationId);
            
            main.style.transition = 'none';
        }

        function handleTouchMove(e) {
            if (!isDragging) return;

            e.preventDefault();
            const currentX = e.touches[0].pageX;
            const diff = currentX - lastX;
            
            // 计算速度
            velocity = Math.min(Math.abs(diff), maxVelocity) * Math.sign(diff);
            
            // 计算新位置
            const newLeft = parseInt(main.style.transform.replace('translateX(', '')) || 0;
            const nextPos = newLeft + velocity;
            
            // 限制边界
            const maxScroll = box.offsetWidth - main.offsetWidth;
            const boundedPos = Math.max(maxScroll, Math.min(0, nextPos));
            
            // 应用位移
            main.style.transform = `translateX(${boundedPos}px)`;
            
            lastX = currentX;
        }

        function handleTouchEnd(e) {
            isDragging = false;
            
            // 加惯性滑动
            const decelerate = () => {
                if (Math.abs(velocity) > 0.5) {
                    velocity *= 0.95; // 减速因子
                    
                    const currentPos = parseInt(main.style.transform.replace('translateX(', '')) || 0;
                    const nextPos = currentPos + velocity;
                    const maxScroll = box.offsetWidth - main.offsetWidth;
                    const boundedPos = Math.max(maxScroll, Math.min(0, nextPos));
                    
                    main.style.transform = `translateX(${boundedPos}px)`;
                    
                    animationId = requestAnimationFrame(decelerate);
                } else {
                    
                    main.style.transition = 'transform 0.3s';
                    snapToNearest();
                }
            };
            
            decelerate();
        }

        function snapToNearest() {
            const currentPos = parseInt(main.style.transform.replace('translateX(', '')) || 0;
            const itemWidth = main.offsetWidth / 5; // 假设有5个等宽的项目
            const snapIndex = Math.round(-currentPos / itemWidth);
            const snapPos = -snapIndex * itemWidth;
            
            main.style.transform = `translateX(${snapPos}px)`;
        }

        box.addEventListener('touchstart', handleTouchStart);
        box.addEventListener('touchmove', handleTouchMove, { passive: false });
        box.addEventListener('touchend', handleTouchEnd);
    </script>
</body>
</html>

禁用滚动,自己监听触摸事件来控制容器滚动

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Scroll Speed Control</title>
    <style>
        .box {
            width: 400px;
            height: 60px;
            border: 2px solid blue;
            overflow: auto;
        }
        .main {
            width: 1000px;
            height: 100%;
            background: yellow;
            display: flex;
            align-items: center;
        }
        .main div {
            flex: 1;
            text-align: center;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="main">
            <div>1</div>
            <div>2</div>
            <div>3</div>
            <div>4</div>
            <div>5</div>
        </div>
    </div>
    <script>
        const container = document.querySelector('.box');
        let startY = 0;
        let startScrollTop = 0;
        let lastTimestamp = 0;
        const maxSpeed = 0.5; // 最大速度,单位:像素/毫秒

        container.addEventListener('touchstart', (event) => {
            startY = event.touches[0].clientY;
            startScrollTop = container.scrollTop;
            lastTimestamp = event.timeStamp;
        });

        container.addEventListener('touchmove', (event) => {
            const currentY = event.touches[0].clientY;
            const currentTimestamp = event.timeStamp;
            const deltaY = startY - currentY;
            const deltaTime = currentTimestamp - lastTimestamp;

            if (deltaTime > 0) {
                const speed = deltaY / deltaTime;
                if (Math.abs(speed) > maxSpeed) {
                    event.preventDefault();
                    container.scrollTop = startScrollTop + Math.sign(deltaY) * maxSpeed * deltaTime;
                } else {
                    container.scrollTop = startScrollTop + deltaY;
                }
            }
        });

        container.addEventListener('touchend', () => {
            startY = 0;
            startScrollTop = 0;
            lastTimestamp = 0;
        });
    </script>
</body>
</html>

可以通过 touchmove 事件监听来控制滑动速度,结合 event.preventDefault() 和自定义逻辑实现滑动速率限制:

let lastTouchY = 0;
let velocity = 0;
const maxVelocity = 2; // 最大速度

document.querySelector('.scroll-container').addEventListener('touchmove', (event) => {
  const currentTouchY = event.touches[0].clientY;
  const deltaY = currentTouchY - lastTouchY;

  velocity = Math.abs(deltaY);
  if (velocity > maxVelocity) {
    event.preventDefault(); // 阻止默认滑动行为
    return;
  }

  lastTouchY = currentTouchY;
});

注意:这种实现方式会影响用户体验,建议谨慎使用。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏