7

I am trying to build my own carousel with pure JavaScript.

I'm struggling with picking up the most efficient way to add an infinite carousel option.

For some reasons, every element (photo, generic object) must have an id

The algorithm I see goes like that:

  • You check if the carousel is overflown (the are enough objects to fit the whole container)
  • If not: append to the back a copy of the first element, then a copy of the second element and so on. (But there will be an issue with the ids, because this object will have the same id)

Adding copies - If the user is scrolling to the last object (to right) then append the first DOM object to the array back
- If the user is scrolling to the first object (to left) then add the last DOM child to array front.

Is this going to work? Is there any other efficient way of doing an infinite carousel?

I have also heard that it's better to use translate property rather than changing the left, right properties, so it there would be more work for the GPU than for CPU.

Denis Rozimovschii
  • 428
  • 1
  • 6
  • 19
  • 1
    I guess you should rather try circular approach. This will ensure your not flooding DOM – Rajesh Sep 24 '16 at 07:19
  • Could you please elaborate? – Denis Rozimovschii Sep 24 '16 at 07:20
  • 1
    Consider you have just 3 images. On click of next button fetch an element after it. If its the last element, pick first and animate it in a way its next. Do same for previous element. – Rajesh Sep 24 '16 at 07:23
  • Great idea! So I don't actually change the DOM, but only move the things around. That's great. Still, the question remains for the copied elements. What if the carousel isn't overflown? Should I just move from id's to classes? – Denis Rozimovschii Sep 24 '16 at 07:25
  • Well that's not true. Try to use GSAP tweenmax if you want to have a performance-based animations. Check it out. – hdotluna Sep 24 '16 at 07:26
  • The main idea was to try to build it without any additional libraries. That's why I'm asking for an efficient way of doing it. Also, why don't you submit it as an answer? – Denis Rozimovschii Sep 24 '16 at 07:29
  • @DenisRozimovschii sample code for circular approach. [Sample Fiddle](https://jsfiddle.net/RajeshDixit/zrLu9han/). And yes if you switch to classes, it would be simpler.' – Rajesh Sep 24 '16 at 07:31

2 Answers2

12

I created a simple slider with css transformations as the animation technique and plain Javascript.

var img = document.getElementsByClassName("img")[0]; 
img.style.transform = 'translate('+value+'px)';

You can test it in this codepen snippet. http://codepen.io/TobiObeck/pen/QKpaBr

A press on a button translates all images in the respective direction along the x-axis. An image on the edge, is set transparent outerImg.style.opacity = '0'; and translated to the other side. You can add or remove image elements in HTML and it still works.

In this second codepen snippet you can see how it works. The opacity is set to 0.5 so it is observable which image switches the side. Because overflow: hidden is removed, you can see how the images on the edge enqueue on the other side. http://codepen.io/TobiObeck/pen/WGpdLE

Moreover it is notworthy that it is checked wether the animation is complete, otherwise the simultaneously added translations would look odd. Therefore a click won't trigger another animation until unless the animation is completed.

img.addEventListener("transitionend", transitionCompleted, true);

var transitionCompleted = function(){
    translationComplete = true;
}

leftBtnCLicked(){
    if(translationComplete === true){
       //doAnimation
    }
}
Tobi Obeck
  • 1,918
  • 1
  • 19
  • 31
  • Okay, thank you. So you approach is to translate them. Pretty much like @Rajesh suggested. Is this the most efficient way? – Denis Rozimovschii Sep 24 '16 at 20:14
  • 2
    I think performance shouldn't be an issue unless you put hundreds or thousands of high resolution pictures in the carousel. There is only one animation/translation running at a time, even if clicked multiple times. But every img is moved per click. I read about left vs. translate() [link](http://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/) So it should be ok. Another approach would be to remove and insert an img as a first or last element in the DOM. This would work, too, because they have float: left; property – Tobi Obeck Sep 24 '16 at 21:58
  • The intention is good, but codes are unfortunately not really maintainable and reusable. – Frontend employee Feb 15 '18 at 14:14
0

you can use this code to manipulate slides. This basically rotates the array back and front

        <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style> 

    body {
    width: 100%;
    height: 100%;
    }

    .parentDiv {
    height: 30%;
    width: 100%;
    display: flex;
    }

    </style>
    <title>test</title>
    </head>
    <body>
    <button class="fwd"> Fwd! </button>
    <button class="bkwd"> Bkwd! </button>
    <script type="text/javascript">
    const arr = ['red', 'blue', 'coral', 'green', 'yellow'];
    let narr = ['red', 'blue', 'coral'];
    


    const parentDiv = document.createElement('div');
    parentDiv.setAttribute('class', 'parentDiv');
    document.body.insertAdjacentElement('afterbegin', parentDiv);


    window.onload = ()=> {
    narr.forEach(color => {
    while(parentDiv.children.length < narr.length){
    const childDiv = document.createElement('div');
    parentDiv.appendChild(childDiv);
    };
    });

    Array.from(parentDiv.children).forEach((child, index) => {
    child.style.border = '1px #000 dotted';
    child.style.minWidth = '20%';
    child.style.minHeight = '20vh';
    child.style.backgroundColor = narr[index]
    });
    };



    document.querySelector('.fwd').addEventListener('click', ()=>{
    narr.shift();

    if(narr[narr.length-1] === arr[arr.length-1]){
        narr.push(arr[0])
    } else {
    narr.push(arr[arr.indexOf(narr[narr.length-1])+1])
    }

    narr.forEach(color => {
    while(parentDiv.children.length < narr.length){
    const childDiv = document.createElement('div');
    parentDiv.appendChild(childDiv);
    };
    });

    Array.from(parentDiv.children).forEach((child, index) => {
    child.style.border = '1px #000 dotted';
    child.style.minWidth = '20%';
    child.style.minHeight = '20vh';
    child.style.backgroundColor = narr[index];
    
    
    });


    })


    document.querySelector('.bkwd').addEventListener('click', ()=>{
    narr.pop();
    if(narr[0] === arr[0]){
        narr.unshift(arr[arr.length-1])
    } else {
    narr.unshift(arr[arr.indexOf(narr[0])-1])
    }

    

    narr.forEach(color => {
    while(parentDiv.children.length < narr.length){
    const childDiv = document.createElement('div');
    parentDiv.appendChild(childDiv);
    };
    });

    Array.from(parentDiv.children).forEach((child, index) => {
    child.style.border = '1px #000 dotted';
    child.style.minWidth = '20%';
    child.style.minHeight = '20vh';
    child.style.backgroundColor = narr[index]
    });
    })

    </script>   
    </body>
    </html>