three 楼层动画

1,444 阅读4分钟

楼层动画是一个非常常见的建筑拆解效果,实现起来也很简单。实现的路径大概有几种,一种是每层楼都上升同样的距离;另一种是在前面的基础上再做一个抽屉效果,特写某个选定楼层;最后一种效果最好,但相对少见,每层楼运动速率相隔是一个和楼层序号相关的等差数列,可以实现顺滑的效果,类似以前 windows 系统蜘蛛纸牌的效果,这次就来实现这个效果,包括如何在 Blender 里做简单处理来导入 three.js 使用。

three 楼层动画.gif

模型处理

模型需要预先按照每个楼层进行分组,BlenderCollection 不等同于 three 里的 group, 你需要使用 empty group 才能在读取模型的时候对应才能看到几何体分组。

Blender.png

这是标准 sketchFab 载的模型处理后的结构。需要注意的是每层楼的命名需要按照统一的命名方式,比如我用 Floor.{num} 代表特定的楼层。

在新的 Blender4.1 版本已经可以导出 Collection 了,方法是:

Blender4.1.png

分析过程

运动状态

通过结果观察到有那么几种状态:

Status.png

除了判定这两种状态下需要的运动形式,还需要有一个 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=S(B+s(l+1)L) P = S * (\frac{B + s * (l + 1)}{L})

当向下运动的时候,过程变化:

P=S(B+s(Ll)L) P = S * (\frac{B + s * (L - l)}{L})

其中,各个变量分别表示为:

{S:帧动画运动幅度B:基础运动速率s:序号l影响权重l:代表楼层序列的序号L:序列的总长\begin{cases} & S: 帧动画运动幅度 \\ \\ & B: 基础运动速率 \\ \\ & s: 序号 l 影响权重 \\ \\ & l: 代表楼层序列的序号 \\ \\ & L: 序列的总长 \\ \\ \end{cases}

基础思想是根据序号的等差数列,然后再根据运动过程状态来进一步构造等式去实现你想要的速率变化过程。

在这个基础上你还可以做的是把运动过程拉长,然后对这个 P 应用缓动函数来 鬼畜 你的运动过程,还可以对序列进行 reverse,从而实现另一种效果(你 try try)。

最后比较需要注意的是如何判断运动状态 isAnimating , 我直接使用全量的状态进行判断。即当所有楼层的过程 P 到达 1 的时候该轮动画完成。

完结

然后就撒花,希望对你有帮助,这个方法你可以搬到其他场景下,比如 千手观音?哈哈。 还有能优化的地方,欢迎留言转发。关注博主,带给你深入有用的好货。

在线体验地址 ,点个小星星,手有余香🌹:

wwjll.github.io/three-pract…