Make kaleido pivot keyframable

This commit is contained in:
th committed 2026-06-05 14:31:31 +02:00
1 parent c646f66e8f
commit 70cf131c47
9 files changed
+285 -87

No files matched your search

+33 -26
View File
@@ -561,36 +561,35 @@ function kaleidoIndicesForLayer(layer: Layer): number[] {
}
/** Augments a layer's Konva config with the kaleido per-copy
* position + rotation. Pivot defaults to the layer's own (x, y),
* but a per-layer override moves the orbit centre elsewhere on the
* canvas — same arithmetic as render.ts/exportPlayer use, so all
* three render paths land identical pixels. Non-source copies are
* position + rotation. Pivot lookup matches render.ts: live drag
* override → keyframed `props.kaleidoPivotX/Y` → static layer
* override → layer's own (x, y). Non-source copies are
* listening:false to keep events on the source. */
function withK<T extends Record<string, unknown>>(
cfg: T,
layer: Layer,
props: AnimatableProps,
ki: number,
n: number,
): T {
if (n <= 1 || ki === 0) return cfg
const sx = typeof cfg.x === 'number' ? (cfg.x as number) : 0
const sy = typeof cfg.y === 'number' ? (cfg.y as number) : 0
const srot = typeof cfg.rotation === 'number' ? (cfg.rotation as number) : 0
// Live-pivot drag: when the user drags the on-stage handle for
// this layer, route through liveKaleidoPivot so the orbit follows
// the pointer in real time.
const live = liveKaleidoPivot.value
const pivotX = live && live.instanceId === layer.id ? live.x : (layer.kaleidoPivotX ?? sx)
const pivotY = live && live.instanceId === layer.id ? live.y : (layer.kaleidoPivotY ?? sy)
const liveOverride = live && live.instanceId === layer.id
const pivotX = liveOverride
? live.x
: props.kaleidoPivotX ?? layer.kaleidoPivotX ?? props.x
const pivotY = liveOverride
? live.y
: props.kaleidoPivotY ?? layer.kaleidoPivotY ?? props.y
const angle = kaleidoAngleDegrees(ki, n)
const a = (angle * Math.PI) / 180
const dx = sx - pivotX
const dy = sy - pivotY
const dx = props.x - pivotX
const dy = props.y - pivotY
return {
...cfg,
x: pivotX + dx * Math.cos(a) - dy * Math.sin(a),
y: pivotY + dx * Math.sin(a) + dy * Math.cos(a),
rotation: srot + angle,
rotation: props.rotation + angle,
listening: false,
draggable: false,
} as T
@@ -1683,8 +1682,14 @@ const kaleidoPivotHandle = computed<KaleidoPivotEntity | null>(() => {
if (asset?.kind === 'button') return null
const sampled = sampleLayer(layer, store.playhead)
const live = liveKaleidoPivot.value
const x = live && live.instanceId === lyId ? live.x : (layer.kaleidoPivotX ?? sampled.x)
const y = live && live.instanceId === lyId ? live.y : (layer.kaleidoPivotY ?? sampled.y)
// Pivot lookup matches render.ts: live drag → keyframed prop →
// legacy static layer field → sampled layer position.
const x = live && live.instanceId === lyId
? live.x
: sampled.kaleidoPivotX ?? layer.kaleidoPivotX ?? sampled.x
const y = live && live.instanceId === lyId
? live.y
: sampled.kaleidoPivotY ?? layer.kaleidoPivotY ?? sampled.y
return { kind: 'layer', layerId: layer.id, x, y }
}
return null
@@ -1752,9 +1757,11 @@ function onKaleidoPivotPointerUp() {
kaleidoPivotY: liveKaleidoPivot.value.y,
})
} else {
store.setLayerKaleidoPivot(pivotDragEntity.layerId, {
x: liveKaleidoPivot.value.x,
y: liveKaleidoPivot.value.y,
// Layer pivot is keyframable. Upsert at the current playhead
// so dropping multiple kfs animates the orbit centre.
store.upsertKeyframe(pivotDragEntity.layerId, store.playhead, {
kaleidoPivotX: liveKaleidoPivot.value.x,
kaleidoPivotY: liveKaleidoPivot.value.y,
})
}
}
@@ -4496,7 +4503,7 @@ function onResizeHandlePointerUp(e: PointerEvent) {
>
<v-group
v-if="entry.item.asset.kind === 'svg' && svgAssetFor(entry.item.asset)"
:config="withK(buttonLayerGroupConfig(entry.item.layer, entry.item.props), entry.item.layer, ki, kaleidoCountFor(entry.item.layer))"
:config="withK(buttonLayerGroupConfig(entry.item.layer, entry.item.props), entry.item.layer, entry.item.props, ki, kaleidoCountFor(entry.item.layer))"
@dragmove="handleDragMove"
@dragend="handleDragEnd"
@transform="handleTransform"
@@ -4581,7 +4588,7 @@ function onResizeHandlePointerUp(e: PointerEvent) {
</v-group>
<v-rect
v-else-if="entry.item.asset.kind === 'rect'"
:config="withK(rectConfig(entry.item.layer, entry.item.asset, entry.item.props), entry.item.layer, ki, kaleidoCountFor(entry.item.layer))"
:config="withK(rectConfig(entry.item.layer, entry.item.asset, entry.item.props), entry.item.layer, entry.item.props, ki, kaleidoCountFor(entry.item.layer))"
@dragmove="handleDragMove"
@dragend="handleDragEnd"
@transform="handleTransform"
@@ -4589,7 +4596,7 @@ function onResizeHandlePointerUp(e: PointerEvent) {
/>
<v-circle
v-else-if="entry.item.asset.kind === 'circle'"
:config="withK(circleConfig(entry.item.layer, entry.item.asset, entry.item.props), entry.item.layer, ki, kaleidoCountFor(entry.item.layer))"
:config="withK(circleConfig(entry.item.layer, entry.item.asset, entry.item.props), entry.item.layer, entry.item.props, ki, kaleidoCountFor(entry.item.layer))"
@dragmove="handleDragMove"
@dragend="handleDragEnd"
@transform="handleTransform"
@@ -4597,7 +4604,7 @@ function onResizeHandlePointerUp(e: PointerEvent) {
/>
<v-text
v-else-if="entry.item.asset.kind === 'text'"
:config="withK(textConfig(entry.item.layer, entry.item.asset, entry.item.props), entry.item.layer, ki, kaleidoCountFor(entry.item.layer))"
:config="withK(textConfig(entry.item.layer, entry.item.asset, entry.item.props), entry.item.layer, entry.item.props, ki, kaleidoCountFor(entry.item.layer))"
@dragmove="handleDragMove"
@dragend="handleDragEnd"
@transform="handleTransform"
@@ -4605,7 +4612,7 @@ function onResizeHandlePointerUp(e: PointerEvent) {
/>
<v-line
v-else-if="entry.item.asset.kind === 'polygon'"
:config="withK(polygonConfig(entry.item.layer, entry.item.asset, entry.item.props), entry.item.layer, ki, kaleidoCountFor(entry.item.layer))"
:config="withK(polygonConfig(entry.item.layer, entry.item.asset, entry.item.props), entry.item.layer, entry.item.props, ki, kaleidoCountFor(entry.item.layer))"
@dragmove="handleDragMove"
@dragend="handleDragEnd"
@transform="handleTransform"
@@ -4613,7 +4620,7 @@ function onResizeHandlePointerUp(e: PointerEvent) {
/>
<v-image
v-else-if="entry.item.asset.kind === 'image' && imageConfig(entry.item.layer, entry.item.asset, entry.item.props)"
:config="withK(imageConfig(entry.item.layer, entry.item.asset, entry.item.props)!, entry.item.layer, ki, kaleidoCountFor(entry.item.layer))"
:config="withK(imageConfig(entry.item.layer, entry.item.asset, entry.item.props)!, entry.item.layer, entry.item.props, ki, kaleidoCountFor(entry.item.layer))"
@dragmove="handleDragMove"
@dragend="handleDragEnd"
@transform="handleTransform"