站点工具

用户工具


## 旋转木马 在内容轮播方面,前端开发人员有无数的选择。[我可以推荐 Flickity 吗?](https://flickity.metafizzy.co/) (备注:这是原作者的推荐,不是译者加)既然我们的浏览器具有 3D 功能,为什么不尝试创建一个真正的 3D 轮播呢?

此HTML结构采用与盒子、立方体和卡片相同的形式。让我们让它变得有趣并有一个带有 9 个面板的旋转木马。

```html <div class=“scene”>

<div class="carousel">
  <div class="carousel__cell">1</div>
  <div class="carousel__cell">2</div>
  <div class="carousel__cell">3</div>
  <div class="carousel__cell">4</div>
  <div class="carousel__cell">5</div>
  <div class="carousel__cell">6</div>
  <div class="carousel__cell">7</div>
  <div class="carousel__cell">8</div>
  <div class="carousel__cell">9</div>
</div>

</div> ``` ![](css3d-carousel-1.png)

现在应用基本布局样式。让我们给每个单元格彼此之间设置 20px 的间隙,在这里使用`left: 10px`. 每个面板的有效宽度保持为 210px。

```css .scene {

width: 210px;
height: 140px;
position: relative;
perspective: 1000px;

}

.carousel {

width: 100%;
height: 100%;
position: absolute;
transform-style: preserve-3d;

}

.carouselcell { position: absolute; width: 190px; height: 120px; left: 10px; top: 10px; } ``` 下一步:旋转各个面。这个旋转木马有 9 个面。如果每个面在旋转木马上均等分布,则每个面将比上一个面多旋转 40 度 ( 360 / 9 )。 ```css .carouselcell:nth-child(1) { transform: rotateY( 0deg); } .carouselcell:nth-child(2) { transform: rotateY( 40deg); } .carouselcell:nth-child(3) { transform: rotateY( 80deg); } .carouselcell:nth-child(4) { transform: rotateY(120deg); } .carouselcell:nth-child(5) { transform: rotateY(160deg); } .carouselcell:nth-child(6) { transform: rotateY(200deg); } .carouselcell:nth-child(7) { transform: rotateY(240deg); } .carouselcell:nth-child(8) { transform: rotateY(280deg); } .carouselcell:nth-child(9) { transform: rotateY(320deg); } ```

现在向外转移。回到我们创建立方体和盒子的时候,这个translate值很容易计算,因为它等于对象的宽度、高度或深度的一半。现在有了旋转木马,我们无法立即参考尺寸。我们将通过其他方式计算移位的距离。

根据这个轮播画一个简图,我们目前知道两件事:每个面板的宽度为210px,每个面板从下一个旋转40度。如果我们将这些三角形中的一个沿其中心分开,我们就会得到一个直角三角形。

![](css3d-carousel-2.png)

我们可以用一个基本的切线方程来确定这个图中r的长度。

![](css3d-carousel-3.png)

这288px就是在 3D 空间中将面板平移出来的距离。 ```css

.carouselcell:nth-child(1) { transform: rotateY( 0deg) translateZ(288px); } .carouselcell:nth-child(2) { transform: rotateY( 40deg) translateZ(288px); } .carouselcell:nth-child(3) { transform: rotateY( 80deg) translateZ(288px); } .carouselcell:nth-child(4) { transform: rotateY(120deg) translateZ(288px); } .carouselcell:nth-child(5) { transform: rotateY(160deg) translateZ(288px); } .carouselcell:nth-child(6) { transform: rotateY(200deg) translateZ(288px); } .carouselcell:nth-child(7) { transform: rotateY(240deg) translateZ(288px); } .carouselcell:nth-child(8) { transform: rotateY(280deg) translateZ(288px); } .carouselcell:nth-child(9) { transform: rotateY(320deg) translateZ(288px); } ``` ![](css3d-carousel-4.png) 如果我们决定改变面板的宽度或面板的数量,我们只需要将这两个变量插入到我们的方程中以获得适当的 translateZ 值。用JavaScript计算,该等式将是: ```javascript var tz = Math.round( ( cellSize / 2 ) / Math.tan( ( ( Math.PI * 2 ) / numberOfCells ) / 2 ) ); or simplified to var tz = Math.round( ( cellSize / 2 ) / Math.tan( Math.PI / numberOfCells ) ); ``` 就像我们之前的 3D 对象一样,要显示任何一个面板,我们只需要在旋转木马上应用反向变换。 ```css /* show fifth cell */ .carousel { transform: translateZ(-288px) rotateY(-160deg); } ``` [在 CodePen 上编辑此演示](https://codepen.io/desandro/pen/jxwELK) 完整代码: ```html <div class=“scene”> <div class=“carousel”> <div class=“carouselcell”>1</div> <div class=“carouselcell”>2</div> <div class=“carouselcell”>3</div> <div class=“carouselcell”>4</div> <div class=“carouselcell”>5</div> <div class=“carouselcell”>6</div> <div class=“carouselcell”>7</div> <div class=“carouselcell”>8</div> <div class=“carouselcell”>9</div> </div> </div> <p style=“text-align: center;”> <button class=“previous-button”>Previous</button> <button class=“next-button”>Next</button> </p> <style> * { box-sizing: border-box; } body { font-family: sans-serif; } .scene { border: 1px solid #CCC; margin: 40px 0; position: relative; width: 210px; height: 140px; margin: 40px auto; perspective: 1000px; } .carousel { width: 100%; height: 100%; position: absolute; transform: translateZ(-288px); transform-style: preserve-3d; transition: transform 1s; } .carouselcell { position: absolute; width: 190px; height: 120px; left: 10px; top: 10px; border: 2px solid black; line-height: 116px; font-size: 80px; font-weight: bold; color: white; text-align: center; } .carouselcell:nth-child(9n+1) { background: hsla( 0, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+2) { background: hsla( 40, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+3) { background: hsla( 80, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+4) { background: hsla(120, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+5) { background: hsla(160, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+6) { background: hsla(200, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+7) { background: hsla(240, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+8) { background: hsla(280, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+0) { background: hsla(320, 100%, 50%, 0.8); } .carouselcell:nth-child(1) { transform: rotateY( 0deg) translateZ(288px); } .carouselcell:nth-child(2) { transform: rotateY( 40deg) translateZ(288px); } .carouselcell:nth-child(3) { transform: rotateY( 80deg) translateZ(288px); } .carouselcell:nth-child(4) { transform: rotateY(120deg) translateZ(288px); } .carouselcell:nth-child(5) { transform: rotateY(160deg) translateZ(288px); } .carouselcell:nth-child(6) { transform: rotateY(200deg) translateZ(288px); } .carouselcell:nth-child(7) { transform: rotateY(240deg) translateZ(288px); } .carouselcell:nth-child(8) { transform: rotateY(280deg) translateZ(288px); } .carouselcell:nth-child(9) { transform: rotateY(320deg) translateZ(288px); } </style> <script> var carousel = document.querySelector('.carousel'); var cellCount = 9; var selectedIndex = 0; function rotateCarousel() { var angle = selectedIndex / cellCount * -360; carousel.style.transform = 'translateZ(-288px) rotateY(' + angle + 'deg)'; } var prevButton = document.querySelector('.previous-button'); prevButton.addEventListener( 'click', function() { selectedIndex–; rotateCarousel(); }); var nextButton = document.querySelector('.next-button'); nextButton.addEventListener( 'click', function() { selectedIndex++; rotateCarousel(); }); </script> ``` ## 使用 JavaScript 的 3D 轮播 到现在为止,您可能在想为每个面板重新编写变换样式是多么无趣。你是完全正确的。3D 对象的重复性使其适合编写JavaScript脚本。我们可以将所有单调的变换样式转移到我们的 JavaScript 中,如果处理得当,这将比写在CSS里怼硬编码版本要更灵活。 [在 CodePen 上编辑此演示](https://codepen.io/desandro/pen/wjeBpp) 完整代码 ```html <div class=“scene”> <div class=“carousel”> <div class=“carouselcell”>1</div> <div class=“carouselcell”>2</div> <div class=“carouselcell”>3</div> <div class=“carouselcell”>4</div> <div class=“carouselcell”>5</div> <div class=“carouselcell”>6</div> <div class=“carouselcell”>7</div> <div class=“carouselcell”>8</div> <div class=“carouselcell”>9</div> <div class=“carouselcell”>10</div> <div class=“carouselcell”>11</div> <div class=“carouselcell”>12</div> <div class=“carouselcell”>13</div> <div class=“carouselcell”>14</div> <div class=“carouselcell”>15</div> </div> </div> <div class=“carousel-options”> <p> <label> Cells <input class=“cells-range” type=“range” min=“3” max=“15” value=“9” /> </label> </p> <p> <button class=“previous-button”>Previous</button> <button class=“next-button”>Next</button> </p> <p> Orientation: <label> <input type=“radio” name=“orientation” value=“horizontal” checked /> horizontal </label> <label> <input type=“radio” name=“orientation” value=“vertical” /> vertical </label> </p> </div> <style> * { box-sizing: border-box; } body { font-family: sans-serif; text-align: center; } .scene { border: 1px solid #CCC; margin: 40px 0; position: relative; width: 210px; height: 140px; margin: 80px auto; perspective: 1000px; } .carousel { width: 100%; height: 100%; position: absolute; transform: translateZ(-288px); transform-style: preserve-3d; transition: transform 1s; } .carouselcell { position: absolute; width: 190px; height: 120px; left: 10px; top: 10px; border: 2px solid black; line-height: 116px; font-size: 80px; font-weight: bold; color: white; text-align: center; transition: transform 1s, opacity 1s; } .carouselcell:nth-child(9n+1) { background: hsla( 0, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+2) { background: hsla( 40, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+3) { background: hsla( 80, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+4) { background: hsla(120, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+5) { background: hsla(160, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+6) { background: hsla(200, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+7) { background: hsla(240, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+8) { background: hsla(280, 100%, 50%, 0.8); } .carouselcell:nth-child(9n+0) { background: hsla(320, 100%, 50%, 0.8); } .carouselcell:nth-child(1) { transform: rotateY( 0deg) translateZ(288px); } .carouselcell:nth-child(2) { transform: rotateY( 40deg) translateZ(288px); } .carouselcell:nth-child(3) { transform: rotateY( 80deg) translateZ(288px); } .carouselcell:nth-child(4) { transform: rotateY(120deg) translateZ(288px); } .carouselcell:nth-child(5) { transform: rotateY(160deg) translateZ(288px); } .carouselcell:nth-child(6) { transform: rotateY(200deg) translateZ(288px); } .carouselcell:nth-child(7) { transform: rotateY(240deg) translateZ(288px); } .carouselcell:nth-child(8) { transform: rotateY(280deg) translateZ(288px); } .carouselcell:nth-child(9) { transform: rotateY(320deg) translateZ(288px); } .carousel-options { text-align: center; position: relative; z-index: 2; background: hsla(0, 0%, 100%, 0.8); } </style> <script> var carousel = document.querySelector('.carousel'); var cells = carousel.querySelectorAll('.carousel__cell'); var cellCount; cellCount set from cells-range input value var selectedIndex = 0; var cellWidth = carousel.offsetWidth; var cellHeight = carousel.offsetHeight; var isHorizontal = true; var rotateFn = isHorizontal ? 'rotateY' : 'rotateX'; var radius, theta; console.log( cellWidth, cellHeight ); function rotateCarousel() { var angle = theta * selectedIndex * -1; carousel.style.transform = 'translateZ(' + -radius + 'px) ' + rotateFn + '(' + angle + 'deg)'; } var prevButton = document.querySelector('.previous-button'); prevButton.addEventListener( 'click', function() { selectedIndex–; rotateCarousel(); }); var nextButton = document.querySelector('.next-button'); nextButton.addEventListener( 'click', function() { selectedIndex++; rotateCarousel(); }); var cellsRange = document.querySelector('.cells-range'); cellsRange.addEventListener( 'change', changeCarousel ); cellsRange.addEventListener( 'input', changeCarousel ); function changeCarousel() { cellCount = cellsRange.value; theta = 360 / cellCount; var cellSize = isHorizontal ? cellWidth : cellHeight; radius = Math.round( ( cellSize / 2) / Math.tan( Math.PI / cellCount ) ); for ( var i=0; i < cells.length; i++ ) { var cell = cells[i]; if ( i < cellCount ) { visible cell cell.style.opacity = 1; var cellAngle = theta * i; cell.style.transform = rotateFn + '(' + cellAngle + 'deg) translateZ(' + radius + 'px)'; } else { hidden cell cell.style.opacity = 0; cell.style.transform = 'none'; } } rotateCarousel(); } var orientationRadios = document.querySelectorAll('input[name=“orientation”]'); ( function() { for ( var i=0; i < orientationRadios.length; i++ ) { var radio = orientationRadios[i]; radio.addEventListener( 'change', onOrientationChange ); } })(); function onOrientationChange() { var checkedRadio = document.querySelector('input[name=“orientation”]:checked'); isHorizontal = checkedRadio.value == 'horizontal'; rotateFn = isHorizontal ? 'rotateY' : 'rotateX'; changeCarousel(); } set initials onOrientationChange(); </script> ``` 我们不仅可以改变单元格的数量,我们甚至可以将旋转木马的方向从水平改为垂直。

若愚 · 2021/12/21 16:00 · css3d_旋转木马.1640073610.txt.gz