CSS 3D坐标空间实现研究

先展示研究成果:

今天想研究一下CSS的 3D实现,但又不敢深入三角函数和线性代数的知识(多年前就已还给高数老师了),所以就浅浅地从translateX/translateY/translateZ这3个变换做一个立方体开始。这三个变换函数很简单,关键是要建立固定的坐标系,这样页面的元素就能通过这个坐标系获得立体感。

我的HTML结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div class="scene">
  <!--方块-->
  <div class="cube">
    <div class="cube-face cube-face-front">1</div>
    <div class="cube-face cube-face-right">2</div>
    <div class="cube-face cube-face-top">4</div>
    <div class="cube-face cube-face-left">3</div>
    <div class="cube-face cube-face-back">5</div>
    <div class="cube-face cube-face-bottom">6</div>
  </div>
  <!--坐标线-->
  <div class="line line-x"></div>
  <div class="line line-y"></div>
  <div class="line line-z"></div>
</div>

作为场景,其中6个div方块为立方体的6个面,三条坐标线代表3D的坐标轴,为了使整个场景能响应屏幕尺寸,取百分比40%,60%为坐标原点。
坐标线的样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.line{
	position: absolute;
	width: 500px; height: 1px;
	border-top: 1px dotted #000;
	background:transparent;
	/*坐标轴要围绕原点旋转,先定好transform-origin*/
	transform-origin: 0 0;
	left:40%;
	top:60%;
}
.line-y{
	/*x方向旋转获得y轴*/
	transform: rotate(-90deg);
}
.line-z{
	/*y方向旋转获得z轴*/
	transform: rotateY(-90deg);
}

场景样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.scene {
	position: relative;
	width: 700px;
	max-width:100%;
	height: 500px;
	border: 2px solid #CCC;
	/*这两个perspective属性直接决定视觉效果,诸位可以自己调整*/
	perspective: 1600px;
	perspective-origin: 150% -40%;
	overflow: hidden;
	/*立方体的尺寸变量,在坐标中会多次用到负值,这里定义一正一负两个值是为了减少后面的运算*/
	--cube-size: 100px;
	--cube-size-minus: -100px;
	transition: all 0.5s;
}

以上定义完毕后,获得场景,注意坐标原点位置为40%,60%:

接下来设置立方体的样式:

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
.cube {
	width: var(--cube-size);
	height: var(--cube-size);
	position: absolute;
	/*位置放在原点*/
	left: 40%;
	top: 60%;
	transform-style: preserve-3d;
	/*因要使立方体底部对其原点,所以必须向上偏移一个立方体高度*/
	transform: translateY(var(--cube-size-minus));
	transition: all 0.5s;
}
/*六个面统一样式*/
.cube-face {
	position: absolute;
	bottom: 0;
	left: 0;
	width: var(--cube-size);
	height: var(--cube-size);
	display:flex;
	flex-direction: row;
	justify-content: center;
	align-items: center;
	font-size: 40px;
	font-weight: bold;
	color: #fff;
	text-align: center;
	transform-origin: 0 100%;
	transition: all 0.5s linear;
}
/*六个面的颜色*/
.cube-face-front  { background: rgba( 25, 175, 240, 0.8); }
.cube-face-right  { background: rgba( 134, 209, 204, 0.8); }
.cube-face-back   { background: rgba(189, 211, 225, 0.8); }
.cube-face-left   { background: rgba(225, 190, 135, 0.8); }
.cube-face-top    { background: rgba(240, 175, 25, 0.8); }
.cube-face-bottom { background: rgba(25, 175, 240, 0.8); }
 
/*六个面的位置,其中back这面正好对齐x和y轴原点面,无需翻转,其他个面,都是在back面基础上,通过rotate和translate复合翻转位移*/
.cube-face-back   { }
/*前面,仅朝着屏幕移近1个宽度,z轴负方向*/
.cube-face-front  { transform: translateZ(var(--cube-size)); }
/*右侧面,竖直方向翻转90度到屏幕里面去了,translateX移出来一个单位,再向右移一个单位到正确位置*/
.cube-face-right  { transform: rotateY( 90deg) translateX(var(--cube-size-minus)) translateZ(var(--cube-size));}
/*左侧面,竖直方向翻转90度到屏幕里面去了,translateX移出来一个单位*/
.cube-face-left   { transform: rotateY( 90deg) translateX(var(--cube-size-minus)); }
/*顶部,X轴方向翻转90度到屏幕里面去了,再上移,再移出*/
.cube-face-top    { transform: rotateX( 90deg) translateY(var(--cube-size)) translateZ(var(--cube-size));  }
/*顶部,X轴方向翻转90度到屏幕里面去了,再移出*/
.cube-face-bottom { transform: rotateX( 90deg) translateY(var(--cube-size)); }

现在方块就形成了:

这是一个正立方体,假如要调整任意一个面的尺寸,保持它的完整,它就会变成长方体,其他各个面的尺寸也需要相应调整。这时候就借助一下JS:

//通过这个函数,可以完成立方体的任意尺寸变化
function resizeCube(l,w,h){
	var cube_length = l;
	var cube_width = w;
	var cube_height = h;
 
	$(".cube-face-front").css("width" , cube_width+"px")
	$(".cube-face-front").css("height" , cube_height+"px")
	$(".cube-face-front").css("transform" , "translateZ("+cube_length+"px)")
 
	$(".cube-face-back").css("width" , cube_width+"px")
	$(".cube-face-back").css("height" , cube_height+"px")
 
	$(".cube-face-top").css("width" , cube_width+"px")
	$(".cube-face-top").css("height" , cube_length+"px")
	$(".cube-face-top").css("transform" , "rotateX( 90deg) translateZ("+cube_height+"px) translateY("+cube_length+"px)")
 
	$(".cube-face-bottom").css("width" , cube_width+"px")
	$(".cube-face-bottom").css("height" , cube_length+"px")
	$(".cube-face-bottom").css("transform" , "rotateX( 90deg) translateY("+cube_length+"px)")
 
	$(".cube-face-right").css("width" , cube_length+"px")
	$(".cube-face-right").css("height" , cube_height+"px")
	$(".cube-face-right").css("transform" , "rotateY( 90deg) translateZ("+cube_width+"px) translateX(-"+cube_length+"px)")
 
	$(".cube-face-left").css("width" , cube_length+"px")
	$(".cube-face-left").css("height" , cube_height+"px")
	$(".cube-face-left").css("transform" , "rotateY( 90deg) translateX(-"+cube_length+"px)")
}

以上都是基础原理,我后续又对整个程序做了一点点小变化,它就成了一个可以做一些随机变化的小玩具,见这个链接:https://blog.brain1981.com/wp-content/uploads/css-3d-cube.html

这个坐标系只是我的一个网站项目里要用到的小工具,因为只需要简单实现,就不想给页面加载任何庞大的JS库,于是自己研究了一下实现。再复杂的3D形状,都是通过一个个面形成的,我们可以用background-image给它们贴图,也可以增加任意个面构建复杂的形状,不过纯手工这样做是很累的,做复杂的项目,肯定还是要引入适合的库了。

本站所有文章均为原创,欢迎转载,请注明文章出处:https://blog.brain1981.com/2414.html。百度和各类采集站皆不可信,搜索请谨慎鉴别。技术类文章一般都有时效性,本人习惯不定期对自己的博文进行修正和更新,因此请访问出处以查看本文的最新版本。

关注我们的微信公众号-JennyStudio 本站记录了近几年的工作中遇到的一些技术问题和解决过程,“作品集”还收录了本人的大部分作品展示。除了本博客外,我们的工作室网站 – JennyStudio,内有更多作品回顾和展示。
您也可以扫描左边的二维码,关注我们的微信公众号,在微信上查看我们的案例。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注