再谈六面体
本帖最后由 马黑黑 于 2023-2-3 20:31 编辑多面体中,六面体我们已经探讨了不少,因为它是立体几何中最基础的,弄懂它其他的多面体就不再是难题。
在HTML里,实现六面体首先需要3D视界,它实际上相当观察者的眼睛,以及,眼睛离被观察对象的纵深空间:
<div id="眼睛">
其他元素代码
</div>
对应纵深的相关属性要在CSS中设置:
#眼睛 {
perspective: 1000px;
perspective-origin: 50% 40%;
}
perspective属性就是景深,指眼睛到被观察对象间的空间距离,等同于眼睛和荧屏里的被观察3D对象的距离,属Z轴指向的方向。这个景深是客观存在的也是虚拟的,说它客观存在,因为电脑使用者的眼睛的确与荧屏事实存在一定距离,说它虚拟,是因为被观察3D对象在荧屏里它真实只存在于屏幕表面,它在Z轴上离观者眼睛的远近是依靠算法虚拟出来的。CSS还能虚拟出观者的角度,就是 perspective-origin 所干的事,由它模拟出被观察对象的视点和视面,上例中,观者眼睛的角度在X轴的中央(50%)、在Y轴中心往上10个百分点(40%),有略微俯视的意味。
被观察对象需要活动场景,即舞台(想象一下兔子跳钢管舞或做单杠运动的空间),这个舞台,也是由HTML的一个div或其他相应元素标签来营造:
<div id="眼睛">
<div id="舞台">
其他元素代码
</div>
</div>
我们需要这个舞台要能呈现出3D风格,因此需要通过CSS来定义该风格属性:
#舞台 {
transform-style: preserve-3d;
}
如果我们不设置 transform-style 为 preserve-3d,以及该属性缺省时,舞台所能呈现的是 flat(平面)风格,没有立体效果。
现在,我们有了观者(眼睛)和舞台,我们花了钱去剧场,不能只欣赏空空荡荡的舞台吧?因此,演员,要登场表演,这就是3D对象,它在HTML代码流里,也是由 div 或其他元素标签去实现的:
<div id="眼睛">
<div id="舞台">
<span class="演员">前面</span>
<span class="演员">背后</span>
<span class="演员">左侧</span>
<span class="演员">右侧</span>
<span class="演员">顶部</span>
<span class="演员">底部</span>
</div>
</div>
舞台节目虽然有单口相声和独角戏,可是更多的节目是群体节目,演员不止一个,为了方便设置,我们上面的代码,演员用class而不是id来表明身份,总共六位(请想一想快递盒子有多少面)。这些演员,有共性特征,我们用CSS来描述这些相同的属性:
.演员 {
position: absolute;
width: 100%;
height: 100%;
display: grid;
place-items: center;
font: normal 2em arial sans-serif;
opacity: .85;
}
上面代码中,每个面板的宽高都是100%,它们继承上层父盒子即舞台的尺寸,也可以根据需要设置合乎自己需求的值。接下来我们来处理第一块,即面对我们的那一块。我们打算,① 把它往我们眼睛的方向即在Z轴方向拉过来一点,距离是其长度的一半,这么做是为了其余多数面板不用额外挪动Z轴方向的位置;② 给它一个背景颜色,以便方便观察效果:
.演员:nth-of-type(1) {
background: teal;
transform: translateZ(150px) rotateY(0deg);
}
第二块,就是背后那一块,自然要往后拉相同的距离,背景颜色也给一个(后面的面板也都设定不同的背景色):
.演员:nth-of-type(2) {
background: tan;
transform: translateZ(-150px) rotateY(0deg);
}
以上前、后两块面板都不需要旋转,因为它们正对着我们,面板是与Z轴垂直的。
第三、四块在左、右侧,它们要沿Y轴旋转90度(转个身),旋转之后它们与X轴垂直,且在X轴的中心点,所以要各自往左、往右挪动其长度的一半的距离:
.演员:nth-of-type(3) {
background: orange;
transform: translateX(-150px) rotateY(90deg);
}
.演员:nth-of-type(4) {
background: pink;
transform: translateX(150px) rotateY(90deg);
}
第五、六块分别在顶部和底部,他们需要沿X轴旋转90度(躺平),旋转后与Y轴垂直,它们此时在Y轴的中心点,需要各自往上、往下挪动自身长度一半的距离:
.演员:nth-of-type(5) {
background: purple;
transform: translateY(-150px) rotateX(90deg);
}
.演员:nth-of-type(6) {
background: yellow;
transform: translateY(150px) rotateX(90deg);
}
这样,这六块面板就构成了一个漂亮的快递盒子,不过,由于角度问题,我们看到的不是很真切,要解决这个问题,我们有三种方案:其一,让它沿XYZ轴旋转起来,其二,改变眼睛的视点(perspective-origin),其三,让舞台旋转那么一丢丢。我们采用第三种方法,用静态的方式观察快递盒子的美:
#舞台 {
position: relative;
width: 300px;
height: 300px;
transform-style: preserve-3d;
animation: spin1 10s infinite linear;
transform: rotateX(-15deg) rotateY(-15deg) rotateX(-15deg);
}
本帖探讨内容所围绕的例子,完整代码如下,效果请在后面的回复中查看:
<style>
#眼睛 {
margin: 200px;
perspective: 3000px;
perspective-origin: 50% 40%;
}
#舞台 {
position: relative;
width: 300px;
height: 300px;
transform-style: preserve-3d;
animation: spin1 10s infinite linear;
transform: rotateX(-15deg) rotateY(-15deg) rotateX(-15deg);
}
.演员 {
position: absolute;
width: 100%;
height: 100%;
display: grid;
place-items: center;
font: normal 2em arial sans-serif;
opacity: .85;
}
.演员:nth-of-type(1) {
background: teal;
transform: translateZ(150px) rotateY(0deg);
}
.演员:nth-of-type(2) {
background: tan;
transform: translateZ(-150px) rotateY(0deg);
}
.演员:nth-of-type(3) {
background: orange;
transform: translateX(-150px) rotateY(90deg);
}
.演员:nth-of-type(4) {
background: tan;
transform: translateX(150px) rotateY(90deg);
}
.演员:nth-of-type(5) {
background: purple;
transform: translateY(-150px) rotateX(90deg);
}
.演员:nth-of-type(6) {
background: yellow;
transform: translateY(150px) rotateX(90deg);
}
@keyframes spin {
from { transform: rotateX(0) rotateY(0) rotateZ(0); }
to { transform: rotateX(0deg) rotateY(360deg) rotateZ(360deg); }
}
</style>
<div id="眼睛">
<div id="舞台">
<span class="演员">前面</span>
<span class="演员">背后</span>
<span class="演员">左侧</span>
<span class="演员">右侧</span>
<span class="演员">顶部</span>
<span class="演员">底部</span>
</div>
</div>
本帖最后由 起个网名好难 于 2023-2-3 18:49 编辑 <br /><br /><style type="text/css">
#cube{
margin:100px auto;
width:250px; /*立方体面的宽*/
height:250px; /*立方体面的高*/
position:relative; /*相对定位*/
transform-style:preserve-3d; /* 默认flat 2D */
transform:rotateX(-15deg) rotateY(15deg);
}
#cube img{
width:100%;
height:100%;
position:absolute; /*绝对定位*/
opacity:.8; /*透明度*/
display:none;
border:thin blue solid;
}
@keyframes rotate{
0%{
transform:rotateX(0deg) rotateY(0deg);
}
100%{
transform:rotateX(360deg) rotateY(360deg);
}
}
#oblk {
width:800px;height:600px;
overflow:hidden;display:grid;place-items:center;
perspective: 3000px;
}
</style>
<div id="oblk">
<div id="cube">
<img src="https://s1.ax1x.com/2022/11/21/zQXlkD.jpg" />
<img src="https://s1.ax1x.com/2022/11/21/zQXMTO.jpg" />
<img src="https://s1.ax1x.com/2022/11/21/zQXK0K.jpg" />
<img src="https://s1.ax1x.com/2022/11/21/zQXum6.jpg" />
<img src="https://s1.ax1x.com/2022/11/21/zQXmOx.jpg" />
<img src="https://s1.ax1x.com/2022/11/21/zQXe61.jpg" />
</div></div>
<script type="text/javascript">
var cude = document.querySelector('#cube');
var imgs = cude.getElementsByTagName('img');
var aniObj = [];
var aIdx = 0;
for(n = 0; n < imgs.length; n++) {
if(n < 4) {
aniObj = imgs.animate([{transform:"rotateX(" + n*90 + "deg) translateZ(125px)"}], {duration:3000, fill:'forwards'});
}
else {
aniObj = imgs.animate([{transform:"rotateY(" + (n==4?90:-90) + "deg) translateZ(125px)"}], {duration:3000, fill:'forwards'});
}
aniObj.pause();
aniObj.onfinish = function() {
aIdx++;
if(aIdx < imgs.length) {
imgs.style.display = "block";
aniObj.play();
}
else {
cude.style.animation = "rotate 15s infinitelinear";console.log(cude.style.animationPlayState);
}
}
}
imgs.style.display = "block";
aniObj.play();
</script> 本帖最后由 马黑黑 于 2023-2-3 20:31 编辑 <br /><br /><style>
#眼睛 {
margin: 200px;
perspective: 3000px;
perspective-origin: 50% 40%;
}
#舞台 {
position: relative;
width: 300px;
height: 300px;
transform-style: preserve-3d;
animation: spin1 10s infinite linear;
transform: rotateX(-15deg) rotateY(-15deg) rotateX(-15deg);
}
.演员 {
position: absolute;
width: 100%;
height: 100%;
display: grid;
place-items: center;
font: normal 2em arial sans-serif;
opacity: .85;
}
.演员:nth-of-type(1) {
background: teal;
transform: translateZ(150px) rotateY(0deg);
}
.演员:nth-of-type(2) {
background: tan;
transform: translateZ(-150px) rotateY(0deg);
}
.演员:nth-of-type(3) {
background: orange;
transform: translateX(-150px) rotateY(90deg);
}
.演员:nth-of-type(4) {
background: pink;
transform: translateX(150px) rotateY(90deg);
}
.演员:nth-of-type(5) {
background: purple;
transform: translateY(-150px) rotateX(90deg);
}
.演员:nth-of-type(6) {
background: yellow;
transform: translateY(150px) rotateX(90deg);
}
@keyframes spin {
from { transform: rotateX(0) rotateY(0) rotateZ(0); }
to { transform: rotateX(0deg) rotateY(360deg) rotateZ(360deg); }
}
</style>
<div id="眼睛">
<div id="舞台">
<span class="演员">前面</span>
<span class="演员">背后</span>
<span class="演员">左侧</span>
<span class="演员">右侧</span>
<span class="演员">顶部</span>
<span class="演员">底部</span>
</div>
</div>
这个讲得太透彻了,而且特别生动形象。黑黑太棒了{:4_199:} 现在知道了为什么之前的旋转四面体可以从左侧看或右侧看了,是因为舞台旋转了角度{:4_204:} “我们有三种方案:其一,让它沿XYZ轴旋转起来,其二,改变眼睛的视点(perspective-origin),其三,让舞台旋转那么一丢丢。”
这个可以放在一起用的吧,让它转起来,而且改变观察角度。 红影 发表于 2023-2-3 12:19
这个讲得太透彻了,而且特别生动形象。黑黑太棒了
这个,看了还不懂的话,我就得去找个癞蛤蟆来举例了{:4_170:} 红影 发表于 2023-2-3 12:21
现在知道了为什么之前的旋转四面体可以从左侧看或右侧看了,是因为舞台旋转了角度
对,这是影响因素之一 红影 发表于 2023-2-3 12:23
“我们有三种方案:其一,让它沿XYZ轴旋转起来,其二,改变眼睛的视点(perspective-origin),其三,让舞 ...
perspective一般是固定的,设置好之后不再改变。但,如果需要,是可以改变的,去年就看过一个洋妞程序员演示 perspective-origin 的效果,她使用JS去改变视点的。 感谢老师的代码分享,问好!{:4_180:} 这个6面体的制作菱角就很分明了,整体我做4面的时候就发现立体感没有完全出来,我就把图片压了边,加了颜色,这样出来效果漂亮,而且边角透明,可以看见背面图片的一角,4面的效果也是特别棒棒的。
六面体 的立体感就更加好了{:4_178:} 马黑黑 发表于 2023-2-3 12:34
这个,看了还不懂的话,我就得去找个癞蛤蟆来举例了
哈哈,还是别去折腾癞蛤蟆了,它们还在冬眠呢{:4_189:} 马黑黑 发表于 2023-2-3 12:35
对,这是影响因素之一
这个总算明白了。 马黑黑 发表于 2023-2-3 12:37
perspective一般是固定的,设置好之后不再改变。但,如果需要,是可以改变的,去年就看过一个洋妞程序员 ...
那样还要好,可以动态的连续改变了。 红影 发表于 2023-2-3 17:10
哈哈,还是别去折腾癞蛤蟆了,它们还在冬眠呢
屋里有暖气 红影 发表于 2023-2-3 17:13
那样还要好,可以动态的连续改变了。
演示需要可以,一般不会去改变视点 梦缘 发表于 2023-2-3 13:58
感谢老师的代码分享,问好!
{:4_191:} 红影 发表于 2023-2-3 17:12
这个总算明白了。
不容易{:4_173:} 小辣椒 发表于 2023-2-3 17:02
这个6面体的制作菱角就很分明了,整体我做4面的时候就发现立体感没有完全出来,我就把图片压了边,加了颜色 ...
压边面板之间的边界清晰一些 小辣椒 发表于 2023-2-3 17:02
六面体 的立体感就更加好了
{:4_181:}