@live-canvas/vue
v2.0.0
Published
Real-time collaborative whiteboard engine built on fabric.js and Vue
Maintainers
Readme
@live-canvas/vue
Real-time collaborative whiteboard engine built on fabric.js and Vue 3.
Features
- Store-driven architecture — single source of truth, canvas is just a view
- Real-time sync — diff-based network synchronization
- Undo/Redo — mark-based history with automatic diff tracking
- State machine tools — extensible tool system
- Vue 3 composables — for zero-config setup
- TypeScript — full type safety
Install
npm install @live-canvas/vue fabricQuick start
<script setup lang="ts">
import { ref } from 'vue';
import { WhiteboardCanvas } from '@live-canvas/vue';
import type { UseWhiteboardReturn } from '@live-canvas/vue';
const wb = ref<UseWhiteboardReturn>();
</script>
<template>
<WhiteboardCanvas
canvas-id="my-board"
:width="1024"
:height="768"
@ready="(w) => wb = w"
/>
<button @click="wb?.setTool('pen')">Pen</button>
<button @click="wb?.setTool('text')">Text</button>
<button @click="wb?.setTool('eraser')">Eraser</button>
<button @click="wb?.undo()" :disabled="!wb?.canUndo.value">Undo</button>
<button @click="wb?.redo()" :disabled="!wb?.canRedo.value">Redo</button>
</template>Composable API
For more control, use useWhiteboard() directly:
<script setup lang="ts">
import { ref } from 'vue';
import { useWhiteboard } from '@live-canvas/vue';
const canvasRef = ref<HTMLCanvasElement>();
const {
editor,
currentTool,
canUndo,
canRedo,
setTool,
undo,
redo,
connect,
disconnect,
loadSnapshot,
getSnapshot,
} = useWhiteboard(canvasRef, {
canvasId: 'my-board',
width: 1024,
height: 768,
});
</script>
<template>
<canvas ref="canvasRef" />
</template>Architecture
RecordStore (single source of truth)
│
├── CanvasBridge — Store → fabric.Canvas (view)
├── HistoryManager — Store diffs → undo/redo
└── NetworkSynchronizer — Store diffs → server → other clientsAll state lives in RecordStore. Canvas, history, and network are subscribers.
Custom tools
Extend StateNode to create your own tools:
import { StateNode } from '@live-canvas/vue';
class Idle extends StateNode {
static id = 'idle';
onPointerDown(info) {
// your logic
}
}
export class HighlighterTool extends StateNode {
static id = 'highlighter';
static children = [Idle];
static initialChildId = 'idle';
}License
MIT
