纯CSS做3D图片轮播,无限旋转效果咋实现?

3,810字
16–24 分钟
in

搞3D图片轮播,纯CSS就能转得飞起。从基础布局到旋转动画,再到立方体乱翻,一套HTML代码通吃。这里拆解水平旋转、垂直切换、立方体翻滚三种玩法,手把手看代码怎么动起来。

目录

基础堆叠

先把图片叠成一摞。HTML结构就一个容器加一堆img标签,数量随便。

<div class="gallery">
  <img src="img1.jpg" alt="">
  <img src="img2.jpg" alt="">
  <img src="img3.jpg" alt="">
  <img src="img4.jpg" alt="">
  <img src="img5.jpg" alt="">
  <img src="img6.jpg" alt="">
</div>

CSS网格把每张图塞进同一个格子,后盖前。

.gallery {
  display: grid;
}
.gallery > img {
  grid-area: 1 / 1;
  width: 160px;
  aspect-ratio: 1;
  object-fit: cover;
}

这一步就像把六张照片摞在桌面,完全看不见下层。接下来要掰开它们,摆成3D形状。

水平3D旋转

核心思路:每张图绕着中心转圈,再往前推,最后集体转起来。先上Sass循环,自动算位置。

$n: 6; // 图片数量

@for $i from 1 to ($n + 1) {
  .gallery > img:nth-child(#{$i}) {
    transform: 
      rotate(#{360 * ($i - 1) / $n}deg)
      translateY(50% / tan(180deg / $n))
      rotateX(90deg);
  }
}

解释一下这三步:

  • rotate:把图片摊成圆形,每张转不同角度。比如6张图,每张差60度。
  • translateY:把图片往外推。距离公式 50% / tan(180deg/N) 保证边缘刚好碰一起。这就像六个人手拉手围成圈,每人退一步才能拉成圆。
  • rotateX(90deg):把平躺的圆立起来。原本图片朝上,转90度后变成朝侧面,围成柱状。

推完所有图片后,整个容器要旋转起来才能看到每张图。动画抄之前圆形轮播的帧数逻辑。

.gallery {
  transform-style: preserve-3d;
  --_t: perspective(280px) rotateX(-90deg);
  animation: r 12s cubic-bezier(.5, -0.2, .5, 1.2) infinite;
}

@keyframes r {
  0%, 3% { transform: var(--_t) rotate(0deg); }
  14.7%, 19.7% { transform: var(--_t) rotate(-60deg); }
  31.3%, 36.3% { transform: var(--_t) rotate(-120deg); }
  48%, 53% { transform: var(--_t) rotate(-180deg); }
  64.7%, 69.7% { transform: var(--_t) rotate(-240deg); }
  81.3%, 86.3% { transform: var(--_t) rotate(-300deg); }
  98%, 100% { transform: var(--_t) rotate(-360deg); }
}

这里的百分比是按6张图算的:每张停留约11%,中间用2%缓冲过渡。rotateX(-90deg) 是为了让视角从上方俯瞰,perspective 给深度感。跑起来就像旋转木马,每张图轮流停在面前。

写代码时容易翻车的地方:忘记 transform-style: preserve-3d 会导致子元素的3D变换被压扁。还有 translateY 的数值如果写死,换不同数量图片时边缘会错位,用Sass自动算就能避免。

垂直3D旋转

想换个方向转?把 rotate 改成 rotateX 就行。水平版本绕Z轴转,垂直版本绕X轴转。

@for $i from 1 to ($n + 1) {
  .gallery > img:nth-child(#{$i}) {
    transform: 
      rotateX(#{360 * ($i - 1) / $n}deg)
      translateY(50% / tan(180deg / $n))
      rotateX(90deg);
  }
}

关键帧里的 rotate 也换成 rotateX

@keyframes r {
  0%, 3% { transform: var(--_t) rotateX(0deg); }
  14.7%, 19.7% { transform: var(--_t) rotateX(-60deg); }
  /* 后面同理... */
  98%, 100% { transform: var(--_t) rotateX(-360deg); }
}

效果变成图片像风车一样上下翻滚。有个小坑:rotateX 旋转超过90度后图片会面向背面,需要确保每张图的初始角度能兜住整个圆。

立方体滑块

想要真正的六面体?固定6张图,每张贴一个面。这里不用Sass,直接手写每张图的位置。

.gallery {
  --s: 250px;
  transform-style: preserve-3d;
  --_p: perspective(calc(2.5 * var(--s)));
  animation: r 9s infinite cubic-bezier(.5, -0.5, .5, 1.5);
}

.gallery img {
  grid-area: 1 / 1;
  width: var(--s);
  aspect-ratio: 1;
  object-fit: cover;
  transform: var(--_t,) translateZ(calc(var(--s) / 2));
}

.gallery img:nth-child(2) { --_t: rotateX(-90deg); }
.gallery img:nth-child(3) { --_t: rotateY(90deg) rotate(-90deg); }
.gallery img:nth-child(4) { --_t: rotateX(180deg) rotate(90deg); }
.gallery img:nth-child(5) { --_t: rotateX(90deg) rotate(90deg); }
.gallery img:nth-child(6) { --_t: rotateY(-90deg); }

第一张图只有平移,其他每张先旋转到对应面,再平移到中心。比如第二张 rotateX(-90deg) 就贴到顶面。那个逗号 var(--_t,) 不是笔误——当 --_t 没定义时,逗号让属性回退到空值,避免整行失效。

关键帧依次翻转各个面:

@keyframes r {
  0%, 3%   { transform: var(--_p); }
  14%, 19% { transform: var(--_p) rotateX(90deg); }
  31%, 36% { transform: var(--_p) rotateX(90deg) rotateZ(90deg); }
  47%, 52% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg); }
  64%, 69% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg); }
  81%, 86% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg) rotateZ(90deg); }
  97%, 100%{ transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg) rotateZ(90deg) rotateY(-90deg); }
}

每一步叠加一个新旋转,六个状态刚好转完一圈。调试时可以打开浏览器开发者工具,挨个改旋转值,盯着屏幕看到贴正为止。虽然笨,但比硬算角度快得多。

随机乱转立方体

不想按顺序转?那就假装随机。关键帧里每个阶段直接跳到想要展示的那一面,中间过渡用 rotate3d 瞎转。

@keyframes r {
  0%, 3%   { transform: var(--_p) rotate3d( 0, 0, 0,  0deg); }
  14%,19%  { transform: var(--_p) rotate3d(-1, 1, 0, 180deg); }
  31%,36%  { transform: var(--_p) rotate3d( 0,-1, 0,  90deg); }
  47%,52%  { transform: var(--_p) rotate3d( 1, 0, 0,  90deg); }
  64%,69%  { transform: var(--_p) rotate3d( 1, 0, 0, -90deg); }
  81%,86%  { transform: var(--_p) rotate3d( 0, 1, 0,  90deg); }
  97%,100% { transform: var(--_p) rotate3d( 0, 0, 0,   0deg); }
}

rotate3d(x, y, z, deg) 可以绕着任意轴转。这几个值不需要有数学规律,全凭手感在开发者工具里扭。只要保证开头和结尾都是第一张图,中间每段露出不同面就行。比如第二段转到背面,第三段突然翻到右侧面,看起来就像骰子在桌上乱滚。

操作时一个小技巧:把动画时长调慢到20秒,一边改一边预览,找到满意的组合就记下来。最终效果比规规矩矩的旋转更有趣味。