css 实现伪3D魔方动态效果

2,824 阅读2分钟

有次老板在打王者荣耀,正好看到王者荣耀有个魔方的炫酷效果(现在找不到图了),就让我们把这魔方搬到项目中。王者荣耀那是三维的啊,我们为了一个魔方效果就用webgl,岂不有点大材小用?然后就尝试着用纯css去实现一个魔方,接着就有了下面这样的效果,相比真正的三维,只能说差强人意吧。

效果展示

gif图片效果展示:

20211222_163202.gif

线上效果展示:伪3D魔方

制作魔方

接下来就开始制作魔方

实现单个小方块

仔细看我们这个二阶魔方,是由8个小方块组成的,所以我们第一步是先实现一个小方块。小方块有6个面,所以我们可以用6个div来拼成一个小方块。

  <div class="cube-inner">
    <div class="cube-face cube-up">up</div>
    <div class="cube-face cube-down">down</div>
    <div class="cube-face cube-left">left</div>
    <div class="cube-face cube-right">right</div>
    <div class="cube-face cube-before">before</div>
    <div class="cube-face cube-after">after</div>
  </div>

设置容器为3d舞台transform-style: preserve-3d;

$width: 80px;
$color: #02FFF6;
$color1: #98fdf0;
$lightColor: #B6FFF5;
  .cube-inner {
    width: $width;
    height: $width;
    position: relative;
    transform-style: preserve-3d;
    transform: rotateX(20deg) rotateZ(45deg); // 为了方便看到小方块三面
    background: white;
  }

给小方块的面div设置样式

.cube-face {
    position: absolute;
    width: 100%;
    height: 100%;
    background: linear-gradient(0deg, rgba($color,0.7),rgba($color1,0.8));
    text-align: center;
    line-height: $width;
  }

设置完就是下图这样的效果,6个div现在重叠在一起。

image.png

现在就开始变换6个面的位置,我们以before面为参考面,后面所有面的变换都是相对于before面的。设置before面:

  .cube-before {
    left: 0;
    top: 0;
  }

然后设置after面,after面相对于before面有两个变换:

  • 在z轴上移动$width
  • 相对于x轴翻转180度
  .cube-after {
    left: 0;
    top: 0;
    transform: translateZ(-$width) rotateX(180deg);
  }

设置完为如下效果:

image.png

同理,我们设置up面和down面:

  .cube-up {
    left: 0;
    top: -$width;
    transform: rotateX(90deg);
    transform-origin: bottom;
  }
  .cube-down {
    left: 0;
    top: $width;
    transform-origin: top;
    transform: rotateX(-90deg);
  }

image.png

然后再设置left面和right面:

 .cube-left {
    left: -$width;
    top: 0;
    transform-origin: right;
    transform: rotateY(-90deg);
  }
  .cube-right {
    right: -$width;
    top: 0;
    transform-origin: left;
    transform: rotateY(90deg);
  }

image.png

这样我们单个小方块就制作完成了。

发光效果

想要小方块有点发光的效果,所以给div的边框颜色设置成荧光绿,并且利用每个面div的伪元素:before:after在每个角制作出光效。此外文字也可以隐藏了,我们就设置为透明色。

  .cube-face {
    position: absolute;
    width: 100%;
    height: 100%;
    background: linear-gradient(0deg, rgba($color,0.7),rgba($color1,0.8));
    background-blend-mode: screen;
    border: 1px solid $lightColor;
    box-shadow: inset 0 0 2px rgba($lightColor, 1), 0 0 2px rgba($lightColor, 1);
    color: transparent;
    &::before,
    &::after {
      content: '';
      position: absolute;
      top: -2px;
      width: 10px;
      height: 10px;
      border-color: $lightColor;
      border-style: solid;
      border-top-width: 2px;
      border-bottom-width: 0;
      box-shadow: inset 0 0 8px rgba($lightColor, 1), 0 0 8px rgba($lightColor, 1);
    }
    &::before {
      left: -2px;
      border-left-width: 2px;
      border-right-width: 0;
    }
    &::after {
      right: -2px;
      border-left-width: 0;
      border-right-width: 2px;
    }
  }

加上光效后的效果:

image.png

不像个正方体??

我们让它旋转起来再看

  .cube-inner {
    ...
    animation: xuanzhan 10s linear infinite;
  }
   @keyframes xuanzhan {
    0% {
      transform: rotateX(0deg) rotateY(0deg);
    }
    100% {
      transform: rotateX(360deg) rotateY(360deg);
    }
  }

20211222_155212.gif

嗯...每一面都是方的。

实现二阶魔方

二阶魔方由8个这样的小方块组成,一个小方块是一个组件(Magic),那我们引用8次,再调整每个的位置就能达到想要的效果了

  <div class="magic-container">
    <Magic class="magic0" />
    <Magic class="magic1" />
    <Magic class="magic2" />
    <Magic class="magic3" />
    <Magic class="magic4" />
    <Magic class="magic5" />
    <Magic class="magic6" />
    <Magic class="magic7" />
  </div>

用绝对定位来调整位置,距离看着合适就差不多了

.magic0 {
    top: -40%;
    left: 10%;
    z-index: 4;
  }
  .magic1 {
    top: -4%;
    left: -28%;
    z-index: 10;
  }
  .magic2 {
    top: -4%;
    left: 48%;
    z-index: 10;
  }
  .magic3 {
    top: 30%;
    left: 10%;
    z-index: 10;
  }
  .magic4 {
    top: -20%;
    left: 10%;
    z-index: 2;
  }
  .magic5 {
    top: 16%;
    left: -28%;
    z-index: 4;
  }
  .magic6 {
    top: 16%;
    left: 48%;
    z-index: 4;
  }
  .magic7 {
    top: 50%;
    left: 10%;
    z-index: 4;
  }

效果图:

image.png

实现魔方动态效果

我们给魔方加了两个动态效果

  • 自身旋转
  • 设置其中两个小方块向外扩展然后收回 自身旋转我们在上面已经提到过了
.magic-container{
    animation: xuanzhan 30s linear infinite;
    transform-style: preserve-3d;
}
@keyframes xuanzhan {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

小方块的动画也比较简单:

  .magic2 {
    top: -4%;
    left: 48%;
    z-index: 10;
    animation: translateMove1 10s infinite; 
    @keyframes translateMove1 {
      0%,
      60%,
      100% {
        transform: translateX(0) scale(0.6);
      }
      6%,
      40% {
        transform: translateX(24%) scale(0.7);
      }
    }
  }
  
  .magic3 {
    top: 30%;
    left: 10%;
    z-index: 10;
    animation: translateMove 10s infinite -4s;
    @keyframes translateMove {
      0%,
      60%,
      100% {
        transform: translateY(0) scale(0.6);
      }
      8%,
      50% {
        transform: translateY(24%) scale(0.7);
      }
    }
  }

20211222_161842.gif

实现流动线条

线条流动效果的原理也非常简单,就是用div实现的两个圆旋转的效果。 我们首先画个发光的圆:

 <div class="cir cir1" />
 <div class="cir cir2" />
  $angleX: 64deg;
  $angleY: 20deg;
  $bg: #02fff6;
  $lightBg: #b6fff5;
  .cir {
    width: 230px;
    height: 230px;
    border: 4px solid $lightBg;
    border-radius: 50%;
    box-shadow: 0 0 16px rgba($bg, 0.6), 0 0 16px rgba($bg, 0.6) inset, 0 0 10px $lightBg, 0 0 10px $lightBg inset;
  }

image.png

然后利用mask-image属性,让它有种渐变的效果

mask-image: linear-gradient(0deg, rgba(white, 0.9) 0%, rgba($lightBg, 0.7) 30%, rgba($bg, 0));

image.png

然后再添加变换属性和动画效果:

 .cir1 {
    animation: rotate 16s -3s infinite linear alternate;
  }
  @keyframes rotate {
    0%,
    100% {
      transform: rotateX($angleX) rotateY($angleY) rotate(0deg);
    }
    50% {
      transform: rotateX($angleX) rotateY($angleY) rotate(360deg);
    }
  }

同样cir2也是:

 .cir2 {
    transform: rotateX($angleX) rotateY(-$angleY);
    animation: rotate1 16s infinite linear;
  }
  @keyframes rotate1 {
    0%,
    100% {
      transform: rotateX($angleX) rotateY(-$angleY) rotate(0deg);
    }
    50% {
      transform: rotateX($angleX) rotateY(-$angleY) rotate(360deg);
    }
  }

最终效果就形成了

20211222_163202.gif

代码地址

demo使用vue写的,完整代码放在github上了:魔方代码

最后感谢WPS提供的屏幕录制功能,第一次用感觉超好用~~