文章目录
- 一、前言
- 二、技术栈
- 三、项目初始化
- 四、创建基础白板组件
- 4.1 Whiteboard.vue 基础结构
- 4.2 绘图和形状功能
- 五、功能增强
- 5.1 撤销 / 重做
- 5.2 删除选中对象
- 5.3 复制 / 粘贴图形
- 5.4 自定义线条颜色和宽度
- 5.5 图形锁定 / 解锁
- 六、样式优化
- 七、页面使用示例
- 八、总结与拓展方向
一、前言
在线白板在教育、远程协作、设计等场景中越来越常见。如果用原生 Canvas 来实现白板,绘图、交互、变换、导出等功能实现起来会非常复杂,而 Fabric.js 提供了一个强大的封装库,大幅降低了开发成本。
本篇文章将基于 Vue 3 + Fabric.js 实现一个功能完整的白板组件,支持:
- ✏️ 手绘线条
- 🔲 拖拽图形(矩形 / 圆形)
- 📦 添加文字
- 💾 导出画布为图片
- ♻️ 清空画布
- ↩️ 撤销 / 重做
- 🔒 锁定图形
- 🎨 颜色 / 线宽自定义
- 📋 复制 / 粘贴 / 删除图形
二、技术栈
- Vue 3 +
<script setup>
:组件式开发体验 - Fabric.js:Canvas 图形绘制封装
- Vite:快速构建工具
三、项目初始化
npm create vite@latest vue-whiteboard --template vue
cd vue-whiteboard
npm install
npm install fabric
四、创建基础白板组件
4.1 Whiteboard.vue 基础结构
<template><div class="toolbar"><button @click="drawLine">画线</button><button @click="addRect">矩形</button><button @click="addCircle">圆形</button><button @click="addText">文字</button><button @click="exportImage">导出</button><button @click="clearCanvas">清空</button></div><canvas id="canvas" width="1000" height="600"></canvas>
</template><script setup>
import { onMounted, ref } from 'vue';
import { fabric } from 'fabric';let canvas = null;
const isDrawing = ref(false);onMounted(() => {canvas = new fabric.Canvas('canvas', {isDrawingMode: false,backgroundColor: '#ffffff'});
});
</script>
4.2 绘图和形状功能
function drawLine() {canvas.isDrawingMode = true;
}function addRect() {const rect = new fabric.Rect({left: 100,top: 100,fill: 'skyblue',width: 100,height: 60});canvas.add(rect);canvas.isDrawingMode = false;
}function addCircle() {const circle = new fabric.Circle({left: 200,top: 200,fill: 'lightgreen',radius: 40});canvas.add(circle);canvas.isDrawingMode = false;
}function addText() {const text = new fabric.IText('请输入文字', {left: 300,top: 300,fontSize: 24,fill: '#333'});canvas.add(text);canvas.isDrawingMode = false;
}function exportImage() {const dataURL = canvas.toDataURL({format: 'png',quality: 1});const link = document.createElement('a');link.href = dataURL;link.download = 'whiteboard.png';link.click();
}function clearCanvas() {canvas.clear();canvas.setBackgroundColor('#ffffff', () => {});
}
五、功能增强
5.1 撤销 / 重做
const undoStack = [];
const redoStack = [];function saveState() {redoStack.length = 0;const json = canvas.toJSON();undoStack.push(json);
}canvas.on('object:added', saveState);
canvas.on('object:modified', saveState);
canvas.on('object:removed', saveState);function undo() {if (undoStack.length > 0) {const last = undoStack.pop();redoStack.push(canvas.toJSON());canvas.loadFromJSON(last, () => canvas.renderAll());}
}function redo() {if (redoStack.length > 0) {const next = redoStack.pop();undoStack.push(canvas.toJSON());canvas.loadFromJSON(next, () => canvas.renderAll());}
}
5.2 删除选中对象
function deleteActiveObject() {const active = canvas.getActiveObject();if (active) {canvas.remove(active);}
}window.addEventListener('keydown', (e) => {if (e.key === 'Delete') {deleteActiveObject();}
});
5.3 复制 / 粘贴图形
let clipboard = null;function copy() {const active = canvas.getActiveObject();if (active) {active.clone((cloned) => {clipboard = cloned;});}
}function paste() {if (clipboard) {clipboard.clone((clonedObj) => {canvas.discardActiveObject();clonedObj.set({left: clonedObj.left + 20,top: clonedObj.top + 20});canvas.add(clonedObj);canvas.setActiveObject(clonedObj);canvas.requestRenderAll();});}
}window.addEventListener('keydown', (e) => {if (e.ctrlKey && e.key === 'c') copy();if (e.ctrlKey && e.key === 'v') paste();
});
5.4 自定义线条颜色和宽度
<div class="toolbar"><input type="color" v-model="lineColor" /><input type="range" v-model="lineWidth" min="1" max="20" />
</div>
const lineColor = ref('#000000');
const lineWidth = ref(2);watch([lineColor, lineWidth], () => {canvas.freeDrawingBrush.color = lineColor.value;canvas.freeDrawingBrush.width = lineWidth.value;
});onMounted(() => {canvas.freeDrawingBrush.color = lineColor.value;canvas.freeDrawingBrush.width = lineWidth.value;
});
5.5 图形锁定 / 解锁
function toggleLock() {const active = canvas.getActiveObject();if (active) {const locked = !active.lockMovementX;active.set({lockMovementX: locked,lockMovementY: locked,hasControls: !locked,selectable: !locked});canvas.requestRenderAll();}
}
六、样式优化
<style scoped>
.toolbar {margin-bottom: 10px;
}
button {margin-right: 8px;padding: 6px 12px;
}
canvas {border: 1px solid #ccc;
}
</style>
七、页面使用示例
<template><Whiteboard />
</template><script setup>
import Whiteboard from './components/Whiteboard.vue';
</script>
八、总结与拓展方向
我们基于 Vue 3 和 Fabric.js 构建了一个功能完整的在线白板组件,涵盖了从基本的绘图到高阶的图层操作、撤销重做、导出等常见需求。
如果你希望把它进一步升级成生产级组件,可以继续扩展以下方向:
- 🧠 协同绘图:结合 WebSocket 实现多人实时操作同步
- 🧩 图层管理面板:可见性、锁定、命名等功能
- 🧲 对齐吸附 / 网格背景
- ☁️ 白板 JSON 文件存储 / 导入
- 🖼️ 拖入图片编辑支持
Fabric.js 是一款非常适合前端开发者构建图形编辑器的工具,配合 Vue 等现代框架可以开发出媲美专业工具的应用。
到这里,这篇文章就和大家说再见啦!我的主页里还藏着很多 篇 前端 实战干货,感兴趣的话可以点击头像看看,说不定能找到你需要的解决方案~
创作这篇内容花了很多的功夫。如果它帮你解决了问题,或者带来了启发,欢迎:
点个赞❤️ 让更多人看到优质内容
关注「前端极客探险家」🚀 每周解锁新技巧
收藏文章⭐️ 方便随时查阅
📢 特别提醒:
转载请注明原文链接,商业合作请私信联系
感谢你的阅读!我们下篇文章再见~ 💕