Skip to content
本页目录

纯css 3D立体加载动画

基础知识以及参数示例

虽然css能够实现3d动画,但是在实际开发中运用较少,对于部分基础css知识,会存在部分盲区,所以需要提前知道一下知识点,用于方便理解。

示例-1

当前示例虽然实现了正方形六面的3d,但是由于容器有背景参照,能够清楚的看出,容器是提供给六个面3d空间,它本身处于2地平面空间

<template>
  <div class="custom-wrapper">
    <div class="container-1">
      <div class="cube-1">
        <div class="cube-item front">
          1
        </div>
        <div class="cube-item back">
          2
        </div>
        <div class="cube-item right">
          3
        </div>
        <div class="cube-item left">
          4
        </div>
        <div class="cube-item top">
          5
        </div>
        <div class="cube-item bottom">
          6
        </div>
      </div>
    </div>
  </div>
</template>

<style lang='scss' scoped>
.custom-wrapper {
  --w: 100px;
  .container-1 {
    position: relative;
    .cube-1 {
      position: relative;
      width: 100%;
      height: 500px;
      background-color: rgba(black, 0.5);
      backface-visibility: visible;
      perspective-origin: 100% 0%;
      transform-style: preserve-3d;
      perspective: 500px;
      text-align: center;
      display: flex;
      align-items: center;
      justify-content: center;
      color: white;
      text-align: center;
      line-height: 100px;
      .cube-item {
        position: absolute;
        width: var(--w);
        height: var(--w);
        border-radius: 10px;
        border: 2px solid rgba(white, 0.9);
        background-color: rgba(white, 0.3);
      }
      .front {
        background: rgba(0, 0, 0, 0.3);
        transform: translateZ(56px);
      }

      .back {
        background: rgba(0, 255, 0, 1);
        color: black;
        transform: rotateY(180deg) translateZ(56px);
      }

      .right {
        background: rgba(196, 0, 0, 0.7);
        transform: rotateY(90deg) translateZ(56px);
      }

      .left {
        background: rgba(0, 0, 196, 0.7);
        transform: rotateY(-90deg) translateZ(56px);
      }

      .top {
        background: rgba(196, 196, 0, 0.7);
        transform: rotateX(90deg) translateZ(56px);
      }

      .bottom {
        background: rgba(196, 0, 196, 0.7);
        transform: rotateX(-90deg) translateZ(56px);
      }
    }
  }

}
</style>

示例-2

为了防止容器层处于2D平面,我们再加一层, 一下以codesandbox的cube loading为例

<template>
  <div class="custom-wrapper">
    <div class="container-2">
      <div class="cube-2">
        <div class="slides">
          <div class="cube-item top" />
          <div class="cube-item right" />
          <div class="cube-item bottom" />
          <div class="cube-item left" />
          <div class="cube-item front" />
          <div class="cube-item back" />
        </div>
      </div>
    </div>
  </div>
</template>

<style lang='scss' scoped>
.custom-wrapper {
  --w: 100px;

  @keyframes rotate {
    0% {
      transform: rotateX(-37.5deg) rotateY(45deg);
    }
    50% {
      transform: rotateX(-37.5deg) rotateY(405deg);
    }
    100% {
      transform: rotateX(-37.5deg) rotateY(405deg);
    }
  }
  @keyframes top-animation {
    0% {
      opacity: 1;
      transform: rotateX(90deg) translateZ(100px);
    }
    20% {
      opacity: 1;
      transform: rotateX(90deg) translateZ(48px);
    }
    70% {
      opacity: 1;
      transform: rotateX(90deg) translateZ(48px);
    }
    90% {
      opacity: 1;
      transform: rotateX(90deg) translateZ(100px);
    }
    100% {
      opacity: 1;
      transform: rotateX(90deg) translateZ(100px);
    }
  }

  @keyframes bottom-animation {
    0% {
      opacity: 1;
      transform: rotateX(-90deg) translateZ(100px);
    }
    20% {
      opacity: 1;
      transform: rotateX(-90deg) translateZ(48px);
    }
    70% {
      opacity: 1;
      transform: rotateX(-90deg) translateZ(48px);
    }
    90% {
      opacity: 1;
      transform: rotateX(-90deg) translateZ(100px);
    }
    100% {
      opacity: 1;
      transform: rotateX(-90deg) translateZ(100px);
    }
  }

  @keyframes front-animation {
    0% {
      opacity: 1;
      transform: rotateY(0deg) translateZ(var(--w));
    }
    20% {
      opacity: 1;
      transform: rotateY(0deg) translateZ(48px);
    }
    70% {
      opacity: 1;
      transform: rotateY(0deg) translateZ(48px);
    }
    90% {
      opacity: 1;
      transform: rotateY(0deg) translateZ(var(--w));
    }
    100% {
      opacity: 1;
      transform: rotateY(0deg) translateZ(var(--w));
    }
  }

  @keyframes back-animation {
    0% {
      opacity: 1;
      transform: rotateY(-180deg) translateZ(var(--w));
    }
    20% {
      opacity: 1;
      transform: rotateY(-180deg) translateZ(48px);
    }
    70% {
      opacity: 1;
      transform: rotateY(-180deg) translateZ(48px);
    }
    90% {
      opacity: 1;
      transform: rotateY(-180deg) translateZ(var(--w));
    }
    100% {
      opacity: 1;
      transform: rotateY(-180deg) translateZ(var(--w));
    }
  }
  @keyframes left-animation {
    0% {
      opacity: 1;
      transform: rotateY(-90deg) translateZ(var(--w));
    }
    20% {
      opacity: 1;
      transform: rotateY(-90deg) translateZ(48px);
    }
    70% {
      opacity: 1;
      transform: rotateY(-90deg) translateZ(48px);
    }
    90% {
      opacity: 1;
      transform: rotateY(-90deg) translateZ(var(--w));
    }
    100% {
      opacity: 1;
      transform: rotateY(-90deg) translateZ(var(--w));
    }
  }
  @keyframes right-animation {
    0% {
      opacity: 1;
      transform: rotateY(90deg) translateZ(var(--w));
    }
    20% {
      opacity: 1;
      transform: rotateY(90deg) translateZ(48px);
    }
    70% {
      opacity: 1;
      transform: rotateY(90deg) translateZ(48px);
    }
    90% {
      opacity: 1;
      transform: rotateY(90deg) translateZ(var(--w));
    }
    100% {
      opacity: 1;
      transform: rotateY(90deg) translateZ(var(--w));
    }
  }

  .container-2 {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: row;
    background-color: #151515;
    padding: 60px 0;
    height: 400px;
    .cube-2 {
      position: relative;
      width: var(--w);
      height: var(--w);
      * {
        width: var(--w);
        height: var(--w);
        position: absolute;
      }
      .slides {
        animation: rotate 3s ease infinite;
        animation-delay: 0.8s;
        transform-style: preserve-3d;
        transform: rotateX(-37.5deg) rotateY(45deg);

        .cube-item {
          border: 1px solid white;
          border-radius: 8px;
          background: rgba(white, 0.1);
        }
        .top {
          animation: top-animation 3s ease infinite;
          animation-delay: 0ms;
          transform: rotateX(90deg) translateZ(var(--w));
          animation-fill-mode: forwards;
          transform-origin: 50% 50%;
        }
        .bottom {
          animation: bottom-animation 3s ease infinite;
          animation-delay: 0ms;
          transform: rotateX(-90deg) translateZ(var(--w));
          animation-fill-mode: forwards;
          transform-origin: 50% 50%;
        }
        .front {
          animation: front-animation 3s ease infinite;
          animation-delay: 100ms;
          transform: rotateY(0deg) translateZ(var(--w));
          animation-fill-mode: forwards;
          transform-origin: 50% 50%;
        }

        .back {
          animation: back-animation 3s ease infinite;
          animation-delay: 100ms;
          transform: rotateY(-180deg) translateZ(var(--w));
          animation-fill-mode: forwards;
          transform-origin: 50% 50%;
        }
        .left {
          animation: left-animation 3s ease infinite;
          animation-delay: 100ms;
          transform: rotateY(-90deg) translateZ(var(--w));
          animation-fill-mode: forwards;
          transform-origin: 50% 50%;
        }
        .right {
          animation: right-animation 3s ease infinite;
          animation-delay: 100ms;
          transform: rotateY(90deg) translateZ(var(--w));
          animation-fill-mode: forwards;
          transform-origin: 50% 50%;
        }
      }
    }
  }
}
</style>

示例-3

<script lang='ts' setup>
const classes = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
</script>

<template>
  <div class="container">
    <div class="cubescaler">
      <div class="cube">
        <div v-for="i in classes" :key="i" class="main" :class="i">
          <div class="offset">
            <div class="slide front" />
            <div class="slide back" />
            <div class="slide left" />
            <div class="slide right" />
            <div class="slide top" />
            <div class="slide bottom" />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<style lang='scss' scoped>
@keyframes animation-b {
  from {
    transform: translate3d(83px, 46px, 0) scale(1);
    opacity: 0;
  }
  33.33% {
    transform: translate3d(53px, 26px, 0px) scale(1);
    opacity: 1;
  }
  66.66% {
    transform: matrix(1,0, 0, 1, 53, 26);
    opacity: 1;
  }
  to {
    transform: matrix(1,0, 0, 1, 53, 26);
    opacity: 0;
  }
}

@keyframes animation-c {
  from {
    transform: translate3d(-83px, 44px, 0);
    opacity: 1;
  }
  33.33% {
    transform: translate3d(-53px, 24px, 0px);
  }
  66.66% {
    transform: translate3d(-53px, 24px, 0px) scale(1);
    opacity: 1;
  }
  to {
    transform: translate3d(-26px, 12px, 0px) scale(0.5);
    opacity: 0;
  }
}
.container {
  --w: 75px;
  --w-h: 37.5px;
  --bg-color: rgba(102, 204, 51, 0.6);
  position: relative;
  width: 100%;
  display: flex;
  flex-direction: column;
  background-color: #151515;
  .cubescaler {
    transform: matrix(0.75, 0, 0, 0.75, 0, 0);
  }
  .cube {
    display: flex;
    align-items: center;
    position: relative;
    width: 100%;
    justify-content: center;
    height: 250px;
    .main {
      position: absolute;
      width: var(--w);
      height: var(--w);
      transform-style: preserve-3d;
      will-change: transform;
      transform-origin: 50% 50%;
      .offset {
        transform-style: preserve-3d;
        will-change: transform;
        transform-origin: 50% 50%;
        transform: rotateY(48deg) rotateX(-20deg) rotateZ(-20deg);
        * {
          position: absolute;
          width: var(--w);
          height: var(--w);
          background-color: var(--bg-color);
          box-shadow: var(--bg-color) 0px;
          border: 1px solid rgba(white, 0.6);
          transform-origin: 50% 50%;
          will-change: transform;
        }

        .top {
          transform: rotateX(90deg) translateZ(var(--w-h));
        }
        .bottom {
          transform: rotateX(-90deg) translateZ(var(--w-h));
        }
        .right {
          transform: rotateY(0deg) translateZ(var(--w-h));
        }
        .left {
          transform: rotateY(-180deg) translateZ(var(--w-h));
        }
        .front {
          transform: rotateY(-90deg) translateZ(var(--w-h));
        }
        .back {
          transform: rotateY(90deg) translateZ(var(--w-h));
        }
      }

      &.b {
        transform: matrix(1,0, 0, 1, 83, 46);
        // transform: translate3d(53px, 26px, 0px);
        animation: 1.8s ease-in-out infinite animation-b;
      }
      &.c {
        transform: translate3d(-53px, 24px, 0px);
        animation: 1.8s ease-in-out infinite animation-c;
      }
      &.d {
        // transform: translate3d(0px, -84px, 0);
        transform: translate3d(0px, -66px, 0px);
        // opacity: 0;
      }
      &.e {
        // transform: translate3d(0, 70px, 0);
        transform: translate3d(0px, 50px, 0px);
        // opacity: 0;
      }
      &.f {
        // transform: translate3d(-72px, -62px, 0);
        transform: translate3d(-52px, -42px, 0px);
        // opacity: 0;
      }
      &.g {
        // transform: translate3d(73px, -61px, 0);
        transform: translate3d(53px, -41px, 0px);
        // opacity: 0;
      }
      &.h {
        // transform: translate3d(0px, -42px, 0);
        transform: translate3d(0px, -16px, 0px);
        // opacity: 0;
      }
    }
  }
}
</style>

这个动画我写不好