【问题标题】:How to make infinite Scrolling?如何进行无限滚动?
【发布时间】:2021-11-21 20:11:11
【问题描述】:

我正在创建一个具有水平滚动效果的滑块但我卡在一个点上如何使滑块无限滚动就像在我的代码中你可以看到 Item 6 之后它停止滚动,我必须向后滚动,但我希望它像第 6 项之后一样,第 1 项会再次出现类似这样的东西 https://durimel.io/nel 这里你可以看到滚动是无限的?

那么有人可以帮忙吗?

let container = document.querySelector(".container")
let container1 = document.querySelector(".container1")

window.onscroll = ()=>{
  container.style.left = `${-window.scrollY}px` 
  container1.style.right = `${-window.scrollY}px` 
  
}
let currentpos = container.getBoundingClientRect().left
let currentpos1 = container1.getBoundingClientRect().left


let callDisort = () =>{
  let newPos = container.getBoundingClientRect().left;
  let newPos1 = container1.getBoundingClientRect().left;
  let diff = newPos - currentpos;
  let speed = diff * 0.50
  container.style.transform = `skewX(${speed}deg)`
  currentpos = newPos
  container1.style.transform = `skewX(${speed}deg)`
  currentpos = newPos

  requestAnimationFrame(callDisort)
}
console.log(currentpos)


callDisort()
*{
  margin:0;
  padding:0;
  box-sizing:border-box;
  font-family: arial;
}
html,body{
  height:3000px;
   overflow-X:hidden;
}

.container{
  position:fixed;
  display:flex;
  justify-content: space-between;
  top:30vh;
  width: 3000px;
  transition:transform 0.15s;
  will-change:transform;
  border:2px solid green;
 
}
.container1{
  position:fixed;
  display:flex;
  justify-content: space-evenly;
  top:45vh;
  width: 3000px;
  transition:transform 0.15s;
  will-change:transform;
  border:2px solid green;
}

.box{
  position:relative;

}
.box h2{
  font-size:4em;
}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <div class="container">
    <div class="box one">
      <h2>Item 1</h2>
    </div>
    <div class="box two">
      <h2>Item 2</h2>
    </div>
    <div class="box three">
      <h2>Item 3</h2>
    </div>
    <div class="box four">
      <h2>Item 4</h2>
    </div>
    <div class="box five">
      <h2>Item 5</h2>
    </div>
    <div class="box six">
      <h2>Item 6</h2>
    </div>
  </div>

      <div class="container1">
    <div class="box one">
      <h2>Item 1</h2>
    </div>
    <div class="box two">
      <h2>Item 2</h2>
    </div>
    <div class="box three">
      <h2>Item 3</h2>
    </div>
    <div class="box four">
      <h2>Item 4</h2>
    </div>
    <div class="box five">
      <h2>Item 5</h2>
    </div>
    <div class="box six">
      <h2>Item 6</h2>
    </div>
  </div>
  


    
</body>
</html>

【问题讨论】:

  • @Christopher 感谢您的回复,但这不是我问题的答案
  • 通过观察者,您可以检测当前可见的项目,并将不可见的项目添加/附加到 scrollParent 以提供无限滚动的感觉。
  • @Christopher 你能不能把它写成像代码 sn-p 这样的答案形式,这样我可以更好地理解

标签: javascript html jquery css


【解决方案1】:

示例 (https://durimel.io/nel) 使用的方法不同,并不是真正的无限。它仅限于 css 属性 lefttransform: translate3d() 支持的最大值。 But its enough for a normal use.

它会根据方向改变每个框的位置,并使用transform: translate3d()left: ...将其移动到“最后一个”之后或“第一个”之前。

总的来说,这里是我提到的方法的版本。我建议在jsfiddle 上对其进行测试,或者在“整页”视图中运行 sn-p,因为不可滚动 iframe-childs 的鼠标滚轮滚动行为无法阻止。

更新:

  • 添加了一个简单的速度检测例程以允许更快/更慢的滚动。
  • 还修复了 js 部分末尾的观察者选择器

const eventHandler = (e) => {
  document.querySelectorAll(".boxes-container").forEach(container => {
    const cur = +container.dataset.cur || 0;
    container.dataset.before = container.dataset.cur;
    container.dataset.scrollspeed = (+container.dataset.scrollspeed || 0) +1;
    setTimeout(() => {
        container.dataset.scrollspeed = +container.dataset.scrollspeed -1;
    }, 33 * +container.dataset.scrollspeed);
    let moveByPixels = Math.round(e.deltaY / (6 - Math.min(+container.dataset.scrollspeed,5)));
    if (container.dataset.direction == "invert") {
      moveByPixels *= -1;
    }
    container.style.left = `${cur + -moveByPixels}px`;
    container.dataset.cur = cur + -moveByPixels;
  });
};

window.addEventListener("wheel", eventHandler);
window.addEventListener("mousewheel", eventHandler);

const observer = new IntersectionObserver((entries, opts) => {
  entries.forEach(entry => {
    entry.target.classList.toggle('visible', entry.isIntersecting);
  });
  document.querySelectorAll(".boxes-container").forEach(container => {
    const before = (+container.dataset.before || 0),
      current = (+container.dataset.cur || 0),
      diff = before - current,
      boxes = [...container.querySelectorAll(".box")],
      visible = [...container.querySelectorAll(".box.visible")],
      first = boxes.indexOf(visible[0]),
      last = boxes.indexOf(visible[visible.length - 1]),
      adjust = (by) => {
        container.dataset.cur = +container.dataset.cur + by;
        container.dataset.before = +container.dataset.before + by;
        container.style.left = +container.dataset.cur + 'px';
      };
    if (diff >= 0) {
      if (first > 0) { // move the first to the end
        const box = boxes[0];
        box.parentNode.append(box);
        adjust(box.clientWidth);
      }
    } else {
      if (last == 0 || first == 0) { // move the to first
        const box = boxes[boxes.length - 1];
        box.parentNode.prepend(box);
        adjust(-box.clientWidth);

      }
    }
  })
}, { // trigger on any percent value
  threshold: new Array(101).fill(0).map((n, i) => +(i / 100).toFixed(2))
});
document.querySelectorAll(".boxes-container .box").forEach(el => observer.observe(el));
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: Sans-Serif;
}

.boxes-container {
  position: fixed;
  display: flex;
  flex-wrap: nowrap;
  flex-direction: row;
  white-space: nowrap;
  min-width: -min-content;
}

.v30 {
  top: 30vh;
}

.v60 {
  top: 60vh;
}

.box {
  position: relative;
  margin: 0 !important;
  padding: 0 50px;
}

.box h2 {
  font-size: 5rem;
}
<div class="boxes-container">
  <div class="box">
    <h2>0</h2>
  </div>
  <div class="box">
    <h2>1</h2>
  </div>
  <div class="box">
    <h2>2</h2>
  </div>
  <div class="box">
    <h2>3</h2>
  </div>
  <div class="box">
    <h2>4</h2>
  </div>
  <div class="box">
    <h2>5</h2>
  </div>
  <div class="box">
    <h2>6</h2>
  </div>
</div>

<div class="boxes-container v30" data-direction="invert">
  <div class="box">
    <h2>A</h2>
  </div>
  <div class="box">
    <h2>B</h2>
  </div>
  <div class="box">
    <h2>C</h2>
  </div>
  <div class="box">
    <h2>D</h2>
  </div>
  <div class="box">
    <h2>E</h2>
  </div>
  <div class="box">
    <h2>F</h2>
  </div>
  <div class="box">
    <h2>G</h2>
  </div>
</div>

<div class="boxes-container v60">
  <div class="box">
    <h2>0</h2>
  </div>
  <div class="box">
    <h2>1</h2>
  </div>
  <div class="box">
    <h2>2</h2>
  </div>
  <div class="box">
    <h2>3</h2>
  </div>
  <div class="box">
    <h2>4</h2>
  </div>
  <div class="box">
    <h2>5</h2>
  </div>
  <div class="box">
    <h2>6</h2>
  </div>
</div>

【讨论】:

  • 我正在使用您的解决方案,它很好,但是为什么当我通过 css 添加过渡效果时它不起作用。我想要平滑的过渡效果,但它表现得很奇怪。你能指导一下这件事吗?谢谢
  • 项目被移除并添加到 DOM。这可能会破坏一些 CSS 效果。
  • 如何实现css效果不应该破,有没有推荐的解决方案。
  • 插入后可以使用Element.animate函数。在 adjust() 调用之后或移动时。
  • 感谢您的帮助。让我试试这个解决方案。我会更新的。
【解决方案2】:

关于这个解决方案的一些 cmets。

  1. 如果容器设置为display:flex; justify-content:space-around;,则项目之间的空间会随着您滚动而改变(项目越多,包装越紧密)。将每个.box 更改为具有固定宽度的justify-content:flex-start; 可提供最佳结果。

  2. 添加 debounce fn 极大地改善了(并简化了)这项工作。至少,控制台日志令人印象深刻(!)。

  3. 如果您快速滚动滚轮,您可能会在重新填充之前到达轮播的末尾。为了便于查看,请将延迟从 50 更改为 500(毫秒)。

  4. debounce 的意思是,“只有在自上次滚动事件后 50 毫秒后才将更多的盒子放入容器中。您可能更喜欢节流功能,它最多会运行一次 -每 50 毫秒填充一次(或在您设置 debounceDelay 值时)。

  5. HTML,Body 高度需要设置为一个非常大的数字 - 在此演示中现在设置为 30,000。 .container 的宽度应该是 auto,并且它(.container 的宽度)会在每次新项目被放入容器时增加。

  6. 最重要的是:$$$不是 jQuery。它们是 document.querySelector()document.querySelectorAll() 的纯 JavaScript 简写

  7. 演示最好全页查看(演示窗口右上角的链接)。

const $ = document.querySelector.bind(document);
const $$ = document.querySelectorAll.bind(document);
let kontainer = $(".container");
const boxes = $$('.box');
const debounceDelay = 50; //change to 100 for better performance

const updateCarousel = debounce(function(e){
  console.log(scrollY +' // '+ kontainer.getBoundingClientRect().right)
  const currKontainerWidth = kontainer.getBoundingClientRect().right;
  if ( currKontainerWidth - scrollY < 300 ){
    for (let i=0, j=boxes.length; j > i; i++){
      kontainer.appendChild(boxes[i].cloneNode(true));
    }
  }
}, debounceDelay);

window.addEventListener('scroll', updateCarousel, false);

window.onscroll = () => {
  kontainer.style.left = `${-window.scrollY}px`;
}

function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};
*{
  margin:0;
  padding:0;
  box-sizing:border-box;
  font-family: arial;
}
html,body{
  height:30000px;
   overflow-X:hidden;
}

.container{
  position:fixed;
  display:flex;
  justify-content: flex-start;
  top:30vh;
  width: auto;
  transition:transform 0.15s;
  will-change:transform;
  border:2px solid green;
 
}

.box{
  position:relative;
  min-width:250px;

}
.box h2{
  font-size:4em;
}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <div class="container">
    <div class="box one">
      <h2>Item 1</h2>
    </div>
    <div class="box two">
      <h2>Item 2</h2>
    </div>
    <div class="box three">
      <h2>Item 3</h2>
    </div>
    <div class="box four">
      <h2>Item 4</h2>
    </div>
    <div class="box five">
      <h2>Item 5</h2>
    </div>
    <div class="box six">
      <h2>Item 6</h2>
    </div>
  </div>

    
</body>
</html>

最佳浏览“整页” (点击运行代码片段后的右上角链接)

【讨论】:

  • 关于 #5 将 scroll 更改为 wheel 事件并根据 deltaY 调整值解决了这个问题,并且还允许负值。
  • 去做吧...! 您已获得我的许可(和鼓励)接受我的回答,按照您的建议进行改进,然后将其作为您自己的答案提交。 (如果我喜欢你的想法,听起来我会的,你会得到我的支持——我会鼓励 OP 选择你的修订版作为最佳答案。)(顺便说一句,我刚刚看了你的答案......做得很好 - 真的很棒。比这个更复杂一点,这可能对这个 OP 很重要,但是方式,更好。我相信未来的读者会展示他们的 ^approbation^ +1)
  • 任何人在看这个(我的)答案,也请看克里斯托弗的答案。他的稍微高级一点,但更好的是(a)它不会持续在 DOM 中添加越来越多的内容,并且 (b) 它使用 IntersectionObserver API。至少,克里斯托弗的回答值得花时间去研究和理解。而且,坦率地说,添加书签并返回(就像我将要做的那样)。
  • @cssyphus 很好的解决方案,但有问题的是有 3 个容器,所以我们如何滚动 3 个不同的行,中间的一个以相反的方向滚动,如您所见。您只给出了 1 个容器示例。我试图添加另一个,但它不起作用。你能帮忙吗?
猜你喜欢
  • 2022-07-15
  • 1970-01-01
  • 1970-01-01
  • 2016-04-02
  • 2018-09-21
  • 1970-01-01
  • 2019-07-05
  • 2012-10-04
  • 1970-01-01
相关资源
最近更新 更多