如何用JavaScript实现不同高度多列异速滚动且底部均匀对齐
实现思路
1. 统一滚动入口,避免多事件冲突
放弃给每个列单独绑定滚动事件的方案,改为仅监听父容器.portfolio-grid的滚动行为,同时把三个.column的overflow属性改为hidden,禁止列自身的滚动能力,从根源上避免多滚动事件互相触发、嵌套执行的问题。
2. 按比例映射滚动距离
通过滚动比例联动各列的位置,保证同时触底:
- 计算父容器的最大可滚动距离:
父容器滚动距离上限 = 父容器scrollHeight - 父容器clientHeight - 计算每列的最大可滚动距离:
单列滚动距离上限 = 列scrollHeight - 列clientHeight - 父容器滚动时,先计算当前滚动比例:
滚动比例 = 父容器当前scrollTop / 父容器滚动距离上限 - 每列的
scrollTop直接赋值为:单列scrollTop = 滚动比例 * 单列滚动距离上限
这个逻辑天然满足「高度越高的列滚动速度越慢」的需求:总高度越高的列,单列滚动距离上限越大,相同滚动比例下滚动的总距离更长,但和父容器滚动的速度比值更小,符合预期。
3. 性能优化
用requestAnimationFrame包裹列scrollTop的更新逻辑,避免滚动事件高频触发时的不必要DOM操作,降低性能损耗。
如果列内图片是动态加载的,需要在所有图片加载完成后重新计算各列的最大滚动距离,避免高度计算错误。
代码实现
修改后的CSS
.column{ /* 关闭列自身滚动 */ overflow: hidden; scrollbar-width: none; -ms-overflow-style: none; height: 100%; background-color: #468189; float: left; width: 33.33%; padding-right: 10px; box-sizing: border-box; } .portfolio-grid{ overflow: auto; /* 可以根据实际布局调整高度,保证有滚动空间 */ height: 100vh; width: 80%; }
修改后的JavaScript
const grid = document.querySelector('.portfolio-grid') const columns = document.querySelectorAll('.column') // 存储各列的最大滚动距离 let colMaxScroll = [] let gridMaxScroll = 0 // 计算所有高度参数的函数,动态内容加载完成后需要重新调用 function calcScrollParams() { gridMaxScroll = grid.scrollHeight - grid.clientHeight colMaxScroll = Array.from(columns).map(col => col.scrollHeight - col.clientHeight) // 把父容器的滚动高度撑到和最高列的可滚动高度匹配,保证能滚到100%比例 const maxColScroll = Math.max(...colMaxScroll) const spacer = document.createElement('div') spacer.style.height = `${maxColScroll}px` // 先删除旧的占位元素再添加新的 grid.querySelector('.grid-spacer')?.remove() grid.appendChild(spacer) spacer.classList.add('grid-spacer') // 重新计算父容器最大滚动距离 gridMaxScroll = grid.scrollHeight - grid.clientHeight } // 滚动更新逻辑 function updateColScroll() { const ratio = grid.scrollTop / gridMaxScroll columns.forEach((col, index) => { col.scrollTop = ratio * colMaxScroll[index] }) } // 监听滚动,用requestAnimationFrame优化性能 grid.addEventListener('scroll', () => { requestAnimationFrame(updateColScroll) }) // 初始化计算,动态内容加载完成后需要手动调用calcScrollParams() calcScrollParams()
内容的提问来源于stack exchange,提问作者Joseph Perez
相关产品推荐
相关产品推荐

