CSS滚动捕捉类型与水平滚动

12

我做了一个网站,使用了 Bootstrap 4、水平滚动(通过jquery.mousewheel.js)和本地 CSS Snapping。我正在使用 Chrome 81,并不关心旧的或不支持的浏览器。

当我在 CSS 中应用 scroll-snap-type: x mandatory; 时,水平滚动停止工作(根本不会滚动)。如果删除 CSS 属性,则滚动行为正常。以下是我的 HTML 结构:

<div class="portfolio_content container-fluid h-100">
  <div class="row h-100 flex-row flex-nowrap scrollx long_content">
    <div class="col-12 h-100 project d-table" id="project1">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 1
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project2">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 2
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project3">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 3
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project4">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 4
        </p>
      </div>
    </div>
  </div>
</div>

CSS的关键条目在这里:

html, body {
    height: 100%;
}
.portfolio_content .scrollx {
    overflow-x: scroll;
}

.portfolio_content .long_content {
    scroll-snap-type: x mandatory; /* Comment this to make scrolling works */
}
.portfolio_content .project {
    scroll-snap-align: start;
}
.portfolio_content .project_title {
    display: block;
    margin: auto;
    text-align: center;
}
.portfolio_content .project_title h1 {
    font-size: 96px;
    line-height: 0.8;
}

最后,这是JSFiddle演示。如果您在CSS中注释第9行,则可以滚动演示。

我的目标是允许“项目”DIV在滚动时捕捉,即滚动后将完全显示一个“项目”DIV。

我应该如何更改代码?


你是否注意到 text-align: center 规则阻止了 .project_title 中的文本显示? - kmoser
但这与滚动无关。 - Raptor
也许不是直接的问题,但我已经删除了 scroll-snap-type 属性,它仍然无法滚动,因此看起来您的 CSS 存在问题,您需要先解决这些问题,然后再考虑滚动捕捉。 - kmoser
即使我将 text-align: center 注释掉,情况仍然没有任何改变。这个 CSS 属性只是为了说明问题,与问题本身无关。 - Raptor
不要使用任何鼠标滚轮JS库,它们在99.9%的情况下是像你这样的问题的原因。 - cloned
显示剩余3条评论
3个回答

2
你不需要外部插件,只需使用简单的脚本即可完成。

$(".portfolio_content .project").on('wheel', function(e) {
    e.preventDefault();        
    if (e.originalEvent.wheelDelta >= 0) {
        if ($(this).prev().length) {
            var prev = "#" + $(this).prev().attr("id");
            document.querySelector(prev).scrollIntoView({behavior: 'smooth'});
        }
    } else {
        if ($(this).next().length) {
            var next = "#" + $(this).next().attr("id");
            document.querySelector(next).scrollIntoView({behavior: 'smooth'});
        }
    }
});
html, body {
    height: 100%;
}
.portfolio_content .scrollx {
    overflow-x: scroll;
}

.portfolio_content .project {
 scroll-snap-align: start;
}
.portfolio_content .project_title {
 display: block;
 margin: auto;
 text-align: center;
}
.portfolio_content .project_title h1 {
 font-size: 96px;
 line-height: 0.8;
}
#project1, #project3 {
    background-color: #EEE;
}
#project2, #project4 {
    background-color: #CCC;
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>

<div class="portfolio_content container-fluid h-100">
  <div class="row h-100 flex-row flex-nowrap scrollx long_content">
    <div class="col-12 h-100 project d-table" id="project1">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 1
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project2">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 2
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project3">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 3
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project4">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 4
        </p>
      </div>
    </div>
  </div>
</div>


脚本无法正常工作。它不能将垂直滚动转换为水平滚动。 - Raptor
在我的Fiddle中,如果你垂直滚动,DIV内容将会水平滚动。然而在你的代码片段中,没有这样的功能。我必须自己水平滚动。 - Raptor
你使用的是什么浏览器?因为我的代码片段在所有基于Chromium内核的浏览器中都能正常工作,符合你问题中所要求的。但在Firefox上显示完全不同,这并不是我的错,我只是复制了你分享的代码。 - SilverSurfer
我正在使用最新版的Chrome浏览器。你尝试运行代码片段并点击全屏,但它已经失控了。 - Raptor

1

您需要将鼠标滚轮从X方向转换为Y方向,并应用于具有滚动条的元素,还需要在容器的垂直鼠标滚轮交互上防止默认行为,但您需要将其设置为容器的宽度,否则不足以推动。

 // 'swipe' left or right with mouse wheel:
const container = document.querySelector('.long_content');

container.addEventListener('wheel', (evt) => {

evt.preventDefault(); 

//scroll deirection
 let delta = evt.deltaY;
 //take widht of container:
let contWidth = evt.target.offsetWidth;

// check direction and apply to contWidth
if(delta < 0){
contWidth = -contWidth;
}
     
  container.scrollLeft += contWidth;  

});

这里是更新后的代码


0

简短回答:删除 JavaScript 代码。

详细回答:如果您想要检查或在滚轮事件上执行某些操作,则 mousewheel.js 很有用。在这种情况下,您只需让浏览器滚动即可。您正在函数中控制该事件并阻止默认行为,但默认操作正是您想要的。

当您执行 this.scrollLeft -= delta; 时,它会执行两个操作:滚动 然后 结束滚动scroll-snap-type 监听 结束滚动 并捕捉元素。实际上,如果您仅删除 e.preventDefault(); 行,会发生有趣的事情。元素会震动然后更改。在我看来,正在发生的是:1)默认滚动事件,需要一些时间。同时,2)滚轮事件,触发一个更短的滚动事件。3)捕捉。由于第二步的 deltaX 很小,因此您会回到起始位置。重复 2 和 3 几次。4)默认滚动事件以更大的 deltaX 结束。5)捕捉。

总之,对于您想要的行为,最好让浏览器进行滚动。如果您想要做其他事情,需要避免每次mousewheel事件触发时发送一个end scroll信号。为此,您可以简单地消除鼠标滚轮函数内的两行代码并添加所需的代码。
评论后编辑:
我应该从上下文中猜到,您想将垂直滚轮事件转换为水平滚轮事件。如果您发送水平滚轮事件(可以使用触摸板),则前面的答案有效。长答案仍然适用,因此问题是如何绕过问题。我想到了几种策略:
取消事件并创建合成的WheelEvent。迄今为止最优雅的解决方案,但我无法使其正常工作。事件被分派,但没有滚动发生:
 let lc = document.querySelector('.long_content');
 lc.addEventListener('wheel', function(e) {
   if (e.deltaY !== 0){
     e.preventDefault();
     let evt = new WheelEvent('wheel', {
       deltaX: e.deltaY
     });
     this.dispatchEvent(evt);
   }
   else{
     // The new event gets captured here
   }
  });

编辑2

有许多策略可以使其工作,但它们都有缺点。之前的方法对您的脚本影响最小,但更改始于滚轮事件结束后。这和其他参数由浏览器决定,不在您的控制范围内。

我添加了第二个演示,使用另一种策略,可以实现您想要的行为(https://jsfiddle.net/vhywmdb5/)。希望它有所帮助,但每个解决方案都需要进行微调,并且不能保证它适用于每个浏览器。不幸的是,目前mousewheel.js中的事件模拟存在问题。


但我必须将垂直滚动更改为水平滚动,因此我必须使用JS代码。有任何其他替代方案吗? - Raptor
我已经更新了答案。也许这个fiddle对你有用。 - vqf
1
感谢更新。当滚动时,特别是向上滚动(使内容向左滚动)时,Fiddle看起来很卡顿。 - Raptor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接