0.9.1: jumpToScene action + auto-play on scene switch
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
b57994c754
commit
b9f2ba9e47
9 files changed
+436
-15
No files matched your search
@@ -19,6 +19,10 @@ const markerNames = computed(() =>
|
||||
(store.scene.markers ?? []).map((m) => m.name),
|
||||
)
|
||||
|
||||
const sceneOptions = computed(() =>
|
||||
store.project.scenes.map((s) => ({ id: s.id, name: s.name })),
|
||||
)
|
||||
|
||||
const ACTION_TYPES: Action['type'][] = [
|
||||
'play',
|
||||
'pause',
|
||||
@@ -26,6 +30,7 @@ const ACTION_TYPES: Action['type'][] = [
|
||||
'jumpTo',
|
||||
'jumpToFrame',
|
||||
'jumpToMarker',
|
||||
'jumpToScene',
|
||||
'script',
|
||||
]
|
||||
|
||||
@@ -45,6 +50,14 @@ function makeDefault(type: Action['type']): Action {
|
||||
return { type: 'jumpToFrame', frame: 0 }
|
||||
case 'jumpToMarker':
|
||||
return { type: 'jumpToMarker', name: markerNames.value[0] ?? '' }
|
||||
case 'jumpToScene': {
|
||||
// Default to the first scene that ISN'T the current one so the
|
||||
// action is immediately useful; fall back to the first if there's
|
||||
// only one scene.
|
||||
const others = sceneOptions.value.filter((s) => s.id !== store.project.activeSceneId)
|
||||
const target = others[0] ?? sceneOptions.value[0]
|
||||
return { type: 'jumpToScene', sceneId: target?.id ?? '' }
|
||||
}
|
||||
case 'script':
|
||||
return { type: 'script', source: '' }
|
||||
}
|
||||
@@ -152,6 +165,18 @@ function onTypeChange(index: number, newType: Action['type']) {
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else-if="a.type === 'jumpToScene'" class="params">
|
||||
<label>
|
||||
{{ t('actions.param.scene') }}
|
||||
<select
|
||||
:value="a.sceneId"
|
||||
@change="setAt(i, { type: 'jumpToScene', sceneId: ($event.target as HTMLSelectElement).value })"
|
||||
>
|
||||
<option v-if="!sceneOptions.length" value="" disabled>{{ t('actions.noScenes') }}</option>
|
||||
<option v-for="s in sceneOptions" :key="s.id" :value="s.id">{{ s.name }}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else-if="a.type === 'script'" class="params">
|
||||
<label class="block">
|
||||
{{ t('actions.param.source') }}
|
||||
|
||||
@@ -42,17 +42,53 @@ async function startPreview() {
|
||||
const end = rangeOut.value
|
||||
const head = store.playhead
|
||||
const initialTime = head < start || head >= end ? start : head
|
||||
player = mountPlayer(el, store.scene, {
|
||||
loop: true,
|
||||
onFrame(time: number, paused: boolean) {
|
||||
localTime.value = time
|
||||
playing.value = !paused
|
||||
},
|
||||
})
|
||||
player.seek(initialTime)
|
||||
mountAt(el, initialTime, true)
|
||||
requestBrowserFullscreen()
|
||||
}
|
||||
|
||||
/** Mount (or re-mount) the player against `store.scene` and seek to `time`.
|
||||
* Used both by the initial `startPreview` and when a scripted / button
|
||||
* `jumpToScene` swaps the active page under us — we blow away the old
|
||||
* player and rebuild at the same playhead so visual continuity stays
|
||||
* intact across the switch. */
|
||||
function mountAt(el: HTMLElement, time: number, wasPlaying: boolean) {
|
||||
player = mountPlayer(el, store.scene, {
|
||||
loop: true,
|
||||
onFrame(t: number, paused: boolean) {
|
||||
localTime.value = t
|
||||
playing.value = !paused
|
||||
},
|
||||
onJumpToScene(sceneId: string) {
|
||||
// Route the runtime's jump into the store. The watcher below
|
||||
// picks up the activeSceneId change and re-mounts the player
|
||||
// against the new page at the current time.
|
||||
store.switchScene(sceneId)
|
||||
},
|
||||
})
|
||||
player.seek(time)
|
||||
if (!wasPlaying) player.pause()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => store.project.activeSceneId,
|
||||
() => {
|
||||
if (!props.modelValue) return
|
||||
const el = frameEl.value
|
||||
if (!el) return
|
||||
// Every scene switch auto-plays on the new page — matches the
|
||||
// store-level switchScene contract. We keep the current playhead
|
||||
// so animation continuity stays intact across the swap.
|
||||
const keepTime = player ? player.getTime() : store.playhead
|
||||
destroyPlayer()
|
||||
nextTick(() => {
|
||||
if (!props.modelValue) return
|
||||
const still = frameEl.value
|
||||
if (!still) return
|
||||
mountAt(still, keepTime, true)
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
function stopPreview() {
|
||||
destroyPlayer()
|
||||
playing.value = false
|
||||
|
||||
Reference in new issue
Block a user