如何使用Javascript创建无限旋转木马?

5
我想在Javascript中制作一个无限旋转木马。当元素离开屏幕时,我会将它们克隆并将它们粘贴到列表的末尾。但是一分钟后HTML布局中有很多克隆元素。我决定在克隆后删除该元素。但是所有元素都会立即发生偏移。如何避免这种情况?或者也许有另一种算法来实现这个无限旋转木马?
这里是代码:https://codepen.io/alessandro-kex/pen/abGWNEK

window.addEventListener("load", function () {
  const slideContainer = document.querySelector(".carousel");
  const slidesWrapper = document.querySelector(".carousel-slides");
  let slides = document.querySelectorAll(".carousel-slide");
  let index = 0;
  const interval = 1500;
  let moveDistance = 0;
  const paddingRight = 50;
  let lastSlideIndex = slides.length - 1;
  let firstClone;

  const startSlide = (index) => {
    this.setInterval(() => {
      moveDistance = moveDistance + slides[index].clientWidth + paddingRight;
      slidesWrapper.style.transform = `translateX(${-moveDistance}px)`;
      slidesWrapper.style.transition = "1s";

      firstClone = slides[index].cloneNode(true);
      firstClone.id = `first-clone-${index}`;
      slidesWrapper.append(firstClone);
      /*If uncomment it - then the problem starts */
      //slides[index].remove();

      index++;
    }, interval);
  };
  startSlide(index);
});
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.carousel {
  margin: 0 auto;
  width: 100%;
  position: relative;
}

.carousel-slides {
  display: flex;
  list-style: none;
  gap: 50px;
  flex-shrink: 1;
}

.carousel-slide {
  position: relative;
  min-width: 0;
  flex-shrink: 0;
}

.carousel-slide {
  height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
}

.carousel-item-text {
  white-space: nowrap;
}

.carousel-item-img {
  width: 100px;
  height: auto;
}

.round {
  padding: 30px 50px;
  border-radius: 120px;
}

.light-blue {
  background-color: #d8f1ff;
}

.pink {
  background-color: #ffeaf0;
}

.purpule {
  background-color: #eae9ff;
}
  <div class="carousel">
    <ul class="carousel-slides">
      <li class="carousel-slide" data-number="0">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide light-blue round" data-number="1">
        <span class="carousel-item-text">11111111 1111 <br> 111111111</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide pink round">
        <span class="carousel-item-text">22222 22222 <br> 222222222 2222222222 222222222</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide light-blue round">
        <span class="carousel-item-text">333 333333333 <br> 333333333333</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide purpule round">
        <span class="carousel-item-text">4444 444444444444<br> 44444</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide  pink round">
        <span class="carousel-item-text">55555555 55555<br> 55 5555</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide pink round">
        <span class="carousel-item-text">6666666 6666666666 <br> 66666 6666666 66666</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide  light-blue round">
        <span class="carousel-item-text">777777 777 777<br> 77 77777</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide purpule round" data-number="16">
        <span class="carousel-item-text">888 8888<br> 88888888</span>
      </li>
    </ul>
  </div>


你能只使用2个项目制作一个水平旋转木马吗?当第一个项目到达左侧的某个位置时,右侧会跟随移动。这只是一个想法。 - Azu
你的意思是一个幻灯片(一个单独的页面),还是包含多个幻灯片的容器? - Main Admin
3个回答

6
我的想法是纯CSS实现。有两个容器,一个是红色的,另一个是蓝色的,它们重叠在一起。这是我的草稿:image 我使用了a=700px,这是容器的宽度,以及w=500px,这是绿色包装的宽度。希望这能帮助到某些人 :)

.wrapper {
        width: 500px;
        overflow-x: hidden;
        border: 2px solid black;
        position: relative;
        height: 100px;
    }
    
    .first {
        width:700px;
        height: 100px;
        background-color: red;
        position: absolute;
        left:0px;
        top:0;
        animation: firstDiv 10s linear infinite;
        
    }
    @keyframes firstDiv {
      0 {left: 0px;}
      15% {left: -200px;}
      50% {left: -700px;}
      65.9% {left: -900px;}
      66% {left: 500px;}
      99.9% {left: 0px;}
      100% {left: 0px;}
    }
    
    .second {
        width:700px;
        height: 100px;
        background-color: blue;
        position: absolute;
        left:700px;
        top:0;
        animation: secondDiv 10s linear infinite;
    }
    @keyframes secondDiv {
      0 {left: 700px;}
      15% {left: 500px;}
      50% {left: 0px;}
      65.9% {left: -200px;}
      66% {left: -200px;}
      99.9% {left: -700px;}
      100% {left: 700px;}
    }
<div class="wrapper">
        <div class="first">first container first container first container first container first container first container first container first container</div>
        <div class="second">second container second container second container second container second container second container second container</div>
     
    </div>


谢谢你的回答!我本来想用动画来做滑块,但是在动画期间滑块之间出现了空白。不过我会尝试将你的代码适应到我的滑块上,作为另一种未来的选择! - Main Admin

2

对于这种事情,你应该熟悉Promise

否则,这个想法是正确的:
1 - 使用过渡时间进行translateX
2 - 一旦过渡完成,删除过渡时间
3 - 将第一个元素放到末尾,并将translateX重置为零
4 - 再次开始...

完整代码:

 
(async ()=>   // async IIFE code for slider.
  {
  const
    interval       = 1500  // ms
  , paddingRight   = 50
  , slideContainer = document.querySelector('.carousel') 
  , slidesWrapper  = document.querySelector('.carousel-slides')
  , slides         = document.querySelectorAll('.carousel-slides > li')
  , delay          = ms => new Promise(r => setTimeout(r, ms))
  , movLeft = (el, mov) => new Promise(r =>
    {
    el.ontransitionend =_=>
      {
      el.ontransitionend = null
      el.style.transition = 'none';
      r()
      }
    el.style.transition = '1s';
    el.style.transform  = `translateX(${-mov}px)`;
    });

  let index = 0;

  while (true) // infinite carrousel loop
    {
    await delay( interval )
    await movLeft( slidesWrapper, slides[index].clientWidth + paddingRight  )

    slidesWrapper.appendChild( slides[index] )  // mov first slide to the end
    slidesWrapper.style.transform    = `translateX(0)` // rest translateX
    index = ++index % slides.length
    }
  })()
* {
  margin           : 0;
  padding          : 0;
  box-sizing       : border-box;
  }
.carousel {
  margin           : 0 auto;
  width            : 100%;
  position         : relative;
  }
.carousel-slides {
  display          : flex;
  list-style       : none;
  gap              : 50px;
  flex-shrink      : 1;
  }
.carousel-slide {
  position         : relative;
  min-width        : 0;
  flex-shrink      : 0;
  height           : 100px;
  display          : flex;
  align-items      : center;
  justify-content  : center;
  min-width        : 0;
  }
.carousel-item-text {
  white-space      : nowrap;
  }
.carousel-item-img {
  width            : 100px;
  height           : auto;
  }
.round {
  padding          : 30px 50px;
  border-radius    : 120px;
  }
.light-blue {
  background-color : #d8f1ff;
  }
.pink {
  background-color : #ffeaf0;
  }
.purpule {
  background-color : #eae9ff;
  }
<div class="carousel">
  <ul class="carousel-slides">
    <li class="carousel-slide" data-number="0">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide light-blue round" data-number="1">
      <span class="carousel-item-text">11111111 1111 <br> 111111111</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide pink round">
      <span class="carousel-item-text">22222 22222 <br> 222222222 2222222222 222222222</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide light-blue round">
      <span class="carousel-item-text">333 333333333 <br> 333333333333</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide purpule round">
      <span class="carousel-item-text">4444 444444444444<br> 44444</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide  pink round">
      <span class="carousel-item-text">55555555 55555<br> 55 5555</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide pink round">
      <span class="carousel-item-text">6666666 6666666666 <br> 66666 6666666 66666</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide  light-blue round">
      <span class="carousel-item-text">777777 777 777<br> 77 77777</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide purpule round" data-number="16">
      <span class="carousel-item-text">888 8888<br> 88888888</span>
    </li>
  </ul>
</div>

多个轮播图的新版本... (2022/09/20)

'use strict';

class carouselClass
  {
  static #interval     = 1500;
  static #paddingRight = 50;
  #slidesWrapper;
  #slides;
  #Li_index;
  #onLoop;

  constructor(UL_Carousel) 
    {
    this.#slidesWrapper = UL_Carousel; 
    this.#slides        = UL_Carousel.querySelectorAll(`li`);
    this.#Li_index      = 0;
    this.#onLoop        = false;
    }
  #delay( ms ) 
    {
    return new Promise(resolve => setTimeout(resolve, ms));
    }
  #movLeft( mov, indx )
    {
    return new Promise(resolve =>
      {
      this.#slidesWrapper.ontransitionend =()=>
        {
        this.#slidesWrapper.ontransitionend  = null;
        this.#slidesWrapper.style = '';
        this.#slidesWrapper.appendChild( this.#slides[this.#Li_index] ); // mov slide to the end
        this.#Li_index  = ++this.#Li_index % this.#slides.length;       // next Li element index
        resolve();
        }
      this.#slidesWrapper.style.transition = '1s';
      this.#slidesWrapper.style.transform  = `translateX(${mov}px)`;
      });
    }
  async run() 
    {
    this.#onLoop              = true;
    this.#slidesWrapper.style = '';

    while( this.#onLoop )  // infinite carrousel loop
      {
      await this.#delay( carouselClass.#interval );
      await this.#movLeft( -(this.#slides[this.#Li_index].clientWidth + carouselClass.#paddingRight) );
      } 
    }
  stop()
    {
    this.#onLoop = false;
    }
  }

const carousels = [...document.querySelectorAll('.carousel')]
  .map( cBox =>
  ({ box : cBox
   , cls : new carouselClass( cBox.querySelector('ul.carousel-slides') )
  }))

carousels.index = -1;

carousels.animateNext =_=> 
  {
  carousels.index = ++carousels.index % carousels.length;
  carousels.forEach((crl,i)=> crl.box.classList.toggle('noDisplay', carousels.index !== i) );
  carousels[carousels.index].cls.run();
  }
carousels.animateNext();  // first attempt...

bt_switch.onclick =_=>
  {
  carousels[carousels.index].cls.stop();  // stop infinite loop on current carousel
  carousels.animateNext();
  }
* {
  margin     : 0;
  padding    : 0;
  box-sizing : border-box;
  }
.carousel {
  margin   : 0 auto;
  width    : 100%;
  position : relative;
  overflow : hidden;
  }
.carousel-slides {
  display     : flex;
  list-style  : none;
  gap         : 50px;
  flex-shrink : 1;
  }
.carousel-slides img {
  height: 100px;
  }
.carousel-slides span {
  display        : inline-block;
  white-space    : nowrap;
  padding        : 30px 50px;
  border-radius  : 120px;
  }
.light-blue { background-color : #d8f1ff; }
.pink       { background-color : #ffeaf0; }
.purpule    { background-color : #eae9ff; }

.noDisplay { display : none; }
<div class="carousel">
    <ul class="carousel-slides">
      <li> <img src="https://picsum.photos/150/150?random=1"> </li>
      <li> <span class="light-blue">11111111 1111 <br> 111111111</span></li>
      <li> <img src="https://picsum.photos/150/150?random=2"> </li>
      <li> <span class="pink">2222222 2222 222 <br> 22</span></li>
      <li> <img src="https://picsum.photos/150/150?random=3"> </li>
      <li> <span class="light-blue">33 33 <br> 333 33 33 33 333 33 33 33</span></li>
      <li> <img src="https://picsum.photos/150/150?random=4"> </li>
      <li> <span class="purpule"> 444444 444 44 4<br> 44 44 44 4444  </span></li>
      <li> <img src="https://picsum.photos/150/150?random=5"> </li>
      <li> <span class="pink"> 555555 55 <br>555 555 </span></li>
      <li> <img src="https://picsum.photos/150/150?random=6"> </li>
      <li> <span class="pink"> 66666666666666666666 <br> 6 </span></li>
      <li> <img src="https://picsum.photos/150/150?random=7"> </li>
      <li> <span class="light-blue"> 77777777 777777777  777777<br> 77777777777777777 77777777777777777</span></li>
      <li> <img src="https://picsum.photos/150/150?random=8"> </li>
      <li> <span class="purpule"> 88 888 <br>  8 8 8 8 8</span></li>
    </ul>
  </div>

  <div class="carousel">
    <ul class="carousel-slides">
      <li> <img src="https://picsum.photos/150/150?random=9"> </li>
      <li> <span class="light-blue">aaa <br> 11</span></li>
      <li> <img src="https://picsum.photos/150/150?random=10"> </li>
      <li> <span class="pink">bbb bbb <br> 22</span></li>
      <li> <img src="https://picsum.photos/150/150?random=11"> </li>
      <li> <span class="light-blue">ccc <br> 3</span></li>
      <li> <img src="https://picsum.photos/150/150?random=12"> </li>
      <li> <span class="purpule"> ddd ddd <br> 4  </span></li>
      <li> <img src="https://picsum.photos/150/150?random=13"> </li>
      <li> <span class="pink"> eee eee eee <br> 5 </span></li>
      <li> <img src="https://picsum.photos/150/150?random=14"> </li>
      <li> <span class="pink"> ff ff ff <br> 6 </span></li>
      <li> <img src="https://picsum.photos/150/150?random=15"> </li>
      <li> <span class="light-blue"> g<br> g gg g gg g gg</span></li>
    </ul>
  </div>

  <br><br>
  <button id="bt_switch">___ carousel switcher ___</button>


它可用了!非常感谢!我已经为这个问题奋斗了三天!你真是一个伟大的开发者!谢谢! - Main Admin
不,不是这样的!只是太晚了,我关掉了电脑却忘记做那件事了! - Main Admin
你能再帮我一次吗?我使用这个滑块,但是用按钮,并且我为此编写了代码。我需要在页面上插入两个滑块,当我们使用移动设备时,第一个滑块的display=none。所以问题是,当我使用第二个滑块时,我在queryselector中得到第一个滑块的按钮。我认为我必须使用类并进行初始化。你能否请大致解释一下如何解决这个问题?我如何同时使用两个滑块? - Main Admin
非常感谢!我明白了这个想法。但是,如果我没有机会为滑块编写不同的ID呢?我必须将所有滑块都放入数组中吗?! - Main Admin
@MainAdmin 我使用了HTML的id="..."属性作为示例,重要的是能够在每个滑块上有一个指针;根据你的HTML进行适应是由你决定的。 - Mister Jojo

1
你应该创建一个轮播图,并使用JavaScript将所有数据存储在数组中。然后默认情况下,在加载时显示数组中的第一个元素。当用户单击查看轮播图上的下一项或某个幻灯片的时间到期时,只需调用函数更改幻灯片。为此,初始化一个计数器以跟踪当前查看的幻灯片索引,并在幻灯片更改时更新它。当用户希望在最后一张幻灯片上查看下一张幻灯片时,请将计数器设置为0。类似地,当用户希望在第一张幻灯片上查看上一张幻灯片时,请将计数器设置为n-1,其中n是您提供的幻灯片数量。

是的,这是一个基本滑块的标准情况。但我有某种跑马灯元素。而且我的滑块放置在整个屏幕的宽度上。如果我按照你说的做,我的左侧元素会快速跳到开头,这将很明显。 - Main Admin
我觉得我刚开始理解如何做到这一点。但是我无法获取我的滑块容器的宽度。它超出了屏幕,我尝试使用clientWidth,但只得到屏幕宽度!我该如何获取宽度? - Main Admin

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