@drawfn/svelte
v0.0.1
Published
Svelte adapter for drawfn drawing canvas library
Maintainers
Readme
@21n/drawfn-svelte
Svelte adapter for @21n/drawfn drawing canvas library.
Installation
npm install @21n/drawfn-svelte @21n/drawfn perfect-freehandQuick Start
Using the DrawfnCanvas Component
<script>
import DrawfnCanvas from '@21n/drawfn-svelte/DrawfnCanvas.svelte';
let drawfnCanvas;
function handlePen() {
drawfnCanvas?.setTool('pen');
}
function handleUndo() {
drawfnCanvas?.undo();
}
</script>
<button on:click={handlePen}>Pen</button>
<button on:click={() => drawfnCanvas?.setTool('rectangle')}>Rectangle</button>
<button on:click={handleUndo}>Undo</button>
<button on:click={() => drawfnCanvas?.redo()}>Redo</button>
<DrawfnCanvas bind:this={drawfnCanvas} width={1200} height={800} />Using the Svelte Action
<script>
import { drawfn } from '@21n/drawfn-svelte';
let instance;
let canvas;
function handleReady(drawfn) {
instance = drawfn;
}
</script>
<button on:click={() => instance?.setTool('pen')}>Pen</button>
<button on:click={() => instance?.setTool('select')}>Select</button>
<button on:click={() => instance?.undo()}>Undo</button>
<canvas
bind:this={canvas}
use:drawfn={{ onReady: handleReady }}
width={1200}
height={800}
style="display: block; touch-action: none;"
/>Using with Svelte Stores
<script>
import { drawfn, createDrawfnStore } from '@21n/drawfn-svelte';
const store = createDrawfnStore();
let canvas;
function handleReady(instance) {
store.setInstance(instance);
}
</script>
<button on:click={() => store.setTool('pen')} disabled={!$store.isReady}>
Pen {$store.currentTool === 'pen' ? '✓' : ''}
</button>
<button on:click={() => store.setTool('rectangle')} disabled={!$store.isReady}>
Rectangle {$store.currentTool === 'rectangle' ? '✓' : ''}
</button>
<button on:click={() => $store.instance?.undo()} disabled={!$store.canUndo}>
Undo
</button>
<button on:click={() => $store.instance?.redo()} disabled={!$store.canRedo}>
Redo
</button>
<div>Elements: {$store.elementCount}</div>
<div>Zoom: {Math.round($store.camera.zoom * 100)}%</div>
<canvas
bind:this={canvas}
use:drawfn={{ onReady: handleReady }}
width={1200}
height={800}
style="display: block; touch-action: none;"
/>Controlled Scene
<script>
import DrawfnCanvas from '@21n/drawfn-svelte/DrawfnCanvas.svelte';
let scene = null;
function handleSceneChange(newScene) {
scene = newScene;
console.log('Scene updated:', scene);
}
</script>
<DrawfnCanvas
width={1200}
height={800}
{scene}
onSceneChange={handleSceneChange}
/>Export Canvas
<script>
import DrawfnCanvas from '@21n/drawfn-svelte/DrawfnCanvas.svelte';
let drawfnCanvas;
async function handleExportPNG() {
const blob = await drawfnCanvas?.exportPNG({ scale: 2 });
if (!blob) return;
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'drawing.png';
a.click();
URL.revokeObjectURL(url);
}
async function handleExportSVG() {
const svg = await drawfnCanvas?.exportSVG();
if (!svg) return;
const blob = new Blob([svg], { type: 'image/svg+xml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'drawing.svg';
a.click();
URL.revokeObjectURL(url);
}
</script>
<button on:click={handleExportPNG}>Export PNG</button>
<button on:click={handleExportSVG}>Export SVG</button>
<DrawfnCanvas bind:this={drawfnCanvas} width={1200} height={800} />Complete Example with Toolbar
<script>
import DrawfnCanvas from '@21n/drawfn-svelte/DrawfnCanvas.svelte';
let drawfnCanvas;
let currentTool = 'select';
const tools = [
{ id: 'select', label: 'Select' },
{ id: 'pan', label: 'Pan' },
{ id: 'pen', label: 'Pen' },
{ id: 'rectangle', label: 'Rectangle' },
{ id: 'ellipse', label: 'Ellipse' },
{ id: 'arrow', label: 'Arrow' },
{ id: 'text', label: 'Text' },
];
function setTool(tool) {
drawfnCanvas?.setTool(tool);
currentTool = tool;
}
</script>
<div class="toolbar">
{#each tools as tool}
<button
on:click={() => setTool(tool.id)}
class:active={currentTool === tool.id}
>
{tool.label}
</button>
{/each}
<div class="separator" />
<button on:click={() => drawfnCanvas?.undo()}>Undo</button>
<button on:click={() => drawfnCanvas?.redo()}>Redo</button>
<button on:click={() => drawfnCanvas?.clear()}>Clear</button>
</div>
<DrawfnCanvas bind:this={drawfnCanvas} width={1200} height={800} />
<style>
.toolbar {
display: flex;
gap: 8px;
padding: 12px;
background: white;
border-bottom: 1px solid #e5e7eb;
}
button {
padding: 8px 16px;
border: 1px solid #d1d5db;
background: white;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #f9fafb;
}
button.active {
background: #3b82f6;
color: white;
border-color: #3b82f6;
}
.separator {
width: 1px;
background: #e5e7eb;
}
</style>API
DrawfnCanvas Component
Props:
width?: number- Canvas width (default: 800)height?: number- Canvas height (default: 600)scene?: Scene- Initial or controlled sceneoverlayRoot?: HTMLElement- Root element for text editing overlaygetImageBitmap?: (src: string) => Promise<ImageBitmap>- Custom image loaderonReady?: (drawfn: Drawfn) => void- Callback when drawfn is initializedonSceneChange?: (scene: Scene) => void- Callback when scene changesclass?: string- CSS class for canvas element
Methods (via bind:this):
getDrawfn(): Drawfn | null- Get drawfn instancesetTool(tool: ToolName, opts?)- Set active toolundo()- Undo last actionredo()- Redo last undone actionclear()- Clear the canvasexportPNG(opts?): Promise<Blob | undefined>- Export as PNGexportSVG(opts?): Promise<string | undefined>- Export as SVG
Canvas Component
Simple canvas component with proper styling for drawfn.
Props:
width?: number- Canvas width (default: 800)height?: number- Canvas height (default: 600)class?: string- CSS class
drawfn Action
Svelte action for using drawfn on a canvas element.
Usage:
<canvas use:drawfn={options} />Options:
scene?: Scene- Initial sceneoverlayRoot?: HTMLElement- Root for text overlaygetImageBitmap?: (src: string) => Promise<ImageBitmap>- Image loaderonReady?: (drawfn: Drawfn) => void- Ready callbackonSceneChange?: (scene: Scene) => void- Scene change callback
createDrawfnStore()
Creates a Svelte store for reactive drawfn state.
Returns a store with:
{
instance: Drawfn | null;
isReady: boolean;
currentTool: ToolName;
canUndo: boolean;
canRedo: boolean;
selectedIds: string[];
elementCount: number;
camera: Camera;
}Methods:
setInstance(drawfn: Drawfn)- Set drawfn instancesetTool(tool: ToolName, opts?)- Set active toolclear()- Clear canvasdestroy()- Cleanup
Tools
All tools from @21n/drawfn are available:
select- Select, move, resize, rotate elementspan- Pan the canvaspen- Freehand drawingrectangle- Draw rectangles (Shift for square)ellipse- Draw ellipses (Shift for circle)arrow- Draw arrows with arrowheadtext- Add text with inline editingimage- Add images:setTool('image', { src: 'url' })node- Add nodes:setTool('node', { nodeId: 'abc' })
Events
Subscribe to drawfn events:
<script>
import { drawfn } from '@21n/drawfn-svelte';
let instance;
function handleReady(drawfn) {
instance = drawfn;
drawfn.on('elementAdded', (element) => {
console.log('Element added:', element);
});
drawfn.on('selectionChanged', (ids) => {
console.log('Selection changed:', ids);
});
}
</script>
<canvas use:drawfn={{ onReady: handleReady }} />Available events:
pointerDown,pointerMove,pointerUpelementAdded,elementUpdated,elementRemovedselectionChangedhistoryChangedcameraChanged
TypeScript Support
Full TypeScript support with type definitions included.
<script lang="ts">
import DrawfnCanvas from '@21n/drawfn-svelte/DrawfnCanvas.svelte';
import type { Drawfn, Scene } from '@21n/drawfn-svelte';
let drawfnCanvas: DrawfnCanvas;
let scene: Scene | undefined;
</script>License
Apache-2.0
