我想做一个3D旋转 carousel ,就像在堆栈片段中一样,但需要从内部查看,如下图所示.我怎样才能做到这一点呢?

window.addEventListener('load', () => {
  var carousels = document.querySelectorAll('.carousel');

  for (var i = 0; i < carousels.length; i++) {
    carousel(carousels[i]);
  }
});

function carousel(root) {
  var
    figure = root.querySelector('figure'),
    nav = root.querySelector('nav'),
    images = figure.children,
    n = images.length,
    gap = root.dataset.gap || 0,
    bfc = 'bfc' in root.dataset,
    theta = 2 * Math.PI / n,
    currImage = 0;

  setupCarousel(n, parseFloat(getComputedStyle(images[0]).width));
  
  window.addEventListener('resize', () => {
    setupCarousel(n, parseFloat(getComputedStyle(images[0]).width))
  });

  setupNavigation();

  function setupCarousel(n, s) {
    var apothem = s / (2 * Math.tan(Math.PI / n));

    figure.style.transformOrigin = `50% 50% ${- apothem}px`;

    for (var i = 0; i < n; i++) {
      images[i].style.padding = `${gap}px`;
    }
      
    for (i = 1; i < n; i++) {
      images[i].style.transformOrigin = `50% 50% ${- apothem}px`;
      images[i].style.transform = `rotateY(${i * theta}rad)`;
    }
    
    if (bfc) {
      for (i = 0; i < n; i++) {
        images[i].style.backfaceVisibility = 'hidden';
      }
    }

    rotateCarousel(currImage);
  }

  function setupNavigation() {
    nav.addEventListener('click', onClick, true);

    function onClick(e) {
      e.stopPropagation();

      var t = e.target;
      
      if (t.tagName.toUpperCase() != 'BUTTON') {
        return;
      }

      if (t.classList.contains('next')) {
        currImage++;
      } 
      else {
        currImage--;
      }

      rotateCarousel(currImage);
    }
  }

  function rotateCarousel(imageIndex) {
    figure.style.transform = `rotateY(${imageIndex * -theta}rad)`;
  }
}
body {
  margin: 0;
  font-family: 'Roboto', sans-serif;
  font-size: 16px;
}

h1 {
  text-align: center;
  margin-bottom: 1.5em;
}

h2 {
  text-align: center;
  color: #555;
  margin-bottom: 0;
}

.carousel {
  padding: 20px;
  perspective: 500px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.carousel>* {
  flex: 0 0 auto;
}

.carousel figure {
  margin: 0;
  width: 40%;
  transform-style: preserve-3d;
  transition: transform 0.5s;
}

.carousel figure img {
  width: 100%;
  box-sizing: border-box;
  padding: 0 0px;
}

.carousel figure img:not(:first-of-type) {
  position: absolute;
  left: 0;
  top: 0;
}

.carousel nav {
  display: flex;
  justify-content: center;
  margin: 20px 0 0;
}

.carousel nav button {
  flex: 0 0 auto;
  margin: 0 5px;
  cursor: pointer;
  color: #333;
  background: none;
  border: 1px solid;
  letter-spacing: 1px;
  padding: 5px 10px;
}
<h2>Eight images, with 80px gap</h2>

<div class="carousel" data-gap="80">
  <figure>
    <img src="https://source.unsplash.com/VkwRmha1_tI/800x533" alt="">
    <img src="https://source.unsplash.com/EbuaKnSm8Zw/800x533" alt="">
    <img src="https://source.unsplash.com/kG38b7CFzTY/800x533" alt="">
    <img src="https://source.unsplash.com/nvzvOPQW0gc/800x533" alt="">
    <img src="https://source.unsplash.com/mCg0ZgD7BgU/800x533" alt="">
    <img src="https://source.unsplash.com/1FWICvPQdkY/800x533" alt="">
    <img src="https://source.unsplash.com/bjhrzvzZeq4/800x533" alt="">
    <img src="https://source.unsplash.com/7mUXaBBrhoA/800x533" alt="">
  </figure>
  
  <nav>
    <button class="nav prev">Prev</button>
    <button class="nav next">Next</button>
  </nav>
</div>

enter image description here

推荐答案

carousel 需要以相反的方向旋转和平移,以使其成为内部旋转视图.

transform-origin设置 carousel 的中心.前两个数字是x和y,它们只是将 carousel 在屏幕上居中.第三个数字是z,它确定旋转传送带是朝向屏幕移动还是远离屏幕移动.apothem上的负号被移除以将其拉出屏幕,因此中心变得更靠近相机,并实现内部 carousel .backface-visibility需要始终设置为隐藏,因为现在相机后面可能有图像.然后,为了用下一步按钮固定旋转方向,将* -theta改为正数.

window.addEventListener('load', () => {
  var carousels = document.querySelectorAll('.carousel');

  for (var i = 0; i < carousels.length; i++) {
    carousel(carousels[i]);
  }
});

function carousel(root) {
  var
    figure = root.querySelector('figure'),
    nav = root.querySelector('nav'),
    images = figure.children,
    n = images.length,
    gap = root.dataset.gap || 0,
    theta = 2 * Math.PI / n,
    currImage = 0;

  setupCarousel(n, parseFloat(getComputedStyle(images[0]).width));
  
  window.addEventListener('resize', () => {
    setupCarousel(n, parseFloat(getComputedStyle(images[0]).width))
  });

  setupNavigation();

  function setupCarousel(n, s) {
    var apothem = s / (2 * Math.tan(Math.PI / n));

    figure.style.transformOrigin = `50% 50% ${apothem}px`;

    for (var i = 0; i < n; i++) {
      images[i].style.padding = `${gap}px`;
    }
      
    for (i = 1; i < n; i++) {
      images[i].style.transformOrigin = `50% 50% ${apothem}px`;
      images[i].style.transform = `rotateY(${i * theta}rad)`;
    }

    rotateCarousel(currImage);
  }

  function setupNavigation() {
    nav.addEventListener('click', onClick, true);

    function onClick(e) {
      e.stopPropagation();

      var t = e.target;
      
      if (t.tagName.toUpperCase() != 'BUTTON') {
        return;
      }

      if (t.classList.contains('next')) {
        currImage++;
      } 
      else {
        currImage--;
      }

      rotateCarousel(currImage);
    }
  }

  function rotateCarousel(imageIndex) {
    figure.style.transform = `rotateY(${imageIndex * theta}rad)`;
  }
}
body {
  margin: 0;
  font-family: 'Roboto', sans-serif;
  font-size: 16px;
}

h1 {
  text-align: center;
  margin-bottom: 1.5em;
}

h2 {
  text-align: center;
  color: #555;
  margin-bottom: 0;
}

.carousel {
  padding: 20px;
  perspective: 500px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.carousel>* {
  flex: 0 0 auto;
}

.carousel figure {
  margin: 0;
  width: 40%;
  transform-style: preserve-3d;
  transition: transform 0.5s;
}

.carousel figure img {
  width: 100%;
  box-sizing: border-box;
  padding: 0 0px;
  backface-visibility: hidden;
}

.carousel figure img:not(:first-of-type) {
  position: absolute;
  left: 0;
  top: 0;
}

.carousel nav {
  display: flex;
  justify-content: center;
  margin: 20px 0 0;
}

.carousel nav button {
  flex: 0 0 auto;
  margin: 0 5px;
  cursor: pointer;
  color: #333;
  background: none;
  border: 1px solid;
  letter-spacing: 1px;
  padding: 5px 10px;
}
<h2>Eight images, with 80px gap</h2>

<div class="carousel" data-gap="80">
  <figure>
    <img src="https://source.unsplash.com/VkwRmha1_tI/800x533" alt="">
    <img src="https://source.unsplash.com/EbuaKnSm8Zw/800x533" alt="">
    <img src="https://source.unsplash.com/kG38b7CFzTY/800x533" alt="">
    <img src="https://source.unsplash.com/nvzvOPQW0gc/800x533" alt="">
    <img src="https://source.unsplash.com/mCg0ZgD7BgU/800x533" alt="">
    <img src="https://source.unsplash.com/1FWICvPQdkY/800x533" alt="">
    <img src="https://source.unsplash.com/bjhrzvzZeq4/800x533" alt="">
    <img src="https://source.unsplash.com/7mUXaBBrhoA/800x533" alt="">
  </figure>
  
  <nav>
    <button class="nav prev">Prev</button>
    <button class="nav next">Next</button>
  </nav>
</div>

Javascript相关问答推荐

Phaser框架-将子对象附加到Actor

我应该在redux reducer中调用其他reducer函数吗?

如何用显示网格平滑地将元素从一个地方移动到另一个地方?

如何禁用附加图标点击的v—自动完成事件

在react js中使用react—router—dom中的Link组件,分配的右侧不能被 destruct ''

CheckBox作为Vue3中的一个组件

实现JS代码更改CSS元素

如何在每次单击按钮时重新加载HighChart/设置HighChart动画?

IF语句的计算结果与实际情况相反

覆盖TypeScrip中的Lit-Element子类的属性类型

从Nextjs中的 Select 项收集值,但当单击以处理时,未发生任何情况

同一类的所有div';S的模式窗口

从另一个数组中的对应行/键值对更新数组中的键值对对象

当代码另有说明时,随机放置的圆圈有时会从画布上消失

JS Animate()方法未按预期工作

Pevent触发material 用户界面数据网格中的自动保存

FindByIdAndUpdate在嵌套对象中创建_id

如何在加载页面时使锚定标记成为焦点

VITE版本中的内联SVG

JQuery-无法 Select 使用elementor添加的元素的值