From 869b67458a270dc5eb802d22550df9302c8be19a Mon Sep 17 00:00:00 2001 From: th Date: Mon, 20 Apr 2026 10:35:43 +0200 Subject: [PATCH] 0.4.0: clipart drawer, animation paths, bezier keyframes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Clipart library: new left-edge drawer (5px idle, expands on hover, 400px when open). User picks a local folder via the File System Access API in settings; directory handle is persisted in IndexedDB so the library survives reloads. Dragging a thumbnail onto the stage creates an image asset + layer at the drop point. - Animation paths on the stage: selecting a keyframe draws the tween path from the previous keyframe to the selected one as a solid polyline with per-frame draggable dots. All other segments on the same position-track render as dashed ghost lines so the full curve progression is visible. - Path dots grow on hover (2.5 → 6 px), show a grab cursor, and have a wider hit area so they're easier to grab. - Bezier-curved position interpolation: keyframes gain optional ctrlX/ctrlY fields. When set, x/y tween along a quadratic bezier through the control point instead of a straight line. Dragging a path dot solves for the control so the curve passes through the drag point at its frame progress. Mirrored across all four render pipelines. - Tests: new suites for polygon actions, freeform drawing lifecycle, polygon + stroke rendering (15 new tests). Fixed the mask-render tests that relied on a default-transparent canvas background. Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitignore | 3 + package.json | 2 +- server/render.html | 10 +- src/App.vue | 19 +- src/components/CanvasStage.vue | 266 ++++++++++++++++++++++- src/components/ClipartDrawer.vue | 304 ++++++++++++++++++++++++++ src/components/DocumentSettings.vue | 133 +++++++++++- src/devtools/tests.ts | 319 ++++++++++++++++++++++++++++ src/i18n/messages.ts | 18 ++ src/lib/clipartLibrary.ts | 129 +++++++++++ src/lib/exportPlayer.ts | 10 +- src/lib/interpolate.ts | 11 +- src/stores/clipart.ts | 113 ++++++++++ src/stores/scene.ts | 51 +++++ src/types/scene.ts | 4 + src/version.ts | 2 +- 16 files changed, 1379 insertions(+), 15 deletions(-) create mode 100644 src/components/ClipartDrawer.vue create mode 100644 src/lib/clipartLibrary.ts create mode 100644 src/stores/clipart.ts diff --git a/.gitignore b/.gitignore index 89f1a15..2c3ce18 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ dist-ssr .DS_Store *.log + +# Local clipart working folder +/clipart/ diff --git a/package.json b/package.json index 216a043..0d394ff 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "animatr", "private": true, - "version": "0.3.0", + "version": "0.4.0", "type": "module", "scripts": { "dev": "vite", diff --git a/server/render.html b/server/render.html index d62c1c6..7f91efa 100644 --- a/server/render.html +++ b/server/render.html @@ -80,7 +80,15 @@ } var raw=(time-a.time)/(b.time-a.time); var t=easing(b.easing,raw); - return lerpProps(merge(layer.base,a.props), merge(layer.base,b.props), t); + var pa=merge(layer.base,a.props); + var pb=merge(layer.base,b.props); + var outS=lerpProps(pa,pb,t); + if(b.ctrlX!=null && b.ctrlY!=null){ + var om=1-t; + outS.x=om*om*pa.x+2*om*t*b.ctrlX+t*t*pb.x; + outS.y=om*om*pa.y+2*om*t*b.ctrlY+t*t*pb.y; + } + return outS; } } return merge(layer.base); diff --git a/src/App.vue b/src/App.vue index f839ec2..be8cc26 100644 --- a/src/App.vue +++ b/src/App.vue @@ -18,6 +18,7 @@ import { import AboutDialog from './components/AboutDialog.vue' import AssetSidebar from './components/AssetSidebar.vue' import CanvasStage from './components/CanvasStage.vue' +import ClipartDrawer from './components/ClipartDrawer.vue' import DocumentSettings from './components/DocumentSettings.vue' import ExportDialog from './components/ExportDialog.vue' import FullscreenPreview from './components/FullscreenPreview.vue' @@ -290,7 +291,9 @@ onBeforeUnmount(() => { diff --git a/src/components/DocumentSettings.vue b/src/components/DocumentSettings.vue index db859b3..a4ae85a 100644 --- a/src/components/DocumentSettings.vue +++ b/src/components/DocumentSettings.vue @@ -1,7 +1,15 @@