原版 javascript & css 图像滑块无法正常工作

vanilla javascript & css image slider not working properly

我使用一些 javascript 和 css 创建了一个包含许多图像的图像滑块。我只是使用客户端宽度来获取图像的大小(略有不同)并使用计数器变量计算 translateX 距离。最后添加了一个 css 转换。但是我似乎无法让滑块正确翻译整个图像。我不知道为什么会出错。我在计算响应度时使用了 'vw'。我是 javascript 的新手,也希望对代码的其他部分的其他部分有任何提示。

这里是JS fiddle link- https://jsfiddle.net/n6smpv2j/15/

HTML

<div id="lookbook" data-tab-content class="black-text">
            <div class="lookbook-nav">
               <button id="left">←</button>
               <button id="right">→</button>
            </div>
            <div class="lookbook">
               <div class="slider">
                  <img src="https://loremflickr.com/640/360" id="lastClone" alt="">
                  <img src="https://picsum.photos/640/400">
                  <img src="https://loremflickr.com/640/360">
                  <img src="https://picsum.photos/640/400">
                  <img src="https://loremflickr.com/640/360">
                  <img src="https://picsum.photos/640/400">
                  <img src="https://loremflickr.com/640/360">
                  <img src="https://picsum.photos/600/400">
                  <img src="https://fillmurray.com/600/330">
                  <img src="https://picsum.photos/600/400">
                  <img src="https://fillmurray.com/600/330">
                  <img src="https://picsum.photos/600/400">
                  <img src="https://loremflickr.com/640/360">
                  <img src="https://picsum.photos/600/400">
                  <img src="https://loremflickr.com/640/360">
                  <img src="https://picsum.photos/600/400" id="firstClone" alt="">
               </div>
            </div>
         </div>

JS

const slider = document.querySelector('.slider');
const sliderImages = document.querySelectorAll('.slider img');
const leftbtn = document.querySelector('#left');
const rightbtn = document.querySelector('#right');
let counter = 1;
const size = sliderImages[0].clientWidth;
slider.style.transform = 'translateX(' + (-size * counter) + 'vw)';

rightbtn.addEventListener('click', () => {
   if (counter >= sliderImages.length - 1) return;
   slider.style.transition = "transform 0.4s ease-in";
   counter++;
   slider.style.transform = 'translateX(' + (-size * counter) + 'vw)'
})

leftbtn.addEventListener('click', () => {
   if (counter <= 0) return;
   slider.style.transition = "transform 0.4s ease-in";
   counter--;
   slider.style.transform = 'translateX(' + (-size * counter) + 'vw)'
})

slider.addEventListener('transitionend', () => {
   if (sliderImages[counter].id === "lastClone") {
      slider.style.transition = "none";
      counter = sliderImages.length - 2;
      slider.style.transform = 'translateX(' + (-size * counter) + 'vw)'
   }
   if (sliderImages[counter].id === "firstClone") {
      slider.style.transition = "none";
      counter = sliderImages.length - counter;
      slider.style.transform = 'translateX(' + (-size * counter) + 'vw)'
   }
})

CSS

#lookbook {
   width: 100vw;
   height: 100vh;
}

.lookbook-nav {
   width: 70vw;
   height: 10vh;
   margin-left: 15vw;
   margin-top: 45vh;
   position: absolute;
   display: flex;
   justify-content: space-between;
   align-items: center;
}

button {
   border: none;
   outline: none;
   background: transparent;
   font-size: 2rem;
   /* font-weight: bold; */
   cursor: pointer;
}

.lookbook-nav button {
   border: none;
   outline: none;
   background: transparent;
   font-size: 2rem;
   /* font-weight: bold; */
   cursor: pointer;
}

button:hover {
   opacity: 0.4;
}

.lookbook {
   width: 56vw;
   height: 91vh;
   margin: auto;
   overflow: hidden;
}

.lookbook img {
   width: 100%;
   height: auto !important;
}

.slider {
   margin-top: 10vh;
   display: flex;
   width: auto;
   
}

出于某种原因,从该来源加载的图像无法正常工作,所以我将它们下载到本地并且它们确实有效,我也对您的 CSS 进行了一些修改。

var slider = document.getElementById("slider");
var slides = slider.childElementCount;
var i = 0;
document.getElementById("right").addEventListener("click", function () {
  i == slides - 1 ? (i = 0) : i++;
  slider.style.transform = "translate(-" + 600 * i + "px)";
});
      body {
        background-color: aqua;
      }
      #lookbook {
        position: relative;
        box-sizing: content-box;
        height: auto;
        max-width: 600px;
        margin: auto;
      }

      .lookbook-nav {
        position: absolute;
        display: flex;
        justify-content: space-between;
        width: 100%;
        height: 100%;
      }

      button {
        border: none;
        outline: none;
        background: transparent;
        font-size: 2rem;
        cursor: pointer;
      }

      .lookbook-nav button {
        border: none;
        outline: none;
        background: transparent;
        font-size: 2rem;
        /* font-weight: bold; */
        cursor: pointer;
        color: beige;
        z-index: 2;
      }

      button:hover {
        opacity: 0.4;
      }

      .lookbook {
        width: auto;
        height: 91vh;
        margin: auto;
        overflow: hidden;
      }

      .lookbook img {
        width: 600px;
        height: auto !important;
      }

      .slider {
        margin-top: 10vh;
        display: flex;
        /* align-items: flex-end; */
        width: auto;
        /* height: 700px; */
        transition: 0.5s ease-in-out;
      }
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Slider</title>
  </head>
  <body>
    <div id="lookbook" data-tab-content class="black-text">
      <div class="lookbook-nav">
        <button id="left">←</button>
        <button id="right">→</button>
      </div>
      <div class="lookbook">
        <div class="slider" id="slider">
          <img src="https://picsum.photos/600/360" alt="" />
          <img src="https://picsum.photos/600/360" alt="" />
          <img src="https://picsum.photos/600/360" alt="" />
          <img src="https://picsum.photos/600/360" alt="" />
          <img src="https://picsum.photos/600/360" alt="" />
          <img src="https://picsum.photos/600/360" alt="" />
        </div>
      </div>
    </div>
  </body>
</html>

I just made one navigation arrow work but should be the same thing just in reverse order also you don't have to worry about the counter as it will detect how many images you have inside the slider.

这是因为 size 是以像素为单位计算的,如您所见 here。因此,要获得 vw 中的宽度,您可以使用以下函数作为

const size = vw(sliderImages[0].clientWidth);

function vw(v) {
    var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
    return (v * w) / 100;
}

@DecjazMach 的回答解决了最重要的问题,但并未涵盖所有问题。例如,该解决方案还仍然使用第一张图像的宽度来设置可见滑块的宽度。这在很多情况下都很好,但是如果第一张图片是瘦高的肖像而其余图片是风景,反之亦然怎么办?

@Laiqa Mohid 也欢迎任何其他建议,所以这里有一些尝试简化事情的建议,例如最小化 JS 中所需的计算和 'work' 系统必须在单击时执行的操作. 你可以在这里试试 http://bayeuxtapestry.rgspaces.org.uk/slider

备注:

滑块可见部分的大小不依赖于第一张图片的尺寸

imgs 已被替换为 divs + background-image,这样就可以适应不同的 sizes/aspect 比率,而无需 javascript 计算 - 这会自动帮助提高响应速度

这些 div 都具有相同的尺寸,因此滑块需要移动的量不取决于图像的大小

没有填满整个宽度的图片(因为它们相对来说太高了)将被居中

图像也垂直居中。如果需要,可以通过更改 .slider div

中的 background-position 来更改(例如对齐到滑块的顶部)

使用 transform:translateX 可行,但需要在 Javascript 中进行计算。我们可以改用 CSS 动画,只需要移动当前可见的幻灯片和下一张要显示的幻灯片。

图片服务有时不提供图片,所以我使用了自己的 - 故意使用不同的尺寸和纵横比(包括纵向)

使用这种方法可以有一个连续的滑块 - 如果用户点击最后一张幻灯片则显示第一张幻灯片。

代码如下:

<!DOCTYPE html>
<html>
<head>
<title>Slider</title>
<meta charset="utf-8">
<style>
#lookbook {
  width: 100vw;
  height: 100vh;
  margin:0;
  padding:0;
  overflow:hidden;
}

.lookbook-nav {
  width: 70vw;
  height: 10vh;
  margin-left: 15vw;
  margin-top: 45vh;
  position: absolute;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

button {
  border: none;
  outline: none;
  background: transparent;
  font-size: 2rem;
  /* font-weight: bold; */
  cursor: pointer;
}

.lookbook-nav button {
  border: none;
  outline: none;
  background: transparent;
  font-size: 2rem;
  /* font-weight: bold; */
  cursor: pointer;
}

button:hover {
  opacity: 0.4;
}

div .lookbook {
  width: 56vw;
}

.lookbook {
  height: 91vh;
  margin: auto;
  overflow: hidden;
}

div.slider{
  margin:0;
  margin-top: 10vh;
  height:81vh;/* this is height of (lookbook - margin-top) - probably better done through flex */
  position:relative;
  top:0;
  padding:0;
  width:100%;
}

@keyframes slideouttoleft {
  from {
   left: 0;
   visibility:visible;
  }
  to {
   left: -100%;
   visibility:hidden;
  }
}
@keyframes slideinfromright {
  from {
   left: 100%;
   visibility:visible;
  }
  to {
   left: 0;
   visibility:visible;
  }
}
@keyframes slideouttoright {
  from {
   left: 0;
   visibility:visible;
  }
  to {
   left: 100%;
   visibility:hidden;
  }
}
@keyframes slideinfromleft {
  from {
   left: -100%;
   visibility:visible;
  }
  to {
   left: 0;
   visibility:visible;
  }
}

.slider div {
  position:absolute;
  top:0;
  left:0;
  overflow:hidden;
  visibility:hidden;
  margin: 0;
  padding: 0;
  width:100%;
  height:100%;
  background-size: contain;
  background-position: center center;
  background-repeat: no-repeat no-repeat;
  animation-duration: 0.4s;
  animation-delay: 0s;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-timing-function: ease-in;
  animation-fill-mode: forwards;
}
</style>
</head>
<body>

<div id="lookbook" data-tab-content class="black-text">
        <div class="lookbook-nav">
          <button id="left">←</button>
          <button id="right">→</button>
        </div>
        <div class="lookbook">
          <div class="slider">
          <!-- images taken from Reading (UK) Museum's Victorian copy of the Bayeux Tapestry -->
            <div style="background-image:url(https://rgspaces.org.uk/bayeuxtapestry/wp-content/uploads/boat-and-horses-768x546.png);"></div>
            <div style="background-image:url(https://rgspaces.org.uk/bayeuxtapestry/wp-content/uploads/two-horses-300x212.png);"></div>
            <div style="background-image:url(https://rgspaces.org.uk/bayeuxtapestry/wp-content/uploads/woman-and-child-1200x901.png);"></div>
            <div style="background-image:url(https://rgspaces.org.uk/bayeuxtapestry/wp-content/uploads/archer-2-768x1100.png);"></div>
            <div style="background-image:url(https://rgspaces.org.uk/bayeuxtapestry/wp-content/uploads/boat-builder-2-878x1024.png);"></div>
            <div style="background-image:url(https://rgspaces.org.uk/bayeuxtapestry/wp-content/uploads/group-1-768x603.png);"></div>
            <div style="background-image:url(https://rgspaces.org.uk/bayeuxtapestry/wp-content/uploads/pointing-horseman-768x853.png);"></div>
            <div style="background-image:url(https://rgspaces.org.uk/bayeuxtapestry/wp-content/uploads/group-2-768x619.png);"></div>
            <div style="background-image:url(https://rgspaces.org.uk/bayeuxtapestry/wp-content/uploads/carrying-casket-768x556.png);"></div>
          </div>
        </div>
</div>
<script>
const slider = document.querySelector('.slider');
const sliderImages = document.querySelectorAll('.slider div');
const leftbtn = document.querySelector('#left');
const rightbtn = document.querySelector('#right');
const numImgs=sliderImages.length;
let curImg = 0;

rightbtn.addEventListener('click', () => {
  sliderImages[curImg].style.animationName='slideouttoleft';
  curImg=(curImg+1)%numImgs;
  sliderImages[curImg].style.animationName='slideinfromright';
})

leftbtn.addEventListener('click', () => {
  sliderImages[curImg].style.animationName='slideouttoright';
  curImg=curImg==0? numImgs-1 : Math.abs((curImg-1)%numImgs);
  sliderImages[curImg].style.animationName='slideinfromleft';
})

function initialize() {
  sliderImages[0].style.animationName='slideinfromright';
}

window.onload=initialize;

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