阶段六开发进度
This commit is contained in:
1 parent
f27167621c
commit
b98d67fdab
5 files changed
+309
-66
No files matched your search
+49
-11
@@ -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>
|
||||
|
||||
|
||||
Reference in new issue
Block a user