阶段六开发进度

This commit is contained in:
tianmo committed 2026-08-29 01:31:08 +08:00
1 parent f27167621c
commit b98d67fdab
5 files changed
+309 -66

No files matched your search

+49 -11
View File
@@ -70,6 +70,10 @@ const vpRef = ref<HTMLElement | null>(null)
const fps = 60
/** 视口当前能显示的帧数(滚轮缩放它)。视口起点恒为第 0 帧,不滚动 */
const viewFrames = ref(51)
// 拖动期间只更新本地预览值,松手后再一次性写回配置。
// 避免 pointermove 每一帧都触发 Stage 清缓存、重置粒子模拟。
const draftDelays = ref<Record<number, number>>({})
const draftDurations = ref<Record<number, number>>({})
const totalFrames = computed(() => store.timeline.totalFrames)
const zoomLabel = computed(() => (viewFrames.value <= 30 ? '放大' : viewFrames.value >= 300 ? '缩小' : '适中'))
@@ -101,13 +105,23 @@ const playPx = computed(() => store.timeline.frame / pxPerF())
function barLeftPx(sys: SysLike) {
const s = (sys.config as any)
// 左端 = 延迟发射时长(delay);左端之前是空白=延迟
return ((s.delay || 0) * fps) / pxPerF()
const delay = draftDelays.value[sys.id] ?? Math.max(0, Number(s.delay) || 0)
return (delay * fps) / pxPerF()
}
function maxParticleLife(sys: SysLike) {
const s = (sys.config as any)
const lifeMin = Math.max(0, Number(s.lifeMin) || 0)
const lifeMax = Math.max(0, Number(s.lifeMax) || 0)
return s.lifeMode === 'fixed' ? lifeMin : Math.max(lifeMin, lifeMax)
}
function barWidthPx(sys: SysLike) {
const s = (sys.config as any)
// 宽度 = 粒子系统总时长 = 发射时长(duration) + 粒子最大生命(lifeMax)
// 宽度 = 粒子系统总时长 = 发射时长(duration) + 当前模式下的最大粒子生命
// 即从 delay 开始,覆盖到最后一个粒子消失,右端与 totalFrames 对齐
return ((s.duration || 0) * fps + (s.lifeMax || 0) * fps) / pxPerF()
const duration = draftDurations.value[sys.id] ?? Math.max(0, Number(s.duration) || 0)
return ((duration + maxParticleLife(sys)) * fps) / pxPerF()
}
// 滚轮缩放:只改变视口显示的帧数(左侧恒为 0 帧),不改变播放时长/totalFrames
@@ -137,32 +151,56 @@ function onScrubDown(e: PointerEvent) {
function onBarDown(e: PointerEvent, sys: SysLike) {
e.stopPropagation()
const startX = e.clientX
const startDelay = (sys.config as any).delay || 0
const startDelay = Math.max(0, Number((sys.config as any).delay) || 0)
const secPerPx = pxPerF() / fps
const move = (ev: PointerEvent) => {
const dSec = (ev.clientX - startX) * secPerPx
let nd = Math.max(0, Math.min(startDelay + dSec, (store.timeline.totalFrames / fps)))
;(sys.config as any).delay = Math.round(nd * fps) / fps
const nd = Math.max(0, Math.min(startDelay + dSec, 600))
draftDelays.value[sys.id] = Math.round(nd * fps) / fps
}
const up = () => { window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up) }
const finish = (commit: boolean) => {
window.removeEventListener('pointermove', move)
window.removeEventListener('pointerup', up)
window.removeEventListener('pointercancel', cancel)
const value = draftDelays.value[sys.id]
delete draftDelays.value[sys.id]
if (commit && value != null && value !== startDelay) (sys.config as any).delay = value
}
const up = () => finish(true)
const cancel = () => finish(false)
window.addEventListener('pointermove', move)
window.addEventListener('pointerup', up)
window.addEventListener('pointercancel', cancel)
}
// 拖动粒子条右缘:调整发射时长 duration
function onBarResizeDown(e: PointerEvent, sys: SysLike) {
e.stopPropagation()
const startX = e.clientX
const startDur = (sys.config as any).duration || 0.1
const startDur = Math.max(0, Number((sys.config as any).duration) || 0)
const secPerPx = pxPerF() / fps
let moved = false
const move = (ev: PointerEvent) => {
moved = true
const dSec = (ev.clientX - startX) * secPerPx
let nd = Math.max(0.1, Math.min(startDur + dSec, store.timeline.totalFrames / fps))
;(sys.config as any).duration = Math.round(nd * fps) / fps
let nd = Math.max(0, Math.min(startDur + dSec, 600))
// 有限发射最短为 1 帧;0 继续保留“无限发射”的既有语义。
if (nd > 0) nd = Math.max(1 / fps, nd)
draftDurations.value[sys.id] = Math.round(nd * fps) / fps
}
const up = () => { window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up) }
const finish = (commit: boolean) => {
window.removeEventListener('pointermove', move)
window.removeEventListener('pointerup', up)
window.removeEventListener('pointercancel', cancel)
const value = draftDurations.value[sys.id]
delete draftDurations.value[sys.id]
if (commit && moved && value != null && value !== startDur) (sys.config as any).duration = value
}
const up = () => finish(true)
const cancel = () => finish(false)
window.addEventListener('pointermove', move)
window.addEventListener('pointerup', up)
window.addEventListener('pointercancel', cancel)
}
</script>