修正时间轴末帧,导出补阶跃与循环段边界关键帧

- 时间轴:末帧按时长实际覆盖的整数帧计算,修掉 Spine 时长小数舍入多出的一帧,B 端与回放自动停在最后一帧
- 导出:粒子死亡、骨骼池复用、拖尾假死复活处统一写 curve: stepped 硬切,避免死区插值出滑动轨迹
- 导出:循环持续在开始/循环/结束段边界(如 30f/110f)对所有动画通道补关键帧,便于在 Spine 中直接拆分
- 精简器支持阶跃段,阶跃区间内不再补出过渡关键帧
- 补充 test:spine-json 回归用例:骨骼池复用阶跃边界、循环段边界
This commit is contained in:
tianmo committed 2026-10-09 17:05:53 +08:00
1 parent f249c019bc
commit d6232c9d66
9 files changed
+290 -82

No files matched your search

+22
View File
@@ -1,5 +1,27 @@
export type PlaybackRange = { start: number; end: number }
/**
* 动画时长允许的舍入误差(秒)。
* Spine 等来源把 80 帧在 30fps 下的时长写成 2.667(保留小数),
* 乘回 fps 会得到 80.01 帧。误差必须小于一帧,仅用于吸收这种舍入。
*/
const DURATION_ROUNDING_TOLERANCE_SECONDS = 1e-3
/**
* 把一段时长换算成时间轴上的“末帧索引”。
*
* 末帧索引 = 该时长实际覆盖到的最后一个整数帧,因此:
* 80 帧的时长 → 末帧 80(而不是 81)。若直接对秒数×fps 取 ceil,
* Spine 的小数舍入会让 80 帧被算成 81 帧,回放时末帧后多停一帧。
* 这里改用向下取整,并对接近整数帧的舍入误差做补偿。
*/
export function lastFrameOfDuration(seconds: number, fps: number) {
const safeFps = Math.max(1, Number(fps) || 30)
const durationSeconds = Math.max(0, Number(seconds) || 0)
const frames = durationSeconds * safeFps + DURATION_ROUNDING_TOLERANCE_SECONDS * safeFps
return Math.max(0, Math.floor(frames))
}
export function normalizePlaybackRange(start: unknown, end: unknown, totalFrames: number): PlaybackRange {
const last = Math.max(1, Math.floor(totalFrames) - 1)
const safeStart = Math.min(last - 1, Math.max(0, Math.floor(Number(start) || 0)))