93 lines
3.9 KiB
JavaScript
93 lines
3.9 KiB
JavaScript
import { build } from 'esbuild'
|
|
|
|
const bundled = await build({
|
|
bundle: true,
|
|
stdin: {
|
|
contents: [
|
|
"export { bakeLoopFrames, closeLoopFrameSeam } from './src/core/loopFrameBaker.ts'",
|
|
"export { loopPlaybackRange, loopSegmentTiming } from './src/core/loopSegmentTiming.ts'",
|
|
"export { normalizePlaybackRange, resizePlaybackRange } from './src/timeline/playbackRange.ts'",
|
|
].join('\n'),
|
|
resolveDir: process.cwd(),
|
|
sourcefile: 'timeline-test-entry.ts',
|
|
},
|
|
format: 'esm',
|
|
platform: 'node',
|
|
write: false,
|
|
})
|
|
const source = bundled.outputFiles[0].text
|
|
const api = await import(`data:text/javascript;base64,${Buffer.from(source).toString('base64')}`)
|
|
|
|
const timing = api.loopSegmentTiming({
|
|
loopDurationFrames: 30,
|
|
generateLoopStartAnimation: true, loopStartUseCustomDuration: true,
|
|
loopStartDurationFrames: 20,
|
|
generateLoopEndAnimation: true, loopEndUseCustomDuration: true,
|
|
loopEndDurationFrames: 40,
|
|
})
|
|
if (JSON.stringify(timing) !== JSON.stringify({ start: 20, loop: 30, end: 40, total: 90 })) {
|
|
throw new Error('开始、循环、结束三段时长计算错误')
|
|
}
|
|
|
|
const automaticTiming = api.loopSegmentTiming({
|
|
lifeMode: 'random', lifeMin: 0.5, lifeMax: 1.5,
|
|
loopDurationFrames: 30,
|
|
generateLoopStartAnimation: true, loopStartUseCustomDuration: false, loopStartDurationFrames: 20,
|
|
generateLoopEndAnimation: true, loopEndUseCustomDuration: false, loopEndDurationFrames: 40,
|
|
}, 30)
|
|
if (automaticTiming.start !== 45 || automaticTiming.end !== 45) {
|
|
throw new Error('未指定时长时应按最大粒子生命周期自动计算')
|
|
}
|
|
|
|
const loopRange = api.loopPlaybackRange({
|
|
delay: 0, loopDurationFrames: 80,
|
|
generateLoopStartAnimation: true, loopStartUseCustomDuration: true, loopStartDurationFrames: 20,
|
|
generateLoopEndAnimation: true, loopEndUseCustomDuration: true, loopEndDurationFrames: 20,
|
|
}, 30)
|
|
if (loopRange.start !== 20 || loopRange.end !== 100) {
|
|
throw new Error('时间轴上的闭合自循环范围计算错误')
|
|
}
|
|
|
|
const normalized = api.normalizePlaybackRange(10, 10, 100)
|
|
if (normalized.start !== 10 || normalized.end !== 11) throw new Error('播放范围必须至少包含两个帧位置')
|
|
const resized = api.resizePlaybackRange({ start: 5, end: 59 }, 60, 90)
|
|
if (resized.start !== 5 || resized.end !== 89) throw new Error('全范围播放没有随总时长扩展')
|
|
|
|
function state(x) {
|
|
return [{
|
|
boneName: 'p_0', spawnId: 3, x, y: 0, rotation: 0,
|
|
scaleX: 1, scaleY: 1, alpha: 1, colorHex: 0xffffff,
|
|
resourceId: 1, imageFrameIndex: 0, imageVisible: true, active: true,
|
|
}]
|
|
}
|
|
const emitter = {
|
|
step: 0,
|
|
activeCount: 1,
|
|
reset() { this.step = 0; this.activeCount = 1 },
|
|
restartEmissionCycle() { this.step = 0 },
|
|
update(_dt, mode) { if (mode === 'off') this.activeCount = 0; else this.step++; return this.capture() },
|
|
capture() { return this.activeCount ? state(this.step) : [] },
|
|
apply() {},
|
|
}
|
|
const system = {
|
|
config: {
|
|
lifeMode: 'fixed', lifeMin: 1, lifeMax: 1, delay: 0,
|
|
loopDurationFrames: 3,
|
|
generateLoopStartAnimation: true, loopStartUseCustomDuration: true, loopStartDurationFrames: 2,
|
|
generateLoopEndAnimation: true, loopEndUseCustomDuration: true, loopEndDurationFrames: 4,
|
|
},
|
|
}
|
|
api.bakeLoopFrames(system, emitter, 3, 10)
|
|
const frames = system.frames
|
|
if (frames.length !== 10) throw new Error('组合动画总帧数错误')
|
|
if (JSON.stringify(frames[2]) !== JSON.stringify(frames[5])) throw new Error('启动末帧、循环首尾帧没有精确衔接')
|
|
if (frames[0].some((particle) => particle.imageVisible !== false || particle.alpha !== 0)) {
|
|
throw new Error('启动段首帧应把衔接帧粒子反推到未出生状态')
|
|
}
|
|
if (frames.slice(0, 2).some((frame) => frame.some((particle) => particle.x > frames[2][0].x))) {
|
|
throw new Error('启动段不应包含衔接帧之后的额外循环')
|
|
}
|
|
if (frames[9].length !== 0) throw new Error('结束段末帧应为空')
|
|
|
|
console.log('时间轴 A-B 范围与开始/循环/结束三段边界校验通过')
|