0.3.0: freeform drawing, stroke properties, polygon edit gestures
- Freeform drawing tool: new sidebar button enters a click-to-draw mode that appends points, shows a live ghost preview line, and exits via Enter or double-click (near the start point closes the path). Works end-to-end through all four render pipelines. - Polygon authoring gestures: double-click on a path inserts a vertex on the nearest edge; double-click on an open polygon's endpoint resumes the drawing session from that end, reversing point order when needed. - Stroke + closed-path properties: all shape types (rect, circle, text, polygon) expose stroke color, stroke width, and (for polygons) a closed toggle. Fill and stroke each support a "transparent" switch so shapes can render as outline-only or fill-only. - Mask preview no longer inherits the user's stroke — the dashed ghost keeps its own outline regardless of stroke-width changes. - Playhead no longer dims on inactive timeline tracks; only keyframes, segments and clip blocks fade. - Properties panel point-count control for polygons, plus per-keyframe points animation that correctly interpolates once a baseline exists. - Pivot drag hides the rotation handle until release. - Transformer attach retries on a short timer to avoid races where the Konva node hasn't mounted yet when selection changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a05396e734
commit
276d621d20
13 files changed
+944
-64
No files matched your search
@@ -7,6 +7,7 @@ import {
|
||||
PhImageSquare,
|
||||
PhListBullets,
|
||||
PhPencilSimple,
|
||||
PhPencilSimpleLine,
|
||||
PhPlus,
|
||||
PhPolygon,
|
||||
PhSquare,
|
||||
@@ -57,9 +58,21 @@ function addSampleCircle() {
|
||||
store.addLayer(asset.id)
|
||||
}
|
||||
|
||||
function addSamplePolygon() {
|
||||
function startFreeformDrawing() {
|
||||
store.startFreeformDrawing()
|
||||
}
|
||||
|
||||
async function addSamplePolygon() {
|
||||
const input = await promptDialog({
|
||||
title: t('sidebar.newPolygonDialog.title'),
|
||||
message: t('sidebar.newPolygonDialog.message'),
|
||||
defaultValue: '6',
|
||||
confirmLabel: t('sidebar.newPolygonDialog.confirm'),
|
||||
})
|
||||
if (input === null) return
|
||||
const parsed = parseInt(input.trim(), 10)
|
||||
const sides = Number.isFinite(parsed) ? Math.max(3, Math.min(24, parsed)) : 6
|
||||
const radius = 60
|
||||
const sides = 6
|
||||
const points: { x: number; y: number }[] = []
|
||||
for (let i = 0; i < sides; i++) {
|
||||
const a = (i / sides) * Math.PI * 2 - Math.PI / 2
|
||||
@@ -67,7 +80,7 @@ function addSamplePolygon() {
|
||||
}
|
||||
const asset = store.addAsset({
|
||||
kind: 'polygon',
|
||||
name: 'Polygon',
|
||||
name: `Polygon ${sides}`,
|
||||
points,
|
||||
fill: '#9b7ede',
|
||||
})
|
||||
@@ -249,6 +262,14 @@ function onAssetDragStart(e: DragEvent, asset: Extract<Asset, { kind: 'image' }>
|
||||
<button class="shape-btn" :title="t('sidebar.shape.polygon')" @click="addSamplePolygon">
|
||||
<PhPolygon :size="16" />
|
||||
</button>
|
||||
<button
|
||||
class="shape-btn"
|
||||
:class="{ active: store.drawingMode === 'freeform' }"
|
||||
:title="store.drawingMode === 'freeform' ? t('sidebar.shape.freeformActive') : t('sidebar.shape.freeform')"
|
||||
@click="startFreeformDrawing"
|
||||
>
|
||||
<PhPencilSimpleLine :size="16" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<input
|
||||
@@ -399,6 +420,12 @@ h4 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.shape-btn.active {
|
||||
background: var(--accent-soft);
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--danger);
|
||||
font-size: 11px;
|
||||
|
||||
Reference in new issue
Block a user