在你未尝试之前,永远不要认为它很难,一起来看看 CSS 3D 能实现那些酷炫的东西吧
3D 是 CSS 范畴的吗? 在印象当中,web 实现 3D 第一时间会联想到 three.js 或者原生的 canvas WebGL,后来看到一些比较酷炫的 CSS 动画才意识到原来 CSS3 中早已经能实现简单的 3D 效果了,接下来,我将以实现一个 3D 场景下的正方体到全景漫游图为目标,具体讲解几个实现 CSS 3D 重要属性。
实现的效果
在线预览: css-pano 源码: web-pano
两个重要的属性 perspective 概念 perspective 指 3D 场景下透视距离,要理解这个属性,首先需要将平面的屏幕想像成一个 3D 的场景,假设我们距离屏幕 200px ,此时perspective即为200px,这个时候你在屏幕上绘制一个 50px 长宽的正方形,假设该正方体沿着 Z 轴移动 +100px,也就是向你面前靠近 100px,此时,根据“近大远小”的视觉效果,该正方形会变得越来越大,如果该正方体沿着 Z 轴移动 +199px,也就是移动到了你最靠近眼睛的位置,此时你将被一个正方形平面给完全盖住,眼前所有的东西都只是一个正方体,再进一步,如果该正方体沿着 Z 轴移动 +201px,此时,正方形将越过你眼睛,在你眼睛后面,你将不会在看到该正方形。perspective 就是这么简单的概念,我们看一段简单的代码,来实践上述所描述的场景。
代码效果 这里我们使用一个动画,沿着Z轴不断移动,直到越过你眼睛。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <body> <div class="card"> card </div> </body> body { height: 100vh; display: flex; justify-content: center; align-items: center; /* 透视距离 */ perspective: 200px; } @keyframes rotate { 0% { transform: translateZ(0px); } 100% { transform: translateZ(200px); } } .card { width: 100px; height: 100px; background: green; /* 空间移动距离 */ transform: translateZ(0px); animation-duration: 2s; animation-timing-function: linear; animation-name:rotate; }
生效范围 perspective 生效范围是设置该属性容器内的子元素, 它可以设置在任意容器上,假设我们只需要在一个小场景下实现 3D 效果,比如我们设置在一个宽高固定的容器上,当容器内效果超出容器长宽后,需要设置 overflow: hidden; 才能隐藏超出的效果,如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <body> <div class="container"> <div class="card"> card </div> </div> </body> body { height: 100vh; display: flex; justify-content: center; align-items: center; } @keyframes rotate { 0% { transform: translateZ(0px); } 100% { transform: translateZ(300px); } } .container { width: 300px; height: 300px; display: flex; justify-content: center; align-items: center; /* 透视距离 */ perspective: 200px; border: 1px solid red; /* 超出即隐藏 */ overflow: hidden; } .card { width: 100px; height: 100px; background: green; /* 空间移动距离 */ transform: translateZ(0px); animation-duration: 2s; animation-timing-function: linear; animation-name:rotate; }
perspective-origin perspective-origin,透视视角,该属性是表示“眼睛处在容器的哪个方位”,默认值是 perspective-origin: 50% 50%;,即容器的中间,也就是为什么上面两个3D效果都是朝你眼睛正中扑面而来,我们多设置几个“视角”来体验该属性的效果:
如下三个视角分别是:
1 2 3 4 5 6 7 8 9 10 .container1 { perspective-origin: 50% 50%; } .container2 { perspective-origin: 0 0; } .container3 { perspective-origin: 50% 0; }
transform-style 指定子元素如何在3D空间中呈现,有两个属性值:flat和preserve-3d,其中 flat 值为默认值,表示所有子元素在2D平面呈现。preserve-3d 表示所有子元素在3D空间中呈现,简单来讲 flat 场景下,只有 x,y 轴的效果,而 preserve-3d 具有 x,y,z 三轴方向,比如两个平面在空间里相交,在 flat 只有覆盖效果,而 preserve-3d 具有真实的前后相交的效果,我们直接看代码效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <div class="container"> <div class="card card1">card1</div> <div class="card card2">card2</div> </div> .container { position: relative; height: 300px; width: 300px; border: 1px solid red; /* transform-style: preserve-3d; */ perspective: 200px; } .card { height: 160px; width: 240px; position: absolute; left: 0; } .card1 { background: red; } .card2 { width: 290px; background: green; transform: rotateX(40deg); }
transform-style: preserve-3d:
实现一个正方体 在理解上述两个重要属性后,我们开始实现一个正方体,拼一个正方体需要6个平面,分别将其在 3D 场景下放置到合理位置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 <div class="box"> <div class="scene"> <div class="cube"> <div class="side-back slide"></div> <div class="side-front slide"></div> <div class="side-left slide"></div> <div class="side-right slide"></div> <div class="side-top slide"></div> <div class="side-bottom slide"></div> </div> </div> </div> .box { perspective: 90px; height: 300px; width: 600px; overflow: hidden; border: 1px solid red; } @keyframes rotate { 0% { transform:translateZ(-400px) rotateY(0deg) ; } 100% { transform:translateZ(-400px) rotateY(360deg); } } .scene { display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; transform-style: preserve-3d; } .cube { position: absolute; width: 400px; height: 400px; transform-style: preserve-3d; transform-origin: center center; transform: translateZ(-400px) rotateY(0); animation: rotate 12s linear infinite; } .slide { position: absolute; width: 100%; height: 100%; } .side-front { transform: rotateY(0) translateZ(-200px); background: yellow; } .side-back { transform: rotateY(180deg) translateZ(-200px); background: skyblue; } .side-left { transform: rotateY(90deg) translateZ(-200px); background: green; } .side-right { transform: rotateY(-90deg) translateZ(-200px); background: blue; } .side-top { transform: rotateX(-90deg) translateZ(-200px); background: rebeccapurple; } .side-bottom { transform: rotateX(90deg) translateZ(-200px); background: salmon; }
效果:
使得视角处在正方体内部 假设我想看到正方体内部的 3D 效果,我们可以将该容器沿着 Z 轴向我们视角靠近,我们初始的透视距离是 90px,正方体的长宽高是 400px,我们想处在正方体中间,那么就需要将正方体容器沿着 Z 轴移动 +290px:
1 2 3 4 5 6 7 8 9 .scene { display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; transform-style: preserve-3d; transform: translateZ(290px); }
效果:
全景图实现 假设每一块平面是真实的图像场景,我们使用特殊的相机,或者使用渲染引擎渲染出整个前后左右上下的场景,在中心点,向六个面分别拍摄一张照片,将其贴入正方体内部的六个面,那么就能以假乱真地渲染出 3D 全景图的效果,我们试一试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 ... .scene { ... /* 调整到最佳位置 */ transform: translateZ(480px); } ... .side-front { transform: rotateY(0) translateZ(-200px); /* background: yellow; */ background-image: url("https://qhyxpicoss.kujiale.com/r/2019/07/10/L3D224S3ENDIA5FTCIUI5N2GLUF3P3WC888.0_12000x2000.jpg_f?x-oss-process=image/resize,m_fill,w_1024,h_1024"); } .side-front { transform: rotateY(0) translateZ(-200px); /* background: yellow; */ background-image: url("http://static.vince.xin/UYCFVGBHNJMK.jpg"); } .side-back { transform: rotateY(180deg) translateZ(-200px); /* background: skyblue; */ background-image: url("http://static.vince.xin/FRTGYHUJKBHNJ.jpg"); } .side-left { transform: rotateY(90deg) translateZ(-200px); /* background: green; */ background-image: url("http://static.vince.xin/YGVGBHJNKM.jpg"); } .side-right { transform: rotateY(-90deg) translateZ(-200px); /* background: blue; */ background-image: url("http://static.vince.xin/YJMEDFGHJKIUH.jpg"); } .side-top { transform: rotateX(-90deg) translateZ(-200px); /* background: rebeccapurple; */ background-image: url("http://static.vince.xin/YTGHJKLSDF.jpg"); } .side-bottom { transform: rotateX(90deg) translateZ(-200px); /* background: salmon; */ background-image: url("http://static.vince.xin/YOKSDCVBNM.jpg"); }
效果:
这里主要的难点在于,如何找到最佳的 perspective 透视距离和容器移动的 Z 轴距离,其他都是比较简单的 CSS 3D 移动。
总结 总的来说,CSS 中的 3D 效果还是比较简单的,我们理解了上面两个重要属性后,将元素在 X,Y,Z 轴移动结合,就能实现很多酷炫的效果,想要熟练地掌握需要我们多试错与实践,Thanks~
refs