菇凉、竹子和CSS三维动画
本帖最后由 马黑黑 于 2025-7-9 16:41 编辑 <br /><br /><style>.papa {
margin: 20px auto;
left: calc(50% - 81px);
transform: translate(-50%);
width: 1200px;
height: 700px;
border: 1px solid olive;
background: url('https://638183.freep.cn/638183/t24/5/cshow.jpg') no-repeat center/cover;
perspective: 800px;
position: relative;
}
.lzwrap {
position: absolute;
width: 100%;
height: 100%;
background: none ;
transform-style: preserve-3d;
display: grid;
place-items: center;
}
.lzwrap::before {
position:absolute;
content: '点击小球开始动画';
left: 20px;
bottom: 20px;
}
.lzwrap::after {
position: absolute;
content: url('https://638183.freep.cn/638183/small/vuzi.png');
left: 20%;
bottom: 0;
background: url('https://638183.freep.cn/638183/small/vuzi.png') no-repeat center/cover;
transform: rotateY(15deg);
}
li-zi {
--size: 50px;
position: absolute;
width: var(--size);
height: var(--size);
border-radius: 50%;
background: linear-gradient(35deg, lightgreen, skyblue);
transform: rotateY(0) translate3d(0, 100px, 400px);
cursor: pointer;
}
.ani {
animation: rot 16s ease-in-out;
}
@keyframes rot {
to {
transform: rotateY(720deg) translate3d(0, 100px, 400px) rotateY(-720deg);
}
}
</style>
<div class="papa">
<div class="lzwrap">
<li-zi title="点击运行"></li-zi>
</div>
</div>
<script>
var lz = document.querySelector('li-zi');
var isRunnig = false;
lz.onclick = () => {
if (isRunnig) return;
lz.classList.add('ani');
}
lz.onanimationstart = () => isRunnig = true;
lz.onanimationend = () => {
if (!isRunnig) return;
lz.classList.remove('ani');
isRunnig = false;
};
</script>
本帖最后由 马黑黑 于 2025-7-9 17:45 编辑
本帖演示小球在CSS三维动画时其与竹子、菇凉的位置遮挡关系。菇凉是外层容器的背景图片的内容,不存在于3d场景中,因此小球总是从她面前经过;竹子则与小球一样同属3d景物,存在三维空间物体间的遮挡关系,因此小球可以再竹子前后绕行。
具体代码如下:
<style>
.papa {
margin: 20px auto;
width: 1200px;
height: 700px;
border: 1px solid olive;
background: url('https://638183.freep.cn/638183/t24/5/cshow.jpg') no-repeat center/cover;
perspective: 800px;
position: relative;
}
.lzwrap {
position: absolute;
width: 100%;
height: 100%;
background: none ;
transform-style: preserve-3d;
display: grid;
place-items: center;
}
.lzwrap::before {
position:absolute;
content: '点击小球开始动画';
left: 20px;
bottom: 20px;
}
.lzwrap::after {
position: absolute;
content: url('https://638183.freep.cn/638183/small/vuzi.png');
left: 20%;
bottom: 0;
background: url('https://638183.freep.cn/638183/small/vuzi.png') no-repeat center/cover;
transform: rotateY(15deg);
}
li-zi {
--size: 50px;
position: absolute;
width: var(--size);
height: var(--size);
border-radius: 50%;
background: linear-gradient(35deg, lightgreen, skyblue);
transform: rotateY(0) translate3d(0, 100px, 400px);
cursor: pointer;
}
.ani {
animation: rot 16s ease-in-out;
}
@keyframes rot {
to {
transform: rotateY(720deg) translate3d(0, 100px, 400px) rotateY(-720deg);
}
}
</style>
<div class="papa">
<div class="lzwrap">
<li-zi title="点击运行"></li-zi>
</div>
</div>
<script>
var lz = document.querySelector('li-zi');
var isRunnig = false;
lz.onclick = () => {
if (isRunnig) return;
lz.classList.add('ani');
}
lz.onanimationstart = () => isRunnig = true;
lz.onanimationend = () => {
if (!isRunnig) return;
lz.classList.remove('ani');
isRunnig = false;
};
</script>
若有错误,敬请斧正,本人将感激不尽!
菇凉、竹子和CSS三维动画相关解释:
先讲一下设置HTML为三层结构的目的:确保小球的transform 3d运动全程可见。设若只设计两层HTML标签结构,则当容器标签设置了背景,做3d运动的元素即第二层标签元素将在背向观者时不可见,而通常情况下我们有需要容器元素有背景,为此,容器元素和做3d运动子元素之间应该建立一个中间层,该层元素设置背景为无(推荐)或透明,即transparent或类似rgba(0,0,0,0)颜色表达体系,它之下才是做3d运动的元素。
解释核心代码:
一、容器元素(class="papa"的div)设置透视视图
perspective: 800px;
透视视图也可以理解为相机的位置,因它的存在而在容器元素场景下产生景深效果,后续其内的子元素或孙辈元素因之而拥有渲染3d效果的舞台。perspective属性值会直接影响子孙元素在Z轴上的效果,因为景深的方向和Z轴同向,是观者和屏幕表面(xy面)的视线方向,遵循自然规律的远小近大的规则。
二、中间层元素(class="lzwrap")设置3d转换形式
transform-style: preserve-3d;
真正的三维场景要求物体间拥有真实的遮挡关系,以上设置确保这个关系的正确建立。transform-style属性缺省时默认采用flat值,该值下的3d场景不能虚拟真实世界的三维物体间的遮挡关系,必须设置值为preserve-3d才可以。该属性也可以设置在容器层元素,但既然有中间层,强烈建议将其放在中间层。另外,非常重要的是,设置了transform-style: preserve-3d;的元素不能设置防溢出属性overflow: hidden;,否则preserve-3d的设置无效。
另外,在中间层标签里,通过grib网格布局及其相关属性规范了其下子元素的位置即上下左右居中。子元素的初始位置与其后续的动画有关联,后面会继续解释。
三、呈现三维效果的元素transform转换设置
这里我们使用了一个自定义标签 li-zi,这是依据CSS3的规范设计的标签,标签名称需要由连接符 - 将头尾两部分连接起来。使用自定义标签主要是为了让标签名称更具备语义化含义。此为外话。
li-zi标签初始化了一些CSS属性设置,其中下面这句——
transform: rotateY(0) translate3d(0, 100px, 400px);
——它的作用是告诉浏览器li-zi元素在Y轴旋转0度、在X轴上平移0像素、在Y轴上平移100像素、在Z轴上平移400像素。就是说,通过transform属性初始化了li-zi的位置。
前面提到,中间层元素规范了子元素的绝对居中,此处代码,通过Y轴上的位置设置li-zi元素安放在了预设的位置:纵向偏下的地方(0 是Y轴中心)。
四、关键帧动画 keyframes 设置
关键帧动画延续使用li-zi元素的transform属性,外加一个倒回来的Y轴上的旋转以确保li-zi在自转时不变形:
@keyframes rot {
to {
transform: rotateY(720deg) translate3d(0, 100px, 400px) rotateY(-720deg);
}
}
这里,rotateY() 在先,translate3d() 在后,这样能令运行关键帧动画的元素做弧线移动。注意查看 translate3d 小括号里面的参数,和初始化值保持一致,就是说,通过此transform转换设置,仅通过li-zi的自转就能让li-zi做弧线行进运动,其原理是,li-zi元素在Y轴上做自转的同时在Z轴上平移400px,换言之,li-zi的每一次自转都会在Z轴上和父层元素的中心点 0 保持 400px 的距离,为了维持这个距离,每一次的转动都会绕Z轴做弧线位移。这里还需要理解一下li-zi在Y轴上离开中心点 0 有 100px 的距离但自转时不会在Y轴上产生弧线位置移动的原理——因为li-zi的自转是在Y轴上进行的自转。然后是li-zi自转的倒转,前面rotateY多少度,最后跟着倒回多少度,因此运动过程中li-zi并没有旋转,都实时抵消了,但实现了li-zi的弧线运动。
补充一句:自转角度设置为720度意味着li-zi一个运动周期内做两圈弧线运动,圆的度数是360度。
五、li-zi元素运行关键帧动画 animation 设置
animation: rot 16s ease-in-out;
这句可以直接放在 li-zi { ... } 属性体内,本例处于演示需要,把它放在一个类标签里,这样更方便实现JS交互功能。
这球太色了{:4_358:} 樵歌 发表于 2025-7-9 17:48
这球太色了
额,这是彩球 “运动过程中li-zi并没有旋转,都实时抵消了,但实现了li-zi的弧线运动。”
这个挺难理解的。如果没这个旋转,就是400之间的直线运动了吧,有这个菜带出的弧线运动?{:4_187:} 这个帖子用竹子做参照,很清楚地标示了中间层的作用{:4_204:} 红影 发表于 2025-7-9 21:28
“运动过程中li-zi并没有旋转,都实时抵消了,但实现了li-zi的弧线运动。”
这个挺难理解的。如果没这个旋 ...
道理是酱紫:
rotateY + translateZ 驱动了 lz 的弧线位移;
后面的反向自转 rotateY 则在弧线位移之后实时抵消了自转。 红影 发表于 2025-7-9 21:30
这个帖子用竹子做参照,很清楚地标示了中间层的作用
总体弧线运动的知识体系和过去介绍的2d关键帧动画中圆弧运动是一致的,这里只是引入了 Z轴,看着复杂了不少。 马黑黑 发表于 2025-7-9 21:54
道理是酱紫:
rotateY + translateZ 驱动了 lz 的弧线位移;
嗯嗯,和之前的旋转加平移导致的弧线一样,只是这里抵消了自转{:4_187:} 马黑黑 发表于 2025-7-9 21:55
总体弧线运动的知识体系和过去介绍的2d关键帧动画中圆弧运动是一致的,这里只是引入了 Z轴,看着复杂了不 ...
是的,多了个z轴,感觉一下很难理解了。 红影 发表于 2025-7-9 23:05
嗯嗯,和之前的旋转加平移导致的弧线一样,只是这里抵消了自转
对,原理是酱紫的 红影 发表于 2025-7-9 23:06
是的,多了个z轴,感觉一下很难理解了。
这个其实不应该 马黑黑 发表于 2025-7-10 12:51
对,原理是酱紫的
平面的和三维的,原理都一样。 马黑黑 发表于 2025-7-10 12:52
这个其实不应该
嗯,一个是在屏幕上绕圈,一个是在纵深上绕圈。 红影 发表于 2025-7-10 21:32
嗯,一个是在屏幕上绕圈,一个是在纵深上绕圈。
{:4_181:} 红影 发表于 2025-7-10 21:32
平面的和三维的,原理都一样。
差不多的 马黑黑 发表于 2025-7-10 22:18
平面打成了屏幕,哈哈,总打错字{:4_173:} 马黑黑 发表于 2025-7-10 22:18
差不多的
理解了平面的,三维的也比较好理解了。 红影 发表于 2025-7-10 23:08
理解了平面的,三维的也比较好理解了。
点线面体,一步一步扩展