如何实现桌面端网页平滑水平滚动?支持Vue 3/原生JS
横向平滑滚动实现方案
原生JavaScript实现
通过requestAnimationFrame实现逐帧缓动动画,替代瞬间修改scrollLeft的方式,彻底解决块状滚动问题:
// 初始化目标滚动位置和动画状态 let targetScrollLeft = document.documentElement.scrollLeft; let isAnimating = false; function scrollHorizontally(e) { e.preventDefault(); // 标准化滚轮方向值(-1向下滚,1向上滚) const delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail))); // 单次滚轮的总移动步长,可根据需求调整 const scrollStep = delta * 60; // 更新目标滚动位置 targetScrollLeft -= scrollStep; // 限制滚动范围,避免超出内容边界 targetScrollLeft = Math.max(0, Math.min(targetScrollLeft, document.documentElement.scrollWidth - window.innerWidth)); // 未在动画中则启动动画循环 if (!isAnimating) { isAnimating = true; requestAnimationFrame(animateScroll); } } function animateScroll() { const currentScroll = document.documentElement.scrollLeft; // 计算当前位置与目标位置的差值 const diff = targetScrollLeft - currentScroll; // 差值小于1时,直接定位到目标位置并结束动画 if (Math.abs(diff) < 1) { document.documentElement.scrollLeft = targetScrollLeft; document.body.scrollLeft = targetScrollLeft; isAnimating = false; return; } // 缓动移动(系数0.1可调整,数值越小动画越平缓) document.documentElement.scrollLeft += diff * 0.1; document.body.scrollLeft += diff * 0.1; requestAnimationFrame(animateScroll); } // 绑定滚轮事件(兼容Firefox) document.addEventListener('mousewheel', scrollHorizontally); document.addEventListener('DOMMouseScroll', scrollHorizontally);
核心逻辑说明
- 利用
requestAnimationFrame实现浏览器原生帧动画,保证动画流畅度 - 通过缓动公式(每次移动差值的10%)实现平滑过渡,避免块状跳跃
- 限制滚动范围,防止超出内容边界
- 统一处理不同浏览器的滚轮事件参数
Vue3实现方案
封装为自定义指令,方便在组件中复用,同时避免内存泄漏:
1. 注册全局自定义指令
// main.js import { createApp } from 'vue'; import App from './App.vue'; const app = createApp(App); app.directive('horizontal-scroll', { mounted(el) { let targetScrollLeft = el.scrollLeft; let isAnimating = false; const handleWheel = (e) => { e.preventDefault(); const delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail))); const scrollStep = delta * 60; targetScrollLeft -= scrollStep; // 限制容器内的滚动范围 targetScrollLeft = Math.max(0, Math.min(targetScrollLeft, el.scrollWidth - el.clientWidth)); if (!isAnimating) { isAnimating = true; requestAnimationFrame(animate); } }; const animate = () => { const current = el.scrollLeft; const diff = targetScrollLeft - current; if (Math.abs(diff) < 1) { el.scrollLeft = targetScrollLeft; isAnimating = false; return; } el.scrollLeft += diff * 0.1; requestAnimationFrame(animate); }; // 绑定事件 el.addEventListener('mousewheel', handleWheel); el.addEventListener('DOMMouseScroll', handleWheel); // 存储清理函数,用于卸载时移除事件 el._unbindScroll = () => { el.removeEventListener('mousewheel', handleWheel); el.removeEventListener('DOMMouseScroll', handleWheel); }; }, unmounted(el) { // 组件卸载时清理事件 if (el._unbindScroll) el._unbindScroll(); } }); app.mount('#app');
2. 在组件中使用
<template> <div class="horizontal-wrap" v-horizontal-scroll> <!-- 你的横向溢出内容 --> </div> </template> <style scoped> .horizontal-wrap { width: 100vw; overflow-x: auto; white-space: nowrap; } </style>
为什么原生scrollBy({behavior: 'smooth'})体验差?
浏览器内置的平滑滚动是通用实现,无法精准匹配自定义的大跨度滚动步长,且不同浏览器的动画曲线、延迟表现不一致,导致体验割裂。自行实现帧动画可以完全控制滚动速度、缓动效果,体验更统一。
内容的提问来源于stack exchange,提问作者sterix24
相关产品推荐
相关产品推荐

