【问题标题】:Slide Horizontally on Vertical Mouse Scroll在垂直鼠标滚动上水平滑动
【发布时间】:2021-08-10 05:01:42
【问题描述】:

我制作了一个滑块,如果鼠标在其上垂直滚动,它会水平滚动。

// move slider horizontally on vertical scroll
const target = document.querySelector('.slider')

target.addEventListener('wheel', event => {
  const toLeft  = event.deltaY < 0 && target.scrollLeft > 0
  const toRight = event.deltaY > 0 && target.scrollLeft < target.scrollWidth - target.clientWidth

  if (toLeft || toRight) {
    event.preventDefault()
    target.scrollLeft += event.deltaY
  } 
})
.box {
  background: #ccc;
  height: 800px;
}

.slider {
  border: 5px solid black;
  background: #fff;
  overflow-x: scroll;
}

.item-wrap {
  height: 1000px;
  display: flex;
}

.item {
  min-width: 240px;
  height: 240px;
  font-size: 140px;
  border: 2px solid #d1d1d1;
  border-radius: 5px;
  padding: 5px;
  margin: 5px;
  line-height: 1.6;
  text-align: center;
}

/* add classes */
.test {
  position: fixed;
}
<div class="box"></div>
<div class="slider">
  <div class="item-wrap">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
    <div class="item">6</div>
    <div class="item">7</div>
    <div class="item">8</div>
    <div class="item">9</div>
  </div>
</div>
<div class="box"></div>

当从页面顶部向下滚动到滑块顶部时,我希望滑块保持固定,直到所有元素都滚动。当最后一项滚动时(在本例中为 9),我希望窗口继续滚动到页面底部。

然后当用户从页面底部滚动到滑块底部时,我希望滑块保持固定,直到所有元素都滚动。当最后一项滚动时(在本例中为 1),我希望窗口继续滚动到页面顶部。

this link 上滚动到页面底部显示我正在尝试模仿的内容。

我注意到他们一直在监听窗口滚动,直到滑块的顶部碰到视口的顶部。然后滑块的位置为fixed。当用户从页面底部滚动到滚动条底部时的概念相同。

但我不确定如何实现动态fixed位置。

【问题讨论】:

标签: javascript html css


【解决方案1】:

您需要使用css 位置属性和JavaScript 来有条件地检查offsetTopscrollY 并让幻灯片使用css translate3d 属性。
在计算每个滑块项的total width 后,需要一个额外的div (#pan) 来动态定义高度。

有用的链接: https://www.w3schools.com/cssref/css3_pr_transform.asp
注意:还要检查 Full Page

希望下面的sn-p对你有很大帮助。

window.onbeforeunload = function () {
    window.scrollTo(0, 0);
}

window.onload = function() {
    var wh = window.innerHeight;
    var ww = window.innerWidth;
    var slider = document.getElementById('slider');
    var pan = document.getElementById('pan');
    var item_wrap = document.getElementById('item_wrap').scrollWidth + wh/1.25;
    pan.style.height = item_wrap + 'px';

    window.addEventListener('scroll', function (event) {
        if (event.path[1].scrollY >= slider.offsetTop ) {
            if (event.path[1].scrollY < pan.offsetTop + pan.clientHeight) {
                slider.style.position = 'fixed'; //set position fixed
                let aa = event.path[1].scrollY - pan.offsetTop;
                document.getElementById('item_wrap').style.transform = 'translate3d(-' + aa + 'px, 0px, 0px)';   
            }
        }
        if (event.path[1].scrollY <= pan.offsetTop) {
            slider.style.position = 'relative'; //set position relative
        }
    });
}
*{box-sizing: border-box;}
body{
    font-size: Arial;
    padding: 0;
    margin: 0;
}
.box {
    background: #ccc;
    height: 100vh;
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #fff;
    font-size: 10vw;
}

.slider {
    background: #fff;
    width: 100%;
    min-height: 100vh;
    padding: 10px 0;
    top: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.item-wrap {
    display: flex;
    background: #fff;
    width: 100%;
    position: relative;
    transition: 0.5s cubic-bezier(0.05, 0.82, 0.165, 1);
}
.item {
    display: inline-flex;
    justify-content: center;
    align-items: center;
    min-width: 240px;
    width: 240px;
    height: 240px;
    font-size: 800%;
    border: 2px solid #d1d1d1;
    border-radius: 5px;
    padding: 5px;
    margin: 5px;
    line-height: 1;
}
.bg-tomato{
    background-color: tomato;
}
.bg-teal{
    background-color: teal;
}
.bg-thistle{
    background-color: thistle;
}
.bg-violet{
    background-color: violet;
}
<div class="box bg-tomato">Box - #1</div>
<div class="box bg-thistle">Box - #2</div>
<div class="slider" id="slider">
    <h2>Box - #3 (Slider)</h2>
    <div class="item-wrap" id="item_wrap">
        <div class="item">1</div>
        <div class="item">2</div>
        <div class="item">3</div>
        <div class="item">4</div>
        <div class="item">5</div>
        <div class="item">6</div>
        <div class="item">7</div>
        <div class="item">8</div>
        <div class="item">9</div>
        <div class="item">10</div>
        <div class="item">11</div>
        <div class="item">12</div>
        <div class="item">13</div>
    </div>
</div>
<div id="pan"></div>

<div class="box bg-teal">Box - #4</div>
<div class="box bg-violet">Box - #5</div>

【讨论】:

  • 在计算每个滑块项的总宽度后,我可以要求额外解释一个额外的 div (#pan) 如何动态定义高度吗?我很高兴代码有效,但我无法理解您计算背后的原因。
  • 假设您有 20 个幻灯片项目,每个幻灯片项目的宽度为 200px,因此 20 个幻灯片项目的宽度将为 20x200 = 4000。这意味着滑块宽度为 4000 px,但我们正在垂直滚动,我们的高度较小滚动,所以我们需要将总宽度 4000px 定义为 (#pan) 高度以添加更多垂直空间..
  • 我明白这就是为什么pan.style.height 等于document.getElementById('item_wrap').scrollWidth + wh/1.25scrollWidth 在您的示例中占 4000 像素。您还添加了额外的高度wh,以便在滚动 4000 像素后可以看到所有幻灯片。
  • 我还可以要求解释为什么if (event.path[1].scrollY &gt;= slider.offsetTop)if (event.path[1].scrollY &lt;= pan.offsetTop) 有两个if 条件而不是if else?为什么slider.offsetToppan.offsetTop要分开考虑?
  • 为了更可靠地解决抽搐问题,它也适用于event.path[1].scrollY &lt; pan.offsetTop + pan.clientHeightfixed position 的单个 if event.path[1].scrollY &lt; pan.offsetTop + pan.clientHeight 条件。最后一个如果位置:相对。