The thing is that two days ago, a student asked me the principle of seamless rolling round broadcast graph, said that usually used to frame, to their hands when not. At that time, I was very angry when I heard it. Look, you are used to it. When I was in preschool, the wheel map was the first effect to get started. At that time, there were only JQ, and the wheel map TAB was the basic skill. Can’t write code without framework? API oriented programming?

Go ahead. Turn it on. What else are we gonna do

Let’s see what seamless scrolling is:

It looks like there are only four pictures, but whether you drag them forward or backward, you can drag them indefinitely. It feels like four pictures in an infinite loop. This effect is one of the most common interactive effects on the page, and it has been 1202 years

The basic principle of implementation is very simple

The first step is to make a copy of the picture, four to eight

If you have a larger number of images, you can do something else. A few images don’t matter

If it’s dozens of images, just create a 3D circle. Again, the most important thing is to have a solution.

This picture to understand this: at the instant of the mouse click, first take a look at what is the picture of the current display, if it is 0, then press the moment the whole position adjustment to the second set to 0, the second group is copied out of the group, so whether it’s dragging left or right drag are no problem

Then, if the current display is the last picture of the second group, immediately after the mouse is pressed, directly adjust the overall position to the last picture of the first group, so that you can still drag left and right, look at the schematic diagram:

Cover the other images and focus only on the one in the middle, and you can see the scroll seamlessly:

If you want to look at the code, go on and look at the code, the amount of code is actually very small, with only a hundred lines of CSS together, so it’s not complicated at all, the focus is on the principle.

Here is the structure

<div class="wrap">
    <ul class="list"><! -- Here is the list of pictures -->
        <li class="item"><img src="img/img01.png"></li>
        <li class="item"><img src="img/img02.png"></li>
        <li class="item"><img src="img/img03.png"></li>
        <li class="item"><img src="img/img04.png"></li>
    </ul>
    <ul class="dot"><! -- Here's the little dot at the bottom -->
        <li class="active"></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
</div>
Copy the code

As you can see, there are only four images in the original list. After retrieving the list and copying the contents, you can get eight images.

The second step is to get the element and set the variable

  let wrap = document.querySelector('.wrap');// Get the outer box to set the width of the picture scroll
  let list = wrap.querySelector('.list');// List of images
  let dot = wrap.querySelectorAll('.dot li');/ / dot
  
  let startpoint = {};// Start point of mouse press
  let distaince = {};// The distance moved
  
  list.innerHTML += list.innerHTML;// Copy the list of images
Copy the code

There’s really nothing to say here, writing interactions in native, is just getting elements and then adding event elements, so you know what to do, in this case I’m writing the mobile version, so I’m using touch events, right

Third, add events

  wrap.addEventListener('touchstart'.(ev) = >{// Press your finger down
      let touch = ev.changedTouches[0];
      startpoint = {// Record the position of the finger press
          x:touch.pageX,
          y:touch.pageY
      }
  })
  // Just record the position of the finger, let the image drag first, and then worry about seamless scrolling
  
  wrap.addEventListener('touchmove'.(ev) = >{// Move your finger
      let touch = ev.changedTouches[0];
      distaince = {// Calculate how far you moved as you moved
          x:touch.pageX - startpoint.x,
          y:touch.pageY - startpoint.y
      }

      translatex = startOffset + distaince.x;
      list.style.transform = `translateX(${translatex}px)`;// Append the calculated distance to the list of images
  })
  // At this point, the image is ready to be dragged
Copy the code

So now that you’ve got the image that can be dragged, decide when to switch to the next one.

Here, if you don’t do too much research, you can do it in your head, but if you want to focus on user experience, follow my advice and use a scale. For example, when the picture is dragged more than 30% of the outer frame, releasing it will automatically switch to the next one or the previous one, so we need a variable to control the ratio so that we can adjust it again and again

So, when the finger is up, you should make a series of judgments:

  let now = 0;// The current one
  let imgW = wrap.offsetWidth;// Get the width of the outer box
  let proportion = 0.3;// Jump to the next or previous image when dragging a scale that exceeds the overall width
  
  wrap.addEventListener('touchend'.() = >{// Raise your finger
      if(Math.abs(distaince.x) > imgW * proportion){// When to switch
          now -= distaince.x/Math.abs(distaince.x);// Check whether to switch to the previous or the next slide
      }

      translatex = now * -imgW; // Calculate how far the list of images needs to move
      list.style.transition = 's 0.3';
      list.style.transform = `translateX(${translatex}px)`;
  })
Copy the code

At this point, we can already drag images, and then switch from one image to the next. However, if you want to be seamless, you still need to operate further. It is said in the principle analysis that you can look at this principle analysis diagram again:

As you can see, when you click down, if the image is in the first image, then you jump to the first image in the second group, so whether you drag to the left or to the right, you still have the image. Look at the code:

wrap.addEventListener('touchstart'.(ev) = >{
        let touch = ev.changedTouches[0];
        startpoint = {
            x:touch.pageX,
            y:touch.pageY
        }

        if(now == 0) {// If the current is the 0th card, jump to the 0th card of the second group
            now = dot.length;
        }else if(now == dot.length*2 -1) {// If it is the last card, jump to the last card of the first group
            now = dot.length -1;
        }

        translatex = now * -imgW;// Then move the list of images
        startOffset = translatex;
        list.style.transition = 'none';
        list.style.transform = `translateX(${translatex}px)`;
    })
Copy the code

At this point, there is no problem with seamless scrolling. Let’s look at the complete code:

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> body{ margin: 0; background: #fefefe; } .wrap{ width: 100vw; overflow: hidden; Box-shadow: 0 0 5px 0 rgba(0,0,0,0.1); position: relative; background: #000; } ul{ margin: 0; padding: 0; list-style: none; } .list{ display: flex; float: left; } .list img{ width: 100vw; vertical-align: top; filter: saturate(2); } .dot{ width: 100vw; display: flex; justify-content: center; position: absolute; bottom: 20px; } .dot li{ width: 10px; height: 10px; border-radius: 10px; background: #fff; margin: 0 5px; The transition: 0.2 s; Box-shadow: 0 0 3px 0 rgba(0,0,0,0.3); } .dot li.active{ width: 20px; } </style> </head> <body> <div class="wrap"> <ul class="list"><! - here is a list image - > < li class = "item" > < img SRC = "img/img01. PNG" > < / li > < li class = "item" > < img SRC = "img/img02. PNG" > < / li > < li class="item"><img src="img/img03.png"></li> <li class="item"><img src="img/img04.png"></li> </ul> <ul class="dot"><! - here is the dot - > < li class = "active" > < / li > < li > < / li > < li > < / li > < li > < / li > < / ul > < / div > < script > {let wrap = document.querySelector('.wrap'); Let list = wrap.querySelector('.list'); // let dot = wrap. QuerySelectorAll ('.dot li'); // let startPoint = {}; // let distaince = {}; // let startOffset = 0; // Record the position of the image list when mouse down let translatex = 0; // let now = 0; // let imgW = wrap.offsetwidth; // Let proportion = 0.3; // Let isMove = false; list.innerHTML += list.innerHTML; // Wrap. AddEventListener (' Touches Start ',(ev)=>{let touch = ev.changedtouches [0]; startpoint = { x:touch.pageX, y:touch.pageY } if(now == 0){ now = dot.length; }else if(now == dot.length*2 -1){ now = dot.length -1; } translatex = now * -imgW; startOffset = translatex; list.style.transition = 'none'; list.style.transform = `translateX(${translatex}px)`; }) wrap.addEventListener('touchmove',(ev)=>{ let touch = ev.changedTouches[0]; distaince = { x:touch.pageX - startpoint.x, y:touch.pageY - startpoint.y } if(Math.abs(distaince.x) - Math.abs(distaince.y) > 5){ isMove = true; ev.preventDefault(); }else if(Math.abs(distaince.x) - Math.abs(distaince.y) < 5){ isMove = false; } if(isMove){ translatex = startOffset + distaince.x; list.style.transform = `translateX(${translatex}px)`; } }) wrap.addEventListener('touchend',()=>{ if(Math.abs(distaince.x) > imgW * proportion){ now -= distaince.x/Math.abs(distaince.x); } Array.from(dot).forEach((item,index)=>{ item.classList.remove('active'); if(index === (now%dot.length)){ item.classList.add('active'); }}); if(isMove){ translatex = now * -imgW; List. Style. The transition = '0.3 s; list.style.transform = `translateX(${translatex}px)`; } }) } </script> </body> </html>Copy the code

The icing on the cake

This is probably the end of the code, but I would like to add a click event to the small button to jump to the corresponding image:

  Array.from(dot).forEach((item,index) = >{
      item.onclick = function(ev){
          Array.from(dot).forEach((item) = >{
              item.classList.remove('active');
          })

          now = index;
          this.classList.add('active');
          translatex = now * -imgW;
          list.style.transition = 's 0.3';
          list.style.transform = `translateX(${translatex}px)`; ev.preventDefault(); }})Copy the code

There shouldn’t be much to say about this code, but then something amazing happens:

Got it? The principles are universal, just like it… If you want to know what I said about the 3D round screen slideshow from the beginning, see the comments section…


  • This is our open source project element3 from the Flower mountain team welcome to try and star
  • A front-end component library that supports VUE3