diff --git a/replica/src/character.js b/replica/src/character.js index 9c74c93..2f94281 100644 --- a/replica/src/character.js +++ b/replica/src/character.js @@ -69,6 +69,10 @@ this.driven = !!opts.driven; this.paused = !!opts.paused; this.reduceMotion = opts.reduceMotion ?? (typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches); + // autoTricks gates the AUTOMATIC spin sources (wantPn, celebrate re-arm, + // idle trick loop, shape-change trick). State-inherent rotation + // (humming/loading ovSpin) and explicit spinOnce stay untouched. + this.autoTricks = opts.autoTricks !== false; this.badgeColor = opts.badgeColor || "var(--gb-badge, #1d9bf0)"; this.sizePx = opts.sizePx || null; this.eyeColor = opts.eyeColor || null; @@ -642,6 +646,10 @@ this.playlistHold = next; } + setAutoTricks(on) { + this.autoTricks = !!on; + } + playback() { const remain = this.playlistHold ? (this.eyeRemain || 0) @@ -893,7 +901,7 @@ } _cycleShapeTrick() { - if (this.reduceMotion || this.paused || this.driven || this.manualHold) return; + if (this.reduceMotion || this.paused || this.driven || this.manualHold || !this.autoTricks) return; this.trickCycle = (this.trickCycle + 1) % 5; this.wildWide = false; if (this.trickCycle === 0) this._pn(1); @@ -1039,7 +1047,7 @@ this.ctx.wantBlink = false; } if (!this.manualHold && this.ctx.wantPn) { - this._pn(...this.ctx.wantPn); + if (this.autoTricks) this._pn(...this.ctx.wantPn); this.ctx.wantPn = null; } if (!this.manualHold && this.ctx.wantBurst) { @@ -1059,12 +1067,12 @@ this._stepOverlay(now); } - if (!this.manualHold && this.celebrateAt > 0 && now >= this.celebrateAt && !this.trick) { + if (this.autoTricks && !this.manualHold && this.celebrateAt > 0 && now >= this.celebrateAt && !this.trick) { this.trick = TR.startTrick("spinWild", this.reduceMotion || this.paused); this.celebrateAt = now + 6200; } - if (!this.driven && !this.manualHold && now >= this.trickAt) { + if (this.autoTricks && !this.driven && !this.manualHold && now >= this.trickAt) { if ((V_T.has(this.state) || B_T.has(this.state)) && !this.spinTurn && this.hopAt < 0 && !this.trick) { const z = Math.random(); if (V_T.has(this.state)) { diff --git a/src/App.vue b/src/App.vue index 669e923..c0efeba 100644 --- a/src/App.vue +++ b/src/App.vue @@ -127,6 +127,7 @@ const cycle = computed(() => cycles.value.find((c) => c.id === activeId.value) ? const state = ref(intro.value ? 'idle' : (cycle.value.blocks[block.value]?.state ?? 'idle')) const gauche = computed(() => view.value === 'reglages' && !preview.value) +const autoTricks = computed(() => view.value === 'animations') const droite = computed(() => view.value !== 'reglages' && !preview.value && !intro.value) const plein = computed(() => preview.value || photo.value) const follow = computed(() => view.value === 'reglages' && !preview.value) @@ -740,6 +741,7 @@ onUnmounted(() => { :echelle="photo ? photoComposition.scale : 1" :playing="posePlay" :mix="poseMix" + :auto-tricks="autoTricks" @ready="onHeroReady" @update:manual="manual = $event" /> diff --git a/src/components/AvatarStage.vue b/src/components/AvatarStage.vue index 1f544f4..24f1252 100644 --- a/src/components/AvatarStage.vue +++ b/src/components/AvatarStage.vue @@ -18,6 +18,7 @@ const props = withDefaults( playing?: boolean mix?: number echelle?: number + autoTricks?: boolean }>(), { size: 440, @@ -30,7 +31,8 @@ const props = withDefaults( tools: false, playing: false, mix: 0, - echelle: 1 + echelle: 1, + autoTricks: true } ) @@ -125,6 +127,7 @@ defineExpose({ :state="state" :follow="follow && !active" :paper="paper" + :auto-tricks="autoTricks" @ready="onReady" /> (), { size: 440, @@ -18,7 +19,8 @@ const props = withDefaults( color: 'black', state: 'idle', follow: false, - paper: '#f9f9f9' + paper: '#f9f9f9', + autoTricks: true } ) @@ -37,7 +39,8 @@ function input() { color: props.color, follow: props.follow, paper: props.paper, - size: props.size + size: props.size, + autoTricks: props.autoTricks } } @@ -66,7 +69,7 @@ onBeforeUnmount(() => { }) watch( - () => [props.state, props.shape, props.color, props.follow, props.paper, props.size], + () => [props.state, props.shape, props.color, props.follow, props.paper, props.size, props.autoTricks], () => replica?.apply(input()) ) diff --git a/src/replica/boot.ts b/src/replica/boot.ts index 1321182..7b648c6 100644 --- a/src/replica/boot.ts +++ b/src/replica/boot.ts @@ -32,6 +32,7 @@ export type ReplicaBot = { setSize: (px: number) => void seekEye: (index: number, opts?: { snap?: boolean }) => void setPlaylistHold: (on: boolean) => void + setAutoTricks: (on: boolean) => void playback: () => ReplicaPlayback destroy: () => void } diff --git a/src/replica/host.ts b/src/replica/host.ts index 25a69f5..4ecb838 100644 --- a/src/replica/host.ts +++ b/src/replica/host.ts @@ -36,6 +36,7 @@ export type ReplicaInput = { paper: string size: number eye?: number + autoTricks?: boolean } const TILE_MAX = 6 @@ -118,7 +119,8 @@ export async function mountReplica( eyeColor: input.paper, paused: driven ? false : live ? false : 'hold-pose', driven, - eyeIndex: input.eye + eyeIndex: input.eye, + autoTricks: input.autoTricks ?? true }) const noop = { apply: () => {}, @@ -154,6 +156,8 @@ export async function mountReplica( if (next.follow !== last.follow) bot.setFollowPointer(next.follow) if (next.paper !== last.paper) bot.setEyeColor(next.paper) if (next.size !== last.size) bot.setSize(next.size) + if ((next.autoTricks ?? true) !== (last.autoTricks ?? true)) + bot.setAutoTricks(next.autoTricks ?? true) last = { ...next } }, spin: () => bot.spinOnce(1),