查看原文
其他

如何用CSS3画出懂你的3D魔方?

苏南 画漫画的程序员 2022-06-11

点击上方'IT平头哥联盟 '关注这众号~



前言

  最近在写《每周动画点点系列》文章,上一期分享了< 手把手教你如何绘制一辆会跑车 >,本期给大家带来是结合CSS3画出来的一个 立体3d魔方,结合了 js让你随心所欲想怎么转,就怎么转,这里是 @IT·平头哥联盟,我是 首席填坑官∙苏南(South·Su),我们先来看看效果,然后再分解它的实现过程吧

 

绘制过程:

  好吧,gif图看着好像有点不是很清晰,想在线预览的同学,可在下方留言获取链接(微信不支持链接)👈,废话不多扯了,先来分析一下,看如何实现这个功能吧。

∙ API预热 :
  • 本次示例是一个立体的正方形,既然有立体效果,肯定少不了CSS3中的 -webkit-perspective-透视、 preserve-3d-三维空间,这个两个是重点哦,当然还有 transform-origin、 transition、 transform等,先来回故一下 API 怎么是讲的吧:

perspective 取值 :

  • none :不指定透视 ;

  • length :指定观察者与「z=0」平面的距离,使具有三维位置变换的元素产生 透视效果。「z>0」的三维元素比正常大,而「z<0」时则比正常小,大小程度由该属性的值决定,不允许负值。

transform-style 取值 :

  • flat :指定子元素位于此元素所在平面内;

  • preserve-3d :指定子元素定位在三维空间内,当该属性值为 preserve-3d时,元素将会创建局部堆叠上下文;

  • 小结 :决定一个变换元素看起来是处在三维空间还是平面内,需要该元素的父元素上定义 <' transform-style '> 属性,也就是说想某元素有三维效果,需要设定它的父级有 preserve-3d

transform-origin 取值 :

  • percentage:用百分比指定坐标值。可以为负值;

  • length:用长度值指定坐标值。可以为负值;

  • left:指定原点的横坐标为left;

  • center①:指定原点的横坐标为center;

  • right:指定原点的横坐标为right;

  • top:指定原点的纵坐标为top;

  • center②:指定原点的纵坐标为center;

  • bottom:指定原点的纵坐标为bottom;

  • transform、transition等,就不介绍了

  1. /* perspective 使用示例:*/

  2. div{

  3.  -webkit-perspective:600px;

  4.  perspective:600px;

  5. }

  6. /*transform-style 使用示例:*/

  7. .preserve{

  8.  transform-style:preserve-3d;

  9.  -webkit-transform-style:preserve-3d;

  10. }

  11.  /*transform-origin 使用示例:*/

  12. .preserve{

  13.  -webkit-transform-origin:50% 50% -100px; or

  14.  -webkit-transform-origin:bottom; or

  15.  -webkit-transform-origin:top;

  16.  …………

  17. }

∙ 绘制6个面 :
  • 是的,我没有说错,就是6个面:上、正面、下、背面、左、右,

  • 上面API讲了这么多,来实践试一下吧,写6个div,结构大概是这样的,也是接下来的魔方需要的结构:

  1. <div class="cube">

  2.    <div class="cube-inner running">

  3.        <p class="single-side s1"><span>最</span></p>

  4.        <p class="single-side s2"><span>懂</span></p>

  5.        <p class="single-side s3"><span>你</span></p>

  6.        <p class="single-side s4"><span>的</span></p>

  7.        <p class="single-side s5"><span>魔</span></p>

  8.        <p class="single-side s6"><span>方</span></p>

  9.    </div>

  10. </div>

  • !!!发生了什么??是不是很吃惊??说好的值越大,透视效果越强的呢?后面明明藏了个妹子,怎么看没有透视出来?

  • 开始我也是跟你一样吃惊的,但瞬间就悟透了,少了 rotate,加个它再来看看效果吧:.


  1. cube{

  2.    width:200px;

  3.    height:200px;

  4.    margin:10px auto;

  5.    padding:260px;

  6.    position:relative;

  7.    -webkit-perspective:600px;

  8.    perspective:600px;

  9.    transition: .5s ;

  10. }

  11. .cube-inner{

  12.    width:200px;

  13.    height:200px;

  14.    position:relative;

  15.    -webkit-transform-style:preserve-3d;

  16.    transition:.3s;

  17.    -webkit-transform-origin:50% 50% -100px;

  18.    transform: rotateX(45deg);

  19. }

  20. .cube:hover{

  21.    /*鼠标经过时,把 perspective 过渡到100 */

  22.    -webkit-perspective:100px;

  23.    perspective:100px;

  24. }

  • 既然API有效,那么拉下来我们就画出6个面吧,按:上、正面、下、背面、左、右,这个顺序来设置吧;

  • 首先,我们要指定它们是在三维空间内的 preserve-3d,也就是6个面的父级要设置 transform-style 样式;

  • 以上都设置好后,再来看看6个面吧,为了便于区分,给它们每个都设置了不同颜色(用了css3的渐变 radial-gradient)——不想手写的同学推荐一个网站可在线设置你要的效果,复制样式即可,先来一睹风采,为了便于观察,整体角度旋转了10deg:

  • 说到渐变,偶然之间发现了一个有意思的东西 hue-rotate,它能在你初始的颜色基础上旋转元素的色调及其内容,从而达到不同的效果。了解更多

hue-rotate : The hue-rotate() CSS function rotates the hue of an element and its contents. Its result is a .

  • * 上 * - " ":

  1. .cube-inner .single-side.s1{

  2.    /*s1顶部*/

  3.    left:0;top:-200px;

  4.    background: radial-gradient(circle, rgba(255,255,255,.88), #00adff);

  5.    background: -webkit-radial-gradient(circle, rgba(255,255,255,.88), #00adff);

  6.    transform-origin:bottom;

  7.    -webkit-transform-origin:bottom;

  8.    transform:rotateX(90deg);

  9.    -webkit-transform:rotateX(90deg);

  10. }

  • * 正面 * - " ":

  • 下面就是默认的,什么都不用设置,所以就不展示了 ;

  • * 下面 * - " ":

  • 即底部,底部的设置,正好跟顶部它是相反的,一个origin 以 bottom为基准为坐标,一个以top为基准为坐标;

  1. .cube-inner .single-side.s3{

  2.    /*s3底部*/

  3.    left:0;top:200px;

  4.    background: radial-gradient(circle, rgba(255,255,255,.88), #100067);

  5.    background: -webkit-radial-gradient(circle, rgba(255,255,255,.88), #100067);

  6.    transform-origin:top;

  7.    -webkit-transform-origin:top;

  8.    transform:rotateX(-90deg);

  9.    -webkit-transform:rotateX(-90deg);

  10. }

  • * 背面 * - " "

  • 即正面的后边,整体旋转了 135deg,让背面更直观能看到;

  • translateZ 、rotateX 同时移动,形成透视的关系,让它看起来,在正面面的后面;

  • 下图二,把默认的正面,设置了透明度,可以看出,背面的透视效果;

  1. .cube-inner .single-side.s4{

  2.    /*s4背部*/

  3.    z-index:2;

  4.    left:0;top:0;

  5.    background: radial-gradient(circle, rgba(255,255,255,.88), #F0C);

  6.    background: -webkit-radial-gradient(circle, rgba(255,255,255,.88), #F0C);

  7.    transform:translateZ(-200px) rotateX(180deg) ;

  8.    -webkit-transform:translateZ(-200px) rotateX(180deg) ; /*rotateZ(-180deg) 左右旋转的时候,Z轴旋转180°,因为字是倒着的*/

  9. }

  • * 侧面 * - " "

  • origin以right为基准,left负元素的宽度,rotateY轴旋转90deg;

  1. .cube-inner .single-side.s5{

  2.    /*s5左侧*/

  3.    left:-200px;top:0;

  4.    background: radial-gradient(circle, rgba(255,255,255,.88),rgba(33,33,33,1));

  5.    background: -webkit-radial-gradient(circle, rgba(255,255,255,.88),rgba(33,33,33,1));

  6.    transform-origin:right;

  7.    -webkit-transform-origin:right;

  8.    transform:rotateY(-90deg)

  9.    -webkit-transform:rotateY(-90deg)

  10. }

  • * 右侧面 * - " ":

  • 同理右侧,与左侧正好相反;

  1. .cube-inner .single-side.s6{

  2.    /*s6右侧*/

  3.    right:-200px;top:0;

  4.    transform-origin:left;

  5.    -webkit-transform-origin:left;

  6.    background: radial-gradient(circle, rgba(255,255,255,.88), #f00);

  7.    background: -webkit-radial-gradient(circle, rgba(255,255,255,.88), #f00);

  8.    transform:rotateY(90deg);

  9.    -webkit-transform:rotateY(90deg);

  10. }

小结 : 嗯,以上魔方的6个面的绘制过程,基本已经完成,主要在在于 transform-origin、 rotate、 translate等属性的应用,但为了让它更炫酷一些,我们还要给边角加一些光感。

∙ 添加高光 :
  • 细心的宝宝,前面的布局应该已经发现了,每一行布局的 p标签里,都多套了一层 span,就是为高光光感,埋下的伏笔,一个平面正方形有四个边,after、before只有两,那么肯定要再套一层,当然方法很多,比如直接用border也是可以的,但比较麻烦,我就选择了现在要讲的这种:

  • after、before设置1px的边框,设置一个线性渐变,中间是白色,两断是过渡到透明的,这样高光就有了,来看一组图吧:


∙ CSS 360°旋转 :
  • 上面是一个鼠标经过的过渡动画,可以看出立体效果是已经有了,接下来就写一个CSS animation的动画,让它360度旋转,每个角都能看到,这样会显的很666;

  • animation 配合 keyframes 使用,请看代码示例:

  1. .cube .cube-inner{

  2.  /*-webkit-transform:rotateX(180deg) rotateY(0deg) ;*/

  3.  animation: elfCube 10s infinite ease-in-out;

  4.  -webkit-animation: elfCube 10s infinite ease alternate;

  5. }

  6. @keyframes elfCube {

  7.  0% {

  8.    transform: rotateX(0deg) rotateY(0deg);

  9.  }

  10.  50% {

  11.    transform: rotateX(360deg) rotateY(360deg);

  12.  }

  13.  100% {

  14.    transform: rotateX(0deg) rotateY(0deg);

  15.  }

  16. }

  17. @-webkit-keyframes elfCube {

  18.  0% {

  19.   -webkit-transform: rotateX(0deg) rotateY(0deg);

  20.  }

  21.  50% {

  22.   -webkit-transform: rotateX(360deg) rotateY(360deg);

  23.  }

  24.  100% {

  25.    transform: rotateX(0deg) rotateY(0deg);

  26.  }

  27. }

∙ 跟随鼠标旋转 :
  • 说好的随着鼠标旋转呢??

  • 别慌,接下来就是带你装逼,带你飞的时候,

  • 首先我们要了解,鼠标在容器内所在的位置,X = e.pageX - ele.offsetLeft, Y = e.pageY - ele.offsetTop;

  • 同时要知道元素内的中心点:centerX = width/2,centerY =height/2;

  • 然后得出值:axisX = X - centerX,axisY = Y - centerY;

  • PS : 开始尝试想的是鼠标从哪个方向进入,得到它的角度,但发现旋转效果不明显 ,有兴趣的同学可以尝试一下:(((Math.atan2(Y, X) * (180 / Math.PI)) + 180) / 90),参考司徒大神的JS判断鼠标从什么方向进入一个容器;

  • 最后,给容器绑上事件: mouseover、 mousemove、 mouseout,鼠标进入时,暂停css的动画,不然会相互打架哦!



  1. ……

  2. getAxisX(e){

  3.  let left = this.cubeEle.offsetLeft;

  4.  return e.pageX - left - (this.cubeW/2) * (this.cubeW>this.cubeH ? this.cubeH/this.cubeW : 1);

  5. }

  6. getAxisY(e){

  7.  let top = this.cubeEle.offsetTop;

  8.  return e.pageY - top - (this.cubeH/2) * (this.cubeH>this.cubeW ? this.cubeW/this.cubeH : 1);

  9. }

  10. …………

  1. …………

  2. run(){

  3.  this.cubeEle.addEventListener('mouseover',(e)=>this.hoverOut(e),false);

  4.  this.cubeEle.addEventListener('mousemove',(e)=>this.move(e),false);

  5.  this.cubeEle.addEventListener('mouseout',(e)=>this.hoverOut(e),false);

  6. }

  7. hoverOut(e){

  8.  //进入/离开

  9.  e.preventDefault();

  10.  this.axisX = this.getAxisX(e),

  11.  this.axisY = this.getAxisY(e);

  12.  if(e.type == 'mouseout'){ //离开

  13.    this.axisX=0;

  14.    this.axisY = 0;

  15.    console.log("离开")

  16.    this.cubeInner.className="cube-inner running";

  17.  }else{

  18.    this.cubeInner.className="cube-inner";

  19.    console.log("进入")

  20.  };

  21.  let rotate = `rotateX(${-this.axisY}deg) rotateY(${-this.axisX}deg)`;

  22.  this.cubeInner.style.WebkitTransform = this.cubeInner.style.transform = rotate;

  23. }

  24. ……

结尾:

  • -webkit-perspective,

  • -webkit-transform-style,

  • -webkit-transform-origin,

  • radial-gradient、linear-gradient,

  • transform:rotate、translate、scale,

  • transition,

  • animation;

  • 以上就是今天为大家带来的分享,以及使用到的知识点的API,如文章中有不对之处,烦请各位大神斧正,

  • 文章源码获取/在线预览,请在下方留言,微信不支持添加链接 👈


作者说
您好,我是苏南,非常感谢您认真的看完这遍文章,如果您觉得文章不错,请记得点个赞,并关注下方的 公众号,支持一下我们!用心分享 做有温度的攻城狮

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存