0.4.0: clipart drawer, animation paths, bezier keyframes
- 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) <noreply@anthropic.com>
This commit is contained in:
1 parent
276d621d20
commit
869b67458a
16 files changed
+1379
-15
No files matched your search
+16
-3
@@ -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(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app" :style="appStyle">
|
||||
<div class="shell">
|
||||
<ClipartDrawer />
|
||||
<div class="app" :style="appStyle">
|
||||
<header class="topbar">
|
||||
<div class="brand-area">
|
||||
<button class="brand" :title="t('about.openTooltip')" @click="aboutOpen = true">
|
||||
@@ -404,10 +407,19 @@ onBeforeUnmount(() => {
|
||||
<span>{{ loadError }}</span>
|
||||
</Toast>
|
||||
<Toast :visible="!!savedToast" variant="info" :message="savedToast ?? ''" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.shell {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app {
|
||||
display: grid;
|
||||
grid-template-columns: 260px 1fr 300px;
|
||||
@@ -416,8 +428,9 @@ onBeforeUnmount(() => {
|
||||
'top top top'
|
||||
'side stage props'
|
||||
'time time time';
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
|
||||
Reference in new issue
Block a user