尝试一下用 CSS 绘制简略的 3d 图形,比方一个掘金 logo?

相比 2d 绘制,3d 有哪些须要留神的小细节呢?一起看看吧

一、金字塔形/四棱锥形

除去挖空局部,整个形状其实是一个金字塔形,或者叫四棱锥形(四角锥)

一共有5个面,所以咱们能够筹备5个元素

<juejin>  <pane a></pane>  <pane b></pane>  <pane c></pane>  <pane d></pane>  <bottom></bottom></juejin>

其中,bottom示意底部的正方形,其余4个示意侧面4个三角形。

首先先从正方体开始

留神,须要出现 3d 视觉,须要增加transform-style: preserve-3d,先绘制一个侧面

juejin{  --s: 200px;  position: relative;  width: var(--s);  height: var(--s);  transform-style: preserve-3d;  perspective: 3000px;  transform: rotateX(calc( .3 * -90deg)) rotateY(calc( .1 * 90deg));}pane{  position: absolute;  inset: 13.3% 0 0;  transform-origin: center bottom;}pane[a]{  background-color: #368dff;  transform: translate3d(0, 0, calc(var(--s) * -0.5));}

成果如下

用同样的形式,绘制出4个侧面

pane[b]{  background-color: #368dff;  transform: translate3d(0, 0, calc(var(--s) * 0.5));}pane[c]{  background-color: #1e80ff;  transform: translate3d(calc(var(--s) * -0.5), 0, 0) rotateY(90deg);}pane[d]{  background-color: #1e80ff;  transform: translate3d(calc(var(--s) * 0.5), 0, 0) rotateY(90deg);}

成果如下

而后,将4个侧面裁剪成三角形,用clip-path就行了

pane{  /**/  clip-path: polygon(50% 0, 100% 100%, 0 100%);}

成果如下

最初,将4个侧面沿着底部旋转一个角度,使其合并在一起,因为大小齐全一样,底部又是正方形,歪斜角度天然也雷同,这里就不通过数学计算了,间接用一个 CSS 变量去实时比对

juejin{  /**/   --deg: 35.3deg;}pane[a]{  /**/  transform: translate3d(0, 0, calc(var(--s) * -0.5)) rotateX(calc(var(--deg) * -1));}pane[b]{  /**/  transform: translate3d(0, 0, calc(var(--s) * 0.5)) rotateX(var(--deg));}pane[c]{  /**/  transform: translate3d(calc(var(--s) * -0.5), 0, 0) rotateY(90deg) rotateX(calc(var(--deg) * -1));}pane[d]{  /**/  transform: translate3d(calc(var(--s) * 0.5), 0, 0) rotateY(90deg) rotateX(var(--deg));}

成果如下

这样就失去了一个金字塔形/四棱锥形

二、侧边镂空和横截面解决

后面失去了一个残缺的四棱锥,当初须要先将侧面镂空。

这里有两种形式,第一,能够用通明和不通明的突变填充,这样通明的局部就是镂空的了;还有一种形式,用mask遮罩,也能轻易的实现

pane{  /*  */  -webkit-mask: linear-gradient(to bottom, red 50%, transparent 0) 0 0/ 100% 40%;}

成果如下

然而当初还是看着像纸片一样,须要将截断的几个面都封起来,咱们再加一个标签,用两个伪元素将顶部笼罩

<juejin>  <!-- -->  <top></top></juejin>
top{  position: absolute;  inset: 0;  background-color: #1677f7;  transform: translate3d(0, calc(var(--s) * 0.36), 0) rotateX(90deg) scale(.8);  transform-style: preserve-3d;  box-shadow: inset 0 0 calc(var(--s) * 0.3) rgb(255 255 255 / 45%);}top::before{  content: '';  position: absolute;  inset: 0;  background-color: inherit;  transform: translate3d(0, 0, calc(var(--s) * 0.285)) scale(.5);  box-shadow: inherit;}

当然,这里须要多多调整一下细节,确保横截面的大小和地位刚好吻合

同样还有底部的截面

bottom::before,bottom::after{  content: '';  position: absolute;  inset: 0;  background-color: inherit;  transform: translate3d(0, 0, calc(var(--s) * 0.285)) scale(.595);  box-shadow: inherit;}bottom::after{  transform: translate3d(0, 0, calc(var(--s) * 0.568)) scale(.195)}

成果如下

三、其余光影细节和鼠标追随成果

首先是底部的投影,这是通过filter: blur实现的,能够让整个场景更具空间感

juejin::after{  position: absolute;  content: '';  width: var(--s);  height: var(--s);  top: 80%;  background-color: rgba(0, 0, 0, 0.1);  transform: rotateX(90deg) scale(1.2);  filter: blur(20px);}

而后是截面的高光解决,这里是通过内暗影实现的,让整体光影看着更加天然,就像上方有光线一样

top{  /**/  box-shadow: inset 0 0 calc(var(--s) * 0.3) rgb(255 255 255 / 45%);}

最初是鼠标追随成果,这里是通过 CSS 变量实现的,通过 JS 获取鼠标的绝对地位,而后通过 CSS 变量 传递,实时扭转 transform 的旋转角度

juejin{  /* */  transform: rotateX(calc( (var(--y) - 0.3) * -90deg)) rotateY(calc( (var(--x) - 0.3) * 90deg));}

JS 监听mousemove就能够了

document.body.addEventListener('mousemove', function(ev){  document.body.style.setProperty('--x', ev.clientX / document.body.offsetWidth)  document.body.style.setProperty('--y', ev.clientY / document.body.offsetHeight)})

这样就失去了文章结尾所示成果

残缺代码也能够查看以下任意链接:

  • CSS 3d juejin logo (runjs.work)
  • CSS 3d juejin logo (codepen.io)

四、总结一下

总的来说,绘制这样一个的 3d 图形还是比拟轻松的,上面总结一下

  1. 增加transform-style: preserve-3d能力出现 3d 视觉
  2. 掘金 logo 整体上是一个金字塔形,或者叫四棱锥形(四角锥)
  3. 整个绘制就是通过translate3drotate3d等根底操作组合而成
  4. 镂空局部能够通过突变填充,或者mask遮罩实现
  5. 横截面须要多多调整一下细节,确保横截面的大小和地位刚好吻合
  6. 为了让整个模型更加实在,须要增加正当的高光和投影
  7. 鼠标追随成果能够通过 CSS 变量传递实现

当然,像这种比拟规定的图形还能简略通过CSS应酬一下,如果有一些不规则的,或者有曲面,那就不太好实现了,而且高光也无奈真正模仿进去,像这种状况还是倡议通过three.js这样业余的图形处理库才行。最初,如果感觉还不错,对你有帮忙的话,欢送点赞、珍藏、转发❤❤❤