文章目录
一、前言
在线白板在教育、远程协作、设计等场景中越来越常见。如果用原生 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 等现代框架可以开发出媲美专业工具的应用。
到这里,这篇文章就和大家说再见啦!我的主页里还藏着很多 篇 前端 实战干货,感兴趣的话可以点击头像看看,说不定能找到你需要的解决方案~
创作这篇内容花了很多的功夫。如果它帮你解决了问题,或者带来了启发,欢迎:
点个赞❤️ 让更多人看到优质内容
关注「前端极客探险家」🚀 每周解锁新技巧
收藏文章⭐️ 方便随时查阅
📢 特别提醒:
转载请注明原文链接,商业合作请私信联系
感谢你的阅读!我们下篇文章再见~ 💕