0.10.4: button still-frames render only on their own state

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.7 committed 2026-04-22 18:32:18 +02:00
1 parent fcb8edbb88
commit 8895fc76ef
8 files changed
+311 -23

No files matched your search

+36
View File
@@ -174,6 +174,42 @@ export function isLayerActiveAt(layer: Layer, time: number): boolean {
return time >= range.min - EPSILON && time <= range.max + EPSILON
}
/**
* Visibility gate for layers INSIDE a button asset. The 4 discrete
* state times (normal / hover / pressed / disabled) are treated as
* exact frames — a still-frame (hold) keyframe renders only on its own
* state and is invisible on neighbours, not "held forward until the
* next keyframe" as on a regular timeline.
*
* Rules:
* - No keyframes → static from base, always visible.
* - `time` matches a keyframe's time → visible.
* - `time` sits strictly between two adjacent keyframes → visible
* only when the left bracket is NOT a hold (i.e. an interpolation
* region, not a held-into-silence region).
* - Before the first keyframe or after the last → hidden.
*/
export function isButtonLayerActiveAt(layer: Layer, time: number): boolean {
const EPSILON = 1e-6
const kfs: Keyframe[] = []
for (const k of layer.keyframes) kfs.push(k)
if (layer.positionKeyframes) for (const k of layer.positionKeyframes) kfs.push(k)
if (layer.rotationKeyframes) for (const k of layer.rotationKeyframes) kfs.push(k)
if (kfs.length === 0) return true
kfs.sort((a, b) => a.time - b.time)
for (const kf of kfs) {
if (Math.abs(kf.time - time) < EPSILON) return true
}
for (let i = 0; i < kfs.length - 1; i++) {
const a = kfs[i]
const b = kfs[i + 1]
if (time > a.time + EPSILON && time < b.time - EPSILON) {
return !a.hold
}
}
return false
}
export function sampleLayer(layer: Layer, time: number): AnimatableProps {
const main = sampleKeyframeSet(layer.keyframes, layer.base, time)
if (!layer.separated) return main