页面先水平滚动后垂直滚动的交互实现及参数设置咨询
解决水平转垂直滚动的两个核心问题
我来帮你理清这两个关键问题,咱们一步步拆解:
1. Horizontal区域的高度设置逻辑
Horizontal区域的高度决定了用户需要垂直滚动多少距离,才能完成整个水平滚动流程。它的核心关联公式是:
horizontal高度 = (内部元素总宽度 - 视口宽度) * (视口高度 / 视口宽度)
为什么是这个公式?
假设你的水平滚动区有N个100vw宽的item,那么内部元素总宽度是N * 100vw,需要水平移动的总距离是(N-1)*100vw(从第一个item到最后一个item完全占满视口)。
为了让鼠标滚轮的滚动体验更自然,我们希望垂直滚动的距离和水平移动的距离成比例:当你垂直滚动H像素时,水平刚好移动完(N-1)*100vw的距离。这个比例就是视口高度和宽度的比值(因为滚轮滚动的感知是垂直方向的,要对应水平方向的移动幅度)。
简化后,当每个item都是100vw宽时,公式可以写成:
horizontal高度 = (N-1) * 100vh
比如2个item时,高度就是100vh;3个item时就是200vh,以此类推。
2. 正确的Translate比例计算
我们的目标是:当垂直滚动到horizontal区域的底部时,水平滚动区的最后一个item刚好完全占满视口。对应的transform计算逻辑是:
水平移动距离 = - (垂直滚动距离 * 视口宽度 / 视口高度)
原理
当你滚动y像素的垂直距离时,水平方向需要移动的距离是y * (视口宽度/视口高度)——这个比例保证了滚动体验的一致性。用负数是因为要向左移动。
如果用百分比的话,也可以基于.slider-inner的总宽度计算,但用视口单位或px更直观,避免百分比的混淆。
修改后的完整代码
JavaScript
let slider = document.querySelector('.slider-inner'); let itemCount = slider.children.length; // 计算horizontal区域的高度:(item数量-1)*视口高度 let horizontalHeight = (itemCount - 1) * window.innerHeight; document.querySelector(".horizontal").style.height = `${horizontalHeight}px`; function animate() { let y = window.scrollY; // 限制y的最大值不超过horizontal区域的高度,避免过度滚动 let clampedY = Math.min(y, horizontalHeight); // 计算水平移动距离:- (滚动距离 * 视口宽度 / 视口高度) let translateX = - (clampedY * window.innerWidth / window.innerHeight); slider.style.transform = `translateX(${translateX}px)`; requestAnimationFrame(animate); } animate(); // 监听窗口大小变化,重新计算高度和移动距离 window.addEventListener('resize', () => { horizontalHeight = (itemCount - 1) * window.innerHeight; document.querySelector(".horizontal").style.height = `${horizontalHeight}px`; });
CSS
/* 重置默认边距 */ * { margin: 0; padding: 0; box-sizing: border-box; } .horizontal{ position: relative; width: 100vw; /* 高度由JS动态计算 */ } .slider-container{ position: sticky; top: 0; width: 100vw; height: 100vh; overflow: hidden; } .slider-inner{ display: flex; /* 总宽度由item数量自动决定,不需要硬写200vw */ width: fit-content; } .item{ width: 100vw; height: 100vh; background-color: red; border: solid 1px black; } .vertical{ width: 100vw; height: 100vh; background-color: aqua; }
HTML
<div class="horizontal"> <div class="slider-container"> <div class="slider-inner"> <div class="item"></div> <div class="item"></div> </div> </div> </div> <div class="vertical"></div>
关键优化点
- 用
fit-content让.slider-inner的宽度自动适配item数量,不用手动写固定值 - 添加了窗口resize监听,保证窗口大小变化时效果依然正确
- 用
Math.min限制滚动距离,避免horizontal滚动完成后继续触发不必要的水平移动
内容的提问来源于stack exchange,提问作者Bruce
相关产品推荐
相关产品推荐

