animation实现卡片翻转动效‌

使用animation实现卡片翻转动效‌,效果如下(iPhone转的gif有卡顿,实际效果流畅):

先上全部代码,再在最后给出代码解析,分步说明实现原理。

html

<div class="visual-card">
  <div class="contain">
    <div class="section-box">
      <div class="card-box">
        <img src="../../assets/images/1.png" />
      </div>
      <div class="card-box">
        <img src="../../assets/images/2.png" />
      </div>
      <div class="card-box">
        <img src="../../assets/images/3.png" />
      </div>
      <div class="card-box">
        <img src="../../assets/images/4.png" />
      </div>
      <div class="card-box">
        <img src="../../assets/images/5.png" />
      </div>
      <div class="card-box">
        <img src="../../assets/images/6.png" />
      </div>
    </div>
  </div>
</div>

less(此代码less版本为4.*,其他版本less自行替换语法,防止报错[Unrecognised input]):

body {
  background-color: rgb(16, 14, 14);
}

/* ------------------------------------
 * 注释一:设置父级div显示在屏幕正中间
 * ------------------------------------
 */
.visual-card {
  position: absolute;
  bottom: 375px;
  width: 100%;
  left: 50%;
  transform: translateX(-50%);
}

.contain {
  margin: 0;
  padding: 0;
  /* 设置flex布局 */
  display: flex;
  /* 在主轴上居中 */
  justify-content: center;
  /* 在测轴上居中 */
  align-items: center;
  height: 100%;
  width: 100%;
  /* ------------------------------------
   * 注释五a:透视
   * ------------------------------------
   */
  perspective: 900px;
}

.section-box {
  width: 300px;
  height: 400px;
  position: relative;
  /* ------------------------------------
   * 注释五b:所有子元素在3D空间中呈现
   * ------------------------------------
   */
  transform-style: preserve-3d;

  /* ------------------------------------
   * 注释二:设置图片位置及样式
   * ------------------------------------
   */
  .card-box {
    position: absolute !important;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    display: flex;
    vertical-align: middle;
    /* 图片居中 */
    text-align: center;

    /* ------------------------------------
     * 注释三:设置图片倒影
     * ------------------------------------
     */
    -webkit-box-reflect: below 15px linear-gradient(transparent 10%, rgba(255, 255, 255, 0.3));

    /* ------------------------------------
     * 注释六:添加除animation-name外的animation属性
     * ------------------------------------
     */
    animation-duration: 15s;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    animation-delay: 3s;

    img {
      width: 300px;
      height: 400px;
    }
  }
}

/* 以下为animation的关键代码 */
@transforms: rotateY(45deg) translateZ(-800px),
             rotateY(45deg) translateZ(-600px),
             rotateY(45deg) translateZ(-400px),
             rotateY(-45deg) translateZ(-400px),
             rotateY(-45deg) translateZ(-600px),
             rotateY(-45deg) translateZ(-800px);

/* ------------------------------------
 * 注释七:设置每个图片的关键动画帧
 * ------------------------------------
 */
// 1. 循环生成@keyframes的核心函数(Less 4.x 兼容版)
.generate-keyframes(@index) when (@index <= 6) {
  @keyframe-name: ~"dh@{index}";
  // 计算六个阶段transform的索引(核心:超出长度自动回绕到1)
  @len: length(@transforms);
  // 0% 阶段索引 = 当前循环索引
  @start-index: @index;
  // 16.66% 阶段索引:当前+1,若等于长度+1则回绕到1
  @second-index: if(@index + 1 > @len, 1, @index + 1);
  // 33.32% 阶段索引:当前+2,若超出长度则取模回绕
  @third-index: if(@index + 2 > @len, @index + 2 - @len, @index + 2);
  // 49.98% 阶段索引:当前+3,若超出长度则取模回绕
  @fourth-index: if(@index + 3 > @len, @index + 3 - @len, @index + 3);
  // 66.64% 阶段索引:当前+4,若超出长度则取模回绕
  @fifth-index: if(@index + 4 > @len, @index + 4 - @len, @index + 4);
  // 83.3% 阶段索引:当前+5,若超出长度则取模回绕
  @end-index: if(@index + 5 > @len, @index + 5 - @len, @index + 5);
  
  // 通过变量引用生成@keyframes(Less 4.x 识别此写法)
  @keyframes @keyframe-name {
    // 自定义动画逻辑
    0% {
      transform: extract(@transforms, @start-index);
    }

    8.33% {
      transform: extract(@transforms, @start-index);
    }

    16.66% {
      transform: extract(@transforms, @second-index);
    }

    24.99% {
      transform: extract(@transforms, @second-index);
    }

    33.32% {
      transform: extract(@transforms, @third-index);
    }

    41.65% {
      transform: extract(@transforms, @third-index);
    }

    49.98% {
      transform: extract(@transforms, @fourth-index);
    }

    58.31% {
      transform: extract(@transforms, @fourth-index);
    }

    66.64% {
      transform: extract(@transforms, @fifth-index);
    }

    74.97% {
      transform: extract(@transforms, @fifth-index);
    }

    83.3% {
      transform: extract(@transforms, @end-index);
    }

    91.63% {
      transform: extract(@transforms, @end-index);
    }

    100% {
      transform: extract(@transforms, @start-index);
    }
  }

  // 递归自增,完成6次循环
  .generate-keyframes(@index + 1);
}
.generate-keyframes(1);

.card-box-loop(@index) when (@index <= 6) {
  // 生成:nth-child选择器(插值语法兼容所有版本)
  .card-box:nth-child(@{index}) {
    /* ------------------------------------
     * 注释四:设置每个图片的transform属性值
     * ------------------------------------
     */
    transform: extract(@transforms, @index);

    /* ------------------------------------
     * 注释八:赋值动画名
     * ------------------------------------
     */
    animation-name: ~"dh@{index}"
  }
  // 递归自增(循环核心)
  .card-box-loop(@index + 1);
}

// 3. 启动循环(从第1个开始)
.card-box-loop(1);

接下来一步步解析如何实现
1、设置父级div显示在屏幕正中间(注释一代码),效果如图:

2、设置图片位置及样式,全部居于正中间重叠(注释二代码),效果如图:

3、给图片设置倒影(注释三代码),丰富画面,效果如图:

4、设置每个图片的transform属性值,让图片在动画执行的第一帧位置(注释四代码),效果如图

5、此时所有的图片都在一个2D的空间上展示,效果不明显,若要在3D空间展示,需要在父元素上添加样式属性
perspective(注释五.a代码),子元素添加样式属性transform-style(注释五.b代码),添加后即可看见在Y、Z轴上的旋转,效果如下:

6、给每个需要旋转动画的元素添加上除动画名外的动画属性(注释六代码);
7、设置animation-name(注释七代码)。
8、将animation-name赋值给对应的元素(注释八代码)。

此效果关键是需要使用perspective配合transform-style: preserve-3d实现旋转交互,其兼容性以及设置倒影box-reflect兼容性如下:


简述box-reflect语法

box-reflect:direction(方向)、offset(偏移量)和mask-box-image(遮罩图像)
direction:定义倒影的方向,决定了倒影相对于原元素的位置。
            可选值:above(上方)、below(下方)、left(左侧)和right(右侧)。
offset:定义倒影与原元素之间的偏移距离。
            可以是数值,百分比,百分比是根据原元素的尺寸来计算的。
            偏移量可以为负值,表示倒影会向相反方向偏移。
mask-box-image:定义遮罩图像,用于覆盖在倒影上,创造出不同的视觉效果。
            可以是绝对或相对地址指定的图像,线性渐变或径向渐变创建的图像。
            如果不设置该值,则倒影将没有遮罩。

以上。

posted @ 2025-12-06 19:29  显示昵称已被使用#  阅读(7)  评论(0)    收藏  举报