多列滑块

问题描述 投票:0回答:1

enter image description here

我正在尝试找到一种优雅的解决方案,只需很少的 JavaScript 即可创建可以具有一列或多列的轮播。单击按钮,它应该滚动到下一列。

想法(我认为JS太多了):

  • 创建网格
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  • 使用 JavaScript 找出当前有多少列并填充网格
  • 调整大小:我们需要一个ResizeObersiver来了解当用户调整窗口大小并调整内容时列数是否发生变化。 -> 丑陋

我正在尝试找到一种解决方案来避免使用 ResizeObserver。理想情况下,CSS 会自动处理列数、列宽以及侧滚动,而无需复杂的 JS 逻辑。有人有一个优雅的解决方案吗?

css reactjs
1个回答
0
投票

CSS 可以做你需要的一切,除了在单击下一个和上一个按钮时处理它们。

此代码段定义了一个元素,即视图,您可以为该元素指定要在其中查看幻灯片的宽度。这是居中的。

其中有一个元素滑块,它通过使所有项目并排显示为内联块。 [这里似乎没有特别需要使用 grid 或 flex]。

代码片段定义了项目之间的间隙,然后计算出每个元素的宽度,但当然您也可以反过来做。

通过改变滑块的左侧值来移动到下一张/上一张幻灯片。

幻灯片必须有两份,因为如果你想减少 JS,你需要 CSS 才能显示下一张(例如,移动到下一张时可能是第一个)。

JS 用于两个单击事件,当它检测到滑块到达一半时,它会重新定位它,以便右侧始终有项目。用户看不到这种重新定位,因为它仅在一帧中发生(使用 requestAnimationFrame),然后发生实际所需的转换。

CSS 调整宽度以适应视口宽度,无需 JS 感知调整大小。

<style>
  * {
    margin: 0;
  }
  
  .container {
    position: relative;
    width: 100vw;
    left: 50%;
    transform: translateX(-50%);
  }
  
  .view {
    position: relative;
    left: 50%;
    transform: translateX(-50%);
    overflow: hidden;
    white-space: nowrap;
    width: var(--visibleW);
    --visibleW: 80vw;
    /* the width of slider that you want to be actually showing */
  }
  
  .slider {
    position: relative;
    --showN: 1;
    /* the number of items to show at a time, varies with viewport width */
    transition: left var(--time);
    left: calc(-1 * var(--itemI) * var(--visibleW) / var(--showN));
  }
  
  .slider>* {
    --margin: 5vw;
    /* half the gap between items */
    margin: 0 var(--margin);
    height: min(80vh, 300px);
    /* for demo, set to what you want */
    display: inline-block;
    width: calc((var(--visibleW) - (var(--showN) * 2 * var(--margin))) / var(--showN))/* the width of each item including the 'gap' */
  }
  
  @media (min-width: 1024px) {
    .slider {
      --showN: 3;
    }
  }
  
  @media (min-width: 1500px) {
    .slider {
      --showN: 4;
    }
  }
  
  button {
    position: absolute;
    top: 50%;
    font-size: min(24px, 5vw);
    padding: 10px;
    background: transparent;
  }
  
  .prev {
    transform: translateY(-50%);
  }
  
  .next {
    right: 0;
    transform: translateY(-50%);
  }
</style>
<div class="container">
  <div class="view">
    <div class="slider">
      <div style="background: red;"></div>
      <div style="background: green;"></div>
      <div style="background: blue;"></div>
      <div style="background: yellow;"></div>
      <div style="background: red;"></div>
      <div style="background: green;"></div>
      <div style="background: blue;"></div>
      <div style="background: yellow;"></div>
    </div>
  </div>
  <button class="prev"><</button>
  <button class="next">></button>
</div>
<script>
  const view = document.querySelector('.view');
  const slider = document.querySelector('.slider');
  const prev = document.querySelector('.prev');
  const next = document.querySelector('.next');
  const num = slider.children.length;
  const time = '1s'; //set this to the time you want the sliding to next one to take
  let itemI = 0; // this is the index of the item that is the leftmost in the view
  view.style.setProperty('--itemI', itemI);
  slider.style.setProperty('--time', time);
  prev.addEventListener('click', function() {
    if (itemI > 0) {
      itemI--;
      view.style.setProperty('--itemI', itemI);
    } else {
      itemI = num / 2;
      view.style.setProperty('--itemI', itemI);
      slider.style.transition = "left 0s";
      requestAnimationFrame(function() {
        itemI--;
        slider.style.transition = "left var(--time)";
        view.style.setProperty('--itemI', itemI);
      });
    }
  });
  next.addEventListener('click', function() {
    if (itemI != (num / 2)) {
      itemI++;
      slider.style.transition = "left var(--time)";
      view.style.setProperty('--itemI', itemI);
    } else {
      itemI = 0;
      view.style.setProperty('--itemI', itemI);
      slider.style.transition = "left 0s";
      requestAnimationFrame(function() {
        itemI++;
        slider.style.transition = "left var(--time)";
        view.style.setProperty('--itemI', itemI);
      });
    }
  });
</script>

© www.soinside.com 2019 - 2024. All rights reserved.