阶段10开发完成
This commit is contained in:
1 parent
cffe02c1ec
commit
c239171cc4
21 files changed
+997
-36
No files matched your search
+24
-2
@@ -10,7 +10,7 @@
|
||||
<button class="tl-btn" @click="store.nextFrame()" title="后一帧">▶</button>
|
||||
<button class="tl-btn" @click="store.toEnd()" title="移动到最后一帧">⏭</button>
|
||||
<button class="tl-btn" :class="{ on: store.timeline.loop }" @click="store.toggleLoop()" title="循环">⟳</button>
|
||||
<span class="tl-frame">帧 <b>{{ store.timeline.frame }}</b> / <b>{{ store.timeline.totalFrames }}</b></span>
|
||||
<span class="tl-frame">帧 <b>{{ store.timeline.frame }}</b> / <b>{{ Math.max(0, store.timeline.totalFrames - 1) }}</b></span>
|
||||
<span class="tl-legend"><i class="legend-emission"></i>持续发射 <i class="legend-life"></i>粒子存在 <i class="legend-spine"></i>Spine 动画</span>
|
||||
<div ref="animationMenuRef" class="tl-anim tl-owner-animation">
|
||||
<span>所属动画</span>
|
||||
@@ -91,7 +91,7 @@
|
||||
<span class="lane-name">{{ (sys.config as any).name || '系统 ' + (i + 1) }}</span>
|
||||
<span class="bar-life" :style="{ width: lifeWidthPx(sys) + 'px' }"></span>
|
||||
<span
|
||||
v-if="sys.config.mode === 'stream'"
|
||||
v-if="sys.config.mode === 'stream' && !isLoopEnd(sys)"
|
||||
class="bar-emission"
|
||||
:style="{ width: emissionWidthPx(sys) + 'px' }"
|
||||
>
|
||||
@@ -262,16 +262,37 @@ function barWidthPx(sys: SysLike) {
|
||||
|
||||
function emissionWidthPx(sys: SysLike) {
|
||||
if ((sys.config as any).mode !== 'stream') return 0
|
||||
if (isLoopEnd(sys)) return 0
|
||||
if ((sys.config as any).streamBehavior === 'loop') {
|
||||
const config = sys.config as any
|
||||
const durationFrames = Math.max(1, Math.ceil(Number(config.loopDurationFrames) || 1))
|
||||
if (config.generateLoopStartAnimation) {
|
||||
const warmupCycles = Math.max(1, Math.ceil((maxParticleLife(sys) * fps.value) / durationFrames))
|
||||
return (durationFrames * warmupCycles) / pxPerF()
|
||||
}
|
||||
return durationFrames / pxPerF()
|
||||
}
|
||||
const duration = draftDurations.value[sys.id] ?? Math.max(1 / fps.value, Number((sys.config as any).duration) || 1)
|
||||
return (duration * fps.value) / pxPerF()
|
||||
}
|
||||
|
||||
function lifeWidthPx(sys: SysLike) {
|
||||
const config = sys.config as any
|
||||
if (config.mode === 'stream' && config.streamBehavior === 'loop') {
|
||||
// 启动段和纯循环段的存在时长与发射段完全重合;结束段只显示剩余生命/拖尾。
|
||||
if (!config.generateLoopEndAnimation) return emissionWidthPx(sys)
|
||||
return ((maxParticleLife(sys) * fps.value) + 1) / pxPerF()
|
||||
}
|
||||
// 红色底条表示整个系统“仍有粒子存在”的时长:持续发射窗口 + 最长粒子生命。
|
||||
// 爆发模式的发射窗口为 0,因此只保留最长粒子生命。
|
||||
return emissionWidthPx(sys) + (maxParticleLife(sys) * fps.value) / pxPerF()
|
||||
}
|
||||
|
||||
function isLoopEnd(sys: SysLike) {
|
||||
const config = sys.config as any
|
||||
return config.mode === 'stream' && config.streamBehavior === 'loop' && config.generateLoopEndAnimation
|
||||
}
|
||||
|
||||
function selectedSpineDuration(spine: SpineSceneObject) {
|
||||
return spine.animations.find((animation) => animation.name === spine.selectedAnimation)?.duration || 0
|
||||
}
|
||||
@@ -337,6 +358,7 @@ function onBarDown(e: PointerEvent, sys: SysLike) {
|
||||
// 拖动粒子条右缘:调整发射时长 duration
|
||||
function onBarResizeDown(e: PointerEvent, sys: SysLike) {
|
||||
e.stopPropagation()
|
||||
if (isLoopEnd(sys)) return
|
||||
const startDur = Math.max(0, Number((sys.config as any).duration) || 0)
|
||||
const secPerPx = pxPerF() / fps.value
|
||||
const barLeft = (e.currentTarget as HTMLElement).closest('.lane-bar')?.getBoundingClientRect().left ?? e.clientX
|
||||
|
||||
Reference in new issue
Block a user