本文详细介绍tranform属性,通过逐步制作一个可转动正方体的例子介绍此属性
首先看下最终的效果:
1.用法格式
transform:字面解释即变形,对元素的图形进行转变,支持元素从2D向3D转换,即可展示3D的元素
transform的值有rotate(旋转)、skew(扭曲)、scale(缩放)、translate(平移)和matrix(矩阵变形)
本文介绍rotate(旋转)和translate(平移)
上述效果用此两个属性值即可完成,具体属性值介绍请移步MDN或W3School
2.关于图形元素从2D到3D的变换过程
我们平时使用css时,因为屏幕本身是一个平面,默认使用2D效果,我的理解是在一个只有x轴、y轴的平面上描绘图形,当使用3D效果时,可以新增操作一个Z轴,默认Z轴是垂直于屏幕,当我们使用transform的rotate控制图形时,可看到其3D效果。
3.制作旋转正方体
3.1一个旋转的正方形
先从一个简单图形开始,我们来考察下transform的rotate、translate效果,以下代码描绘一个渐变的正方形
<!DOCTYPE html>
<html lang="en">
<head>
<title>3Dbox</title>
<style>
#space {
/* background-color: black; */
}
#box {
width: 180px;
height: 180px;
background-image: linear-gradient(#e66, #36c);
margin: 180px auto;
}
</style>
</head>
<body>
<div id="space">
<div id="box">
</div>
</div>
</body>
</html>
为了更好展示图片效果,每次截取图片都是整个屏幕
添加一行代码到box选择器
transform: rotateX(45deg); /* 元素绕x轴正向旋转45度 */
查看效果:
(绝不是压缩!发挥下想象力)图片绕x轴旋转了45°,可看到上半部分往里收缩,下半部分向外翻转。
关于旋转的理解 :前文提到过3D图形建立了x轴、y轴、z轴,默认情况下,x轴与屏幕水平向右,y轴垂直于x轴并向下,z轴从屏幕延伸出来垂直于屏幕,基点(3轴交汇点)在图形的正中心,当图形绕x轴正方向旋转,可看作从x轴方向逆时针旋转,当图形绕x轴负方向旋转,可看作从x轴正方向顺时针旋转,其他旋转效果类似。轴的方向随着旋转而改变。
为了更好体现旋转效果,我们编写一个手动控制旋转正方形的代码,最终效果:
首先编写html,在上面代码地基础上,添加旋转控制滚动条内容:
<div class="option">
<p>rotate x: <span id="rotatex-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="rotatex" value="0" class="range-control"
onmousemove="change()" /><br />
<p>rotate y: <span id="rotatey-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="rotatey" value="0" class="range-control"
onmousemove="change()" /><br />
<p>rotate z: <span id="rotatez-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="rotatez" value="0" class="range-control"
onmousemove="change()" /><br />
</div>
对应的样式:
.option {
width: 700px;
font-size: 16px;
font-weight: bold;
margin: 0 auto;
}
.range-control {
width: 700px;
}
新增change方法,方法的具体思路是首选获取input的value值,然后通过此值给box添加tranform属性并赋予相对应的rotate值,同时显示当前旋转值(即改变span里面的值):
function change() {
let rotatex = document.getElementById("rotatex").value;
let rotatey = document.getElementById("rotatey").value;
let rotatez = document.getElementById("rotatez").value;
document.getElementById('box').style.transform = "rotateX("+rotatex+"deg) rotateY("+rotatey+"deg) rotateZ("+rotatez+"deg)";
document.getElementById('rotatex-span').innerText = rotatex;
document.getElementById('rotatey-span').innerText = rotatey;
document.getElementById('rotatez-span').innerText = rotatez;
}
整体代码:
<!DOCTYPE html>
<html lang="en">
<head>
<title>3Dbox</title>
<style>
#space {
}
#box {
width: 180px;
height: 180px;
background-image: linear-gradient(#e66, #36c);
margin: 180px auto;
}
.option {
width: 700px;
font-size: 16px;
font-weight: bold;
margin: 0 auto;
}
.range-control {
width: 700px;
}
</style>
<script>
function change() {
let rotatex = document.getElementById("rotatex").value;
let rotatey = document.getElementById("rotatey").value;
let rotatez = document.getElementById("rotatez").value;
document.getElementById('box').style.transform = "rotateX("+rotatex+"deg) rotateY("+rotatey+"deg) rotateZ("+rotatez+"deg)";
document.getElementById('rotatex-span').innerText = rotatex;
document.getElementById('rotatey-span').innerText = rotatey;
document.getElementById('rotatez-span').innerText = rotatez;
}
</script>
</head>
<body>
<div id="space">
<div id="box">
</div>
</div>
<div class="option">
<p>rotate x: <span id="rotatex-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="rotatex" value="0" class="range-control"
onmousemove="change()" /><br />
<p>rotate y: <span id="rotatey-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="rotatey" value="0" class="range-control"
onmousemove="change()" /><br />
<p>rotate z: <span id="rotatez-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="rotatez" value="0" class="range-control"
onmousemove="change()" /><br />
</div>
</body>
</html>
3.2 展示平移属性
以上都是旋转属性,接下来再添加一些平移属性,按照上面代码思路,得到以下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<title>3Dbox</title>
<style>
#space {}
#box {
width: 180px;
height: 180px;
background-image: linear-gradient(#e66, #36c);
margin: 180px auto;
transition: transform 1s linear;
}
.option {
width: 700px;
font-size: 16px;
font-weight: bold;
margin: 0 auto;
}
.range-control {
width: 700px;
}
</style>
<script>
function change() {
let rotatex = document.getElementById("rotatex").value;
let rotatey = document.getElementById("rotatey").value;
let rotatez = document.getElementById("rotatez").value;
let translatex = document.getElementById('translatex').value;
let translatey = document.getElementById('translatey').value;
let translatez = document.getElementById('translatez').value;
document.getElementById('box').style.transform =
"rotateX(" + rotatex + "deg) rotateY(" + rotatey + "deg) rotateZ(" + rotatez + "deg)"
+"translateX("+translatex+"px) translateY("+translatey+"px) translateZ("+translatez+"px)";
document.getElementById('rotatex-span').innerText = rotatex;
document.getElementById('rotatey-span').innerText = rotatey;
document.getElementById('rotatez-span').innerText = rotatez;
document.getElementById('translatex-span').innerText = translatex;
document.getElementById('translatey-span').innerText = translatey;
document.getElementById('translatez-span').innerText =translatez;
}
</script>
</head>
<body>
<div id="space">
<div id="box">
</div>
</div>
<div class="option">
<p>rotate x: <span id="rotatex-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="rotatex" value="0" class="range-control"
onmousemove="change()" /><br />
<p>rotate y: <span id="rotatey-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="rotatey" value="0" class="range-control"
onmousemove="change()" /><br />
<p>rotate z: <span id="rotatez-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="rotatez" value="0" class="range-control"
onmousemove="change()" /><br />
<p>translate x: <span id="translatex-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="translatex" value="0" class="range-control"
onmousemove="change()" /><br />
<p>translate y: <span id="translatey-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="translatey" value="0" class="range-control"
onmousemove="change()" /><br />
<p>translate z: <span id="translatez-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="translatez" value="0" class="range-control"
onmousemove="change()" /><br />
</div>
</body>
</html>
分别添加控制条的html内容以及再方法中添加控制代码,思路与添加rotate一致,
额外的在#box控制器中添加样式transtion: transform 1s liner;此代码使得图片滚动时产生动画效果
上面效果中图片的移动产生了动画的滑动效果,仔细的人会发现,当我们经过平移后,再转动图片,其转动效果不再围绕着图片进行旋转。原因在于,平移不会使得基点发生变化,对应的轴也不会发生改变
3.3实现旋转正方体
这是最后的环节,通过上面的分析,我们发现问题其实已经解决了一大半,剩下的问题是,如何绘制一个正方体,当我们能够成功绘制出一个正方体,再通过上面的思路控制正方体的旋转平移即可
思路:(需要一些想象力,因为我找不到好的画图工具)假设如今有6块重叠在一起的面,每个面有1~6的标记用以区分,以这些面的中心点为基点,通过旋转和平移得到一个正方体,
- 平面1向屏幕前移动一个单位(单位长度为正方体边长的一半)
- 平面6向屏幕相反方向移动一个单位
- 平面2沿着y轴逆方向旋转90°再往左平移一个单位
- 平面5沿着y轴正方向旋转90°再往右平移一个单位
- 平面3沿着x轴逆方向旋转90°再往上平移一个单位
- 平面4沿着x轴正方向旋转90°再往下平移一个单位
此时,一个正方体就呈现了,通过上面思路完成最终代码:
<!DOCTYPE html>
<html lang="en">
<head>
<title>3Dbox</title>
<style>
#space {}
#box {
width: 180px;
height: 180px;
margin: 180px auto;
transition: transform 1s linear;
transform-style: preserve-3d;
transform: rotateX(-35deg) rotateY(35deg);
}
.face {
width: 180px;
height: 180px;
background-color: black;
color: white;
font-size: 180px;
line-height: 180px;
text-align: center;
position: absolute;
border: 1px solid white;
opacity: 0.6;
}
#face1 {
transform: translateZ(90px);
}
#face2 {
transform: rotateY(-90deg) translateZ(90px);
/* 当元素进行旋转后,其坐标轴会跟随改变 */
}
#face3 {
transform: rotateX(90deg) translateZ(90px);
}
#face4 {
transform: rotateX(-90deg) translateZ(90px);
}
#face5 {
transform: rotateY(-90deg) translateZ(-90px);
}
#face6 {
transform: translateZ(-90px);
}
.option {
width: 700px;
font-size: 16px;
font-weight: bold;
margin: 0 auto;
}
.range-control {
width: 700px;
}
</style>
<script>
function change() {
let rotatex = document.getElementById("rotatex").value;
let rotatey = document.getElementById("rotatey").value;
let rotatez = document.getElementById("rotatez").value;
let translatex = document.getElementById('translatex').value;
let translatey = document.getElementById('translatey').value;
let translatez = document.getElementById('translatez').value;
document.getElementById('box').style.transform =
"rotateX(" + rotatex + "deg) rotateY(" + rotatey + "deg) rotateZ(" + rotatez + "deg)"
+ "translateX(" + translatex + "px) translateY(" + translatey + "px) translateZ(" + translatez + "px)";
document.getElementById('rotatex-span').innerText = rotatex;
document.getElementById('rotatey-span').innerText = rotatey;
document.getElementById('rotatez-span').innerText = rotatez;
document.getElementById('translatex-span').innerText = translatex;
document.getElementById('translatey-span').innerText = translatey;
document.getElementById('translatez-span').innerText = translatez;
}
</script>
</head>
<body>
<div id="space">
<div id="box">
<div class="face" id="face1">1</div>
<div class="face" id="face2">2</div>
<div class="face" id="face3">3</div>
<div class="face" id="face4">4</div>
<div class="face" id="face5">5</div>
<div class="face" id="face6">6</div>
</div>
</div>
<div class="option">
<p>rotate x: <span id="rotatex-span">-35</span> deg</p>
<input type="range" min="-360" max="360" id="rotatex" value="-35" class="range-control"
onmousemove="change()" /><br />
<p>rotate y: <span id="rotatey-span">30</span> deg</p>
<input type="range" min="-360" max="360" id="rotatey" value="35" class="range-control"
onmousemove="change()" /><br />
<p>rotate z: <span id="rotatez-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="rotatez" value="0" class="range-control"
onmousemove="change()" /><br />
<p>translate x: <span id="translatex-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="translatex" value="0" class="range-control"
onmousemove="change()" /><br />
<p>translate y: <span id="translatey-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="translatey" value="0" class="range-control"
onmousemove="change()" /><br />
<p>translate z: <span id="translatez-span">0</span> deg</p>
<input type="range" min="-360" max="360" id="translatez" value="0" class="range-control"
onmousemove="change()" /><br />
</div>
</body>
</html>
解析:
<div id="space">
<div id="box">
<div class="face" id="face1">1</div>
<div class="face" id="face2">2</div>
<div class="face" id="face3">3</div>
<div class="face" id="face4">4</div>
<div class="face" id="face5">5</div>
<div class="face" id="face6">6</div>
</div>
</div>
搭建正方体html,box里面对应6个面,通过css操作6个面的展示
#box {
width: 180px;
height: 180px;
margin: 180px auto;
transition: transform 1s linear;
transform-style: preserve-3d;
transform: rotateX(-35deg) rotateY(35deg);
}
transform-style:preserve-3d; 保证此元素的子元素能够使用3D效果,因为每个面要基于父元素基点做旋转平移操作
transform: rotateX(-35deg) rotateY(35deg); 给正方体一个初始角度
.face {
width: 180px;
height: 180px;
background-color: black;
color: white;
font-size: 180px;
line-height: 180px;
text-align: center;
position: absolute;
border: 1px solid white;
opacity: 0.6;
}
#face1 {
transform: translateZ(90px);
}
#face2 {
transform: rotateY(-90deg) translateZ(90px);
/* 当元素进行旋转后,其坐标轴会跟随改变 */
}
#face3 {
transform: rotateX(90deg) translateZ(90px);
}
#face4 {
transform: rotateX(-90deg) translateZ(90px);
}
#face5 {
transform: rotateY(-90deg) translateZ(-90px);
}
#face6 {
transform: translateZ(-90px);
}
给每个面设置样式,根据思路布置好每个面的位置
至此,一个旋转正方体demo已经全部完成
4.关于平移与旋转的一些想法
本人在实践代码时,一开始被这些旋转和平移效果搞得混乱,在实现一些平移的时候,发现并不如己所愿,仔细观察最终效果图,会发现当我拉扯x轴平移、y轴平移,是不会水平平移,竖直平移的,原因在上文有提及,当旋转图形后,坐标轴会跟随旋转,那么能不能使得平移和旋转分开呢?使得当我们旋转的时候,只是绕着正方体中心旋转,当我平移的时候,任何时候都是水平,竖直平移。经过实践,答案是可以。
思路:设置两个坐标系,使得平移和旋转不在同一个坐标系,在box外层套一层盒子,以外层盒子作为平移坐标系,问题随即解决
最终效果:
因为以外层作为坐标系,外层与屏幕平行,且不对外层旋转,所以对于Z轴的平移,不会看到效果,故不添加到代码中,感兴趣的朋友可以尝试添加
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<title>3Dbox</title>
<style>
#space {
transition: transform 1s linear;
transform-style: preserve-3d;
}
#box {
width: 180px;
height: 180px;
margin: 180px auto;
transition: transform 1s linear;
transform-style: preserve-3d;
transform: rotateX(-35deg) rotateY(35deg);
}
.face {
width: 180px;
height: 180px;
background-color: black;
color: white;
font-size: 180px;
line-height: 180px;
text-align: center;
position: absolute;
border: 1px solid white;
opacity: 0.6;
}
#face1 {
transform: translateZ(90px);
}
#face2 {
transform: rotateY(-90deg) translateZ(90px);
/* 当元素进行旋转后,其坐标轴会跟随改变 */
}
#face3 {
transform: rotateX(90deg) translateZ(90px);
}
#face4 {
transform: rotateX(-90deg) translateZ(90px);
}
#face5 {
transform: rotateY(-90deg) translateZ(-90px);
}
#face6 {
transform: translateZ(-90px);
}
.option {
width: 700px;
font-size: 16px;
font-weight: bold;
margin: 0 auto;
}
.range-control {
width: 700px;
}
</style>
<script>
function changeRotate() {
let rotateX = document.getElementById('rotatex-input').value;
let rotateY = document.getElementById('rotatey-input').value;
let rotateZ = document.getElementById('rotatez-input').value;
document.getElementById('box').style.transform =
"rotateX(" + rotateX + "deg) rotateY(" + rotateY + "deg) rotateZ(" + rotateZ + "deg)";
document.getElementById('rotatex-span').innerText = rotateX;
document.getElementById('rotatey-span').innerText = rotateY;
document.getElementById('rotatez-span').innerText = rotateZ;
}
function changeTranslate() {
let translateX = document.getElementById('translatex-input').value;
let translateY = document.getElementById('translatey-input').value;
document.getElementById('space').style.transform =
"translateX(" + translateX + "px) translateY(" + translateY + "px)";
document.getElementById('translatex-span').innerText = translateX;
document.getElementById('translatey-span').innerText = translateY;
}
</script>
</head>
<body>
<div id="space">
<div id="box">
<div class="face" id="face1">1</div>
<div class="face" id="face2">2</div>
<div class="face" id="face3">3</div>
<div class="face" id="face4">4</div>
<div class="face" id="face5">5</div>
<div class="face" id="face6">6</div>
</div>
</div>
<div class="option">
<p>rotate X: <span id="rotatex-span">0</span> deg</p>
<input id="rotatex-input" type="range" value="-35" min="-360" max="360" class="range-control"
onmousemove="changeRotate()">
<p>rotate Y: <span id="rotatey-span">0</span> deg</p>
<input id="rotatey-input" type="range" value="35" min="-360" max="360" class="range-control"
onmousemove="changeRotate()">
<p>rotate Z: <span id="rotatez-span">0</span> deg</p>
<input id="rotatez-input" type="range" value="0" min="-360" max="360" class="range-control"
onmousemove="changeRotate()">
<p>translate X: <span id="translatex-span">0</span> deg</p>
<input id="translatex-input" type="range" value="0" min="-360" max="360" class="range-control"
onmousemove="changeTranslate()">
<p>translate Y: <span id="translatey-span">0</span> deg</p>
<input id="translatey-input" type="range" value="0" min="-360" max="360" class="range-control"
onmousemove="changeTranslate()">
</div>
</body>
</html>