楼层动画是一个非常常见的建筑拆解效果,实现起来也很简单。实现的路径大概有几种,一种是每层楼都上升同样的距离;另一种是在前面的基础上再做一个抽屉效果,特写某个选定楼层;最后一种效果最好,但相对少见,每层楼运动速率相隔是一个和楼层序号相关的等差数列,可以实现顺滑的效果,类似以前 windows
系统蜘蛛纸牌的效果,这次就来实现这个效果,包括如何在 Blender
里做简单处理来导入 three.js
使用。
模型处理
模型需要预先按照每个楼层进行分组,Blender
里 Collection
不等同于 three
里的 group
, 你需要使用 empty group
才能在读取模型的时候对应才能看到几何体分组。
这是标准 sketchFab
载的模型处理后的结构。需要注意的是每层楼的命名需要按照统一的命名方式,比如我用 Floor.{num}
代表特定的楼层。
在新的 Blender4.1 版本已经可以导出 Collection
了,方法是:
分析过程
运动状态
通过结果观察到有那么几种状态:
除了判定这两种状态下需要的运动形式,还需要有一个 reset
函数来重置所有状态。
对于这个动画,每个楼层都会有自己的运动状态,还会有全局的动画状态,判断响应当前是否有执行动画从而决定是否响应新的状态。
设置状态
Object3D
的大部分属性是 three
引擎相关属性,当有额外的信息需要写入的时候,最好写到 .userData
字段里,保证信息才不会参与引擎的渲染或其他逻辑。所以对于每个楼层,我们需要三条基础信息来满足运动:
progress
: 楼层的运动过程 [0 , 1] 的范围。originY
: 当前楼层运动方向的原位置,用于状态重置或其他。我这里楼层的Object3D
均直接使用了世界坐标,因此都是0
, 如果你的楼层有层级关系,它有可能不是0
。status
: 状态,用normal
表示未抬升状态,rised
表示抬升状态。也可以不要这个属性,判断.position.y == originY
也可以知道状态,这里额外增加一个区分。
除此之外,使用 isAnimating
全局变量作为当前是否有动画在运行的 flag
。
计算需要运动的序号
由前面的状态分析可知不同状态下触发运动有不同的运动目标,直接写出函数, 入参 direction
表示筛选的这些楼层需要运动的方向 :
// select floors need to animate
function selectGroups(floorIndex, direction) {
if (direction == "up") {
return floors.filter((floor, index) => {
return floor.userData.status == "normal" && index > floorIndex;
});
} else if (direction == "down") {
return floors.filter((floor, index) => {
return floor.userData.status == "rised" && index <= floorIndex;
});
}
}
这个时候可以对筛选结果统一进行一个同一运动方向的偏移测试下结果是否正确。
序列运动
要实现 gif
里的这个运动,显然需要根据楼层序号来构造一个等差数列,这里简单列个基础的等式,可以调节参数来控制这个速率的变化:
当向上运动的时候,过程变化:
当向下运动的时候,过程变化:
其中,各个变量分别表示为:
基础思想是根据序号的等差数列,然后再根据运动过程状态来进一步构造等式去实现你想要的速率变化过程。
在这个基础上你还可以做的是把运动过程拉长,然后对这个 P
应用缓动函数来 鬼畜 你的运动过程,还可以对序列进行 reverse
,从而实现另一种效果(你 try try)。
最后比较需要注意的是如何判断运动状态 isAnimating
, 我直接使用全量的状态进行判断。即当所有楼层的过程 P
到达 1
的时候该轮动画完成。
完结
然后就撒花,希望对你有帮助,这个方法你可以搬到其他场景下,比如 千手观音?哈哈。 还有能优化的地方,欢迎留言转发。关注博主,带给你深入有用的好货。
在线体验地址 ,点个小星星,手有余香🌹: