macro-clips slice 4d: button canvas runtime + pointer handling
- ButtonInstance gains optional x/y; new instances default to stage center and setButtonInstancePosition updates them - useButtonRuntime composable holds per-instance state/disabled overrides (runtime-only, not persisted). getState layers the runtime disabled override on top of the persisted initial flag. - store.actionRuntime's setButtonState/setButtonDisabled now drive useButtonRuntime instead of mutating the scene, so script-triggered state changes stay ephemeral - CanvasStage renders each visible (playhead >= startTime) button instance inside a <v-group> at (bi.x, bi.y), drawing the current state's layer stack with draggable/listening disabled plus a transparent hit-rect sized to the clip's hitArea or the bbox of the first rect/circle layer. The group wires mouseenter/leave/ down/up into the state machine and fires onClick via evalAction. - exportPlayer runtime mirrors the same thing: getEffectiveButtonState, per-frame buttonHits array, canvas-level mousemove/down/up handlers do their own hit testing in scene space. RUNTIME_VERSION bumped to 5. - tests cover useButtonRuntime defaults/overrides and the store.actionRuntime → runtime-state roundtrip via label resolution. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
024883943b
commit
f61f90d613
6 files changed
+412
-8
No files matched your search
+21
-6
@@ -27,6 +27,7 @@ import {
|
||||
scriptFramesInRange,
|
||||
type ActionRuntime,
|
||||
} from '@/lib/actions'
|
||||
import { useButtonRuntime } from '@/composables/useButtonRuntime'
|
||||
|
||||
const uid = () => Math.random().toString(36).slice(2, 10)
|
||||
|
||||
@@ -1215,10 +1216,18 @@ export const useSceneStore = defineStore('scene', {
|
||||
buttonClipId,
|
||||
clipTrackId,
|
||||
startTime: Math.max(0, Math.min(this.scene.duration, startTime)),
|
||||
x: this.scene.canvas.width / 2,
|
||||
y: this.scene.canvas.height / 2,
|
||||
}
|
||||
;(this.scene.buttonInstances ||= []).push(inst)
|
||||
return inst
|
||||
},
|
||||
setButtonInstancePosition(id: string, x: number, y: number) {
|
||||
const target = this.scene.buttonInstances?.find((bi) => bi.id === id)
|
||||
if (!target) return
|
||||
target.x = x
|
||||
target.y = y
|
||||
},
|
||||
removeButtonInstance(id: string) {
|
||||
if (!this.scene.buttonInstances) return
|
||||
this.scene.buttonInstances = this.scene.buttonInstances.filter((bi) => bi.id !== id)
|
||||
@@ -1545,6 +1554,7 @@ export const useSceneStore = defineStore('scene', {
|
||||
if (byId.has(ref)) return ref
|
||||
return byLabel.get(ref) ?? null
|
||||
}
|
||||
const btnRt = useButtonRuntime()
|
||||
return {
|
||||
fps: self.scene.fps,
|
||||
playbackIn: self.playbackIn,
|
||||
@@ -1560,18 +1570,23 @@ export const useSceneStore = defineStore('scene', {
|
||||
pause() {
|
||||
self.pause()
|
||||
},
|
||||
setButtonState() {
|
||||
// No-op in editor for now — runtime-state management lands in
|
||||
// the export-runtime slice together with pointer handling.
|
||||
},
|
||||
setButtonDisabled(ref: string, disabled: boolean) {
|
||||
setButtonState(ref, state) {
|
||||
const id = resolve(ref)
|
||||
if (!id) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[animatr] unknown button ref:', ref)
|
||||
return
|
||||
}
|
||||
self.setButtonInstanceDisabled(id, disabled)
|
||||
btnRt.setState(id, state)
|
||||
},
|
||||
setButtonDisabled(ref, disabled) {
|
||||
const id = resolve(ref)
|
||||
if (!id) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[animatr] unknown button ref:', ref)
|
||||
return
|
||||
}
|
||||
btnRt.setDisabled(id, disabled)
|
||||
},
|
||||
warn(msg: string) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
Reference in new issue
Block a user