1

I am creating a Slider with horizontal Scrolling effect But I stuck at a point how Can I make the slider scroll infinitely Like in my code you can see After Item 6 it stops Scrolling and I have to scroll backward but I want it like after Item 6, Item 1 will come again Something like this https://durimel.io/nel Here you can see the scrolling is infinite?

So can anyone help in this?

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>
Shayan Kanwal
  • 542
  • 4
  • 15
  • Does this answer your question? [How to check if element is visible after scrolling?](https://stackoverflow.com/questions/487073/how-to-check-if-element-is-visible-after-scrolling) – Christopher Nov 21 '21 at 20:15
  • @Christopher Thanks for your Reply But it is not the answer of my question – Shayan Kanwal Nov 21 '21 at 20:17
  • With the observer you can detect the items which are currently visible and prepend/append the items to the scrollParent that are not visible in order to provide the feeling of infinite scrollin. – Christopher Nov 21 '21 at 20:22
  • @Christopher Can you please write it in an answer form like a code snippet Please So I can understand better – Shayan Kanwal Nov 21 '21 at 20:25

2 Answers2

4

The method the example (https://durimel.io/nel) uses differs and is not really infinite. It is limited to the max values the css properties left and transform: translate3d() support. But its enough for a normal use.

It changes the position of each box as soon as it is out of view depending on the direction and moving it behind the "last" or before the "first" using transform: translate3d() and left: ....

Overall here is version of the method i mentioned. I recommend testing it in on jsfiddle or run the snippet in "Full Page"-View because of the mouse-wheel-scrolling behavior from unscrollable iframe-childs and a scrollable parents can't be prevented.

Update:

  • Added a simple speed detection routine to allow faster/slower scrolling.
  • Also fixed the selector for the observer at the end of the js part

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>
Christopher
  • 3,124
  • 2
  • 12
  • 29
  • I am using your solution, its good but why it does not work when I add transition effects through css. I want smooth transition effects, but it behave odd. Can you please guide in this matter? Thanks – Zubair Amin Jan 20 '22 at 03:31
  • 1
    The items getting removed and added to the DOM. This may break some CSS effects. – Christopher Jan 20 '22 at 06:25
  • How to achieve css effects should not break, Is there any solution you recommend. – Zubair Amin Jan 20 '22 at 16:34
  • 1
    You can make use of the [Element.animate](https://developer.mozilla.org/en-US/docs/Web/API/Element/animate) function after insert. Right after the adjust() call or while moving. – Christopher Jan 21 '22 at 19:50
  • 1
    Thanks for your help. Let me try this solution. I will update. – Zubair Amin Jan 22 '22 at 04:40
3

A few comments on this solution.

  1. If the container is set to display:flex; justify-content:space-around; then the space between the items will change as you scroll (the more items, the closer packed they are). Changing to justify-content:flex-start; with a fixed width for each .box delivers the best result.

  2. Adding a debounce fn greatly improved (and simplified) the job. At least, the console logs are underwhelming(!).

  3. If you scroll the scroll wheel very fast, you might hit the end of the carousel before it re-populates. To make that easier to see, change the delay from 50 to 500 (milliseconds).

  4. The debounce is saying, "only plunk more boxes into the container when 50ms has elapsed since the last scroll event. You might prefer a throttle function instead, where it will run at most one re-population every 50ms (or as you set the debounceDelay value).

  5. The HTML,Body height needs to be set to a very large number - in this demo it is now set to 30,000. The .container width should be auto, and it (the .container width) will increase every time new items are plonked into the container.

  6. Most important: the $ and $$ are not jQuery. They are pure vanilla javaScript shorthand for document.querySelector() and document.querySelectorAll()

  7. The demo is best viewed full page (link at top right of demo window).

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>

Best viewed "full page" (top right link after clicking Run Code Snippet)

cssyphus
  • 37,875
  • 18
  • 96
  • 111
  • About #5 changing the `scroll` to the `wheel` event and adjust the value depending on the deltaY solves this and would also allow negative values. – Christopher Nov 23 '21 at 02:16
  • **Go for it...!** You have my permission (and encouragement) to take my answer, improve it as you suggest, and submit it as your own. *(If I like your ideas, and it sounds like I will, you will have my upvote -- and I will encourage the OP to choose your revision as the best answer.)* (BTW, I just now looked at your answer... Great job - really great job. A bit more complicated than this one, which might matter to this OP, but way, way better. I am sure future readers will show their ^approbation^ +1) – cssyphus Nov 23 '21 at 14:24
  • 1
    **Anyone looking at this (my) answer, please also look at Christopher's answer.** His is slightly more advanced, but it is better in that (a) it does not continually add more and more into the DOM, and (b) it uses the IntersectionObserver API. At the least, Christopher's answer is worth taking the time to study and to understand. And, frankly, to bookmark and come back to (as I will be doing). – cssyphus Nov 24 '21 at 15:32
  • @cssyphus great solution, but in question there are 3 containers, so how we can scroll 3 different rows, middle one in opposite direction as in question you can see. You have given only 1 container example. I have tried to add another one but it is not working. Can you please help in it? – Zubair Amin Jan 19 '22 at 18:42