This commit is contained in:
th committed 2026-04-19 19:07:51 +02:00
1 parent c6863f2bfc
commit 94e9846c13
11 files changed
+985 -136

No files matched your search

+86 -3
View File
@@ -160,6 +160,8 @@ function circleConfig(
return {
...baseNodeConfig(layer, props),
radius: asset.radius,
offsetX: layer.offsetX ?? 0,
offsetY: layer.offsetY ?? 0,
fill: props.fill ?? asset.fill,
}
}
@@ -169,6 +171,8 @@ function textConfig(layer: Layer, asset: Extract<Asset, { kind: 'text' }>, props
...baseNodeConfig(layer, props),
text: asset.text,
fontSize: asset.fontSize,
offsetX: layer.offsetX ?? 0,
offsetY: layer.offsetY ?? 0,
fill: props.fill ?? asset.fill,
}
}
@@ -201,7 +205,7 @@ const pivotInfo = computed<PivotInfo | null>(() => {
const layer = store.scene.layers.find((l) => l.id === id)
if (!layer) return null
const asset = assetMap.value.get(layer.assetId)
if (!asset || (asset.kind !== 'rect' && asset.kind !== 'image')) return null
if (!asset) return null
return { layerId: layer.id, sampled: sampleLayer(layer, store.playhead) }
})
@@ -249,7 +253,7 @@ function onPivotDragEnd(e: Konva.KonvaEventObject<DragEvent>) {
}
const transformerConfig = {
rotateEnabled: true,
rotateEnabled: false,
enabledAnchors: [
'top-left',
'top-center',
@@ -303,6 +307,7 @@ function handleStageMouseDown(e: Konva.KonvaEventObject<MouseEvent>) {
}
}
if (target.findAncestor?.('.pivot-handle')) return
if (target.findAncestor?.('.rotation-handle')) return
if (target === target.getStage()) {
store.selectLayer(null)
return
@@ -339,6 +344,61 @@ function handleTransformEnd(e: Konva.KonvaEventObject<Event>) {
})
}
const ROT_ARM_PX = 44
const rotationHandleConfig = computed(() => {
const info = pivotInfo.value
if (!info) return null
const arm = ROT_ARM_PX / zoom.value
const rad = (info.sampled.rotation * Math.PI) / 180
return {
x: info.sampled.x + arm * Math.sin(rad),
y: info.sampled.y - arm * Math.cos(rad),
draggable: true,
name: 'rotation-handle',
}
})
const rotationArmConfig = computed(() => {
const info = pivotInfo.value
if (!info) return null
const arm = ROT_ARM_PX / zoom.value
const rad = (info.sampled.rotation * Math.PI) / 180
return {
points: [
info.sampled.x,
info.sampled.y,
info.sampled.x + arm * Math.sin(rad),
info.sampled.y - arm * Math.cos(rad),
],
stroke: '#6ea8ff',
strokeWidth: 1.25 / zoom.value,
dash: [3 / zoom.value, 3 / zoom.value],
listening: false,
}
})
const rotationKnobConfig = computed(() => ({
radius: 6 / zoom.value,
fill: '#ffffff',
stroke: '#6ea8ff',
strokeWidth: 2 / zoom.value,
}))
function onRotationHandleDragMove(e: Konva.KonvaEventObject<DragEvent>) {
const info = pivotInfo.value
if (!info) return
const node = e.target
const dx = node.x() - info.sampled.x
const dy = node.y() - info.sampled.y
if (dx === 0 && dy === 0) return
const rad = Math.atan2(dx, -dy)
let deg = (rad * 180) / Math.PI
if (deg > 180) deg -= 360
if (deg < -180) deg += 360
store.upsertKeyframe(info.layerId, store.playhead, { rotation: deg })
}
let ro: ResizeObserver | null = null
function onWheel(e: WheelEvent) {
@@ -363,8 +423,14 @@ let panStartViewX = 0
let panStartViewY = 0
let panPointerId = -1
function isPanTrigger(e: PointerEvent | MouseEvent) {
if (e.button === 1) return true
if (e.button === 0 && e.ctrlKey) return true
return false
}
function onContainerPointerDown(e: PointerEvent) {
if (e.button !== 1) return
if (!isPanTrigger(e)) return
e.preventDefault()
panning = true
panStartX = e.clientX
@@ -376,6 +442,13 @@ function onContainerPointerDown(e: PointerEvent) {
if (container.value) container.value.style.cursor = 'grabbing'
}
function onContainerMouseDownCapture(e: MouseEvent) {
if (isPanTrigger(e)) {
e.stopPropagation()
e.preventDefault()
}
}
function onContainerPointerMove(e: PointerEvent) {
if (!panning || e.pointerId !== panPointerId) return
viewX.value = panStartViewX + (e.clientX - panStartX)
@@ -416,6 +489,7 @@ onMounted(() => {
ro.observe(container.value)
container.value.addEventListener('wheel', onWheel, { passive: false })
container.value.addEventListener('mousedown', onContainerMouseDownCapture, { capture: true })
container.value.addEventListener('pointerdown', onContainerPointerDown)
container.value.addEventListener('pointermove', onContainerPointerMove)
container.value.addEventListener('pointerup', onContainerPointerUp)
@@ -426,6 +500,7 @@ onBeforeUnmount(() => {
ro?.disconnect()
ro = null
container.value?.removeEventListener('wheel', onWheel)
container.value?.removeEventListener('mousedown', onContainerMouseDownCapture, { capture: true } as EventListenerOptions)
container.value?.removeEventListener('pointerdown', onContainerPointerDown)
container.value?.removeEventListener('pointermove', onContainerPointerMove)
container.value?.removeEventListener('pointerup', onContainerPointerUp)
@@ -466,6 +541,7 @@ onBeforeUnmount(() => {
/>
</template>
<v-rect :config="canvasBorderConfig" />
<v-line v-if="rotationArmConfig" :config="rotationArmConfig" />
<v-group
v-if="pivotHandleConfig"
:config="pivotHandleConfig"
@@ -474,6 +550,13 @@ onBeforeUnmount(() => {
<v-circle :config="pivotRingConfig" />
<v-circle :config="pivotDotConfig" />
</v-group>
<v-group
v-if="rotationHandleConfig"
:config="rotationHandleConfig"
@dragmove="onRotationHandleDragMove"
>
<v-circle :config="rotationKnobConfig" />
</v-group>
</v-group>
<v-transformer ref="transformerRef" :config="transformerConfig" />
</v-layer>