当前位置: 首页 > news >正文

使用 Fabric.js 构建一个在线白板组件(支持绘图 / 拖拽 / 导出)

文章目录

  • 一、前言
  • 二、技术栈
  • 三、项目初始化
  • 四、创建基础白板组件
    • 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 等现代框架可以开发出媲美专业工具的应用。


到这里,这篇文章就和大家说再见啦!我的主页里还藏着很多 篇 前端 实战干货,感兴趣的话可以点击头像看看,说不定能找到你需要的解决方案~
创作这篇内容花了很多的功夫。如果它帮你解决了问题,或者带来了启发,欢迎:
点个赞❤️ 让更多人看到优质内容
关注「前端极客探险家」🚀 每周解锁新技巧
收藏文章⭐️ 方便随时查阅
📢 特别提醒:
转载请注明原文链接,商业合作请私信联系
感谢你的阅读!我们下篇文章再见~ 💕

在这里插入图片描述

相关文章:

  • 【含文档+PPT+源码】微信小程序的线上茶叶交易商城的设计与实现
  • 批处理脚本bat丨遍历一个包含项目名称的数组,并对每个文件中的项目执行 git pull 操作 (一键拉很多文件的代码)
  • AI智能体生态革命:谷歌A2A协议如何重塑未来十年? ——当“安卓模式”撞上AI Agent,一场没有硝烟的战争开始了
  • 支付宝SEO全攻略:小程序搜索优化的系统方法与实践指南
  • Python文件操作完全指南:从基础到高级应用
  • 一文读懂WPF布局
  • 深度解读分销小程序商城源码系统:从搭建到运营的关键指南​​​​
  • IntelliJ IDEA 中安装和使用通义灵码 AI 编程助手教程
  • 第一部分——Docker篇 第五章 容器编排
  • 汽车知识杂志社汽车知识编辑部汽车知识杂志2025年第4期目录
  • 2020 CCF CSP-S2.函数调用
  • IP属地和所在地不一致什么意思?怎么换成另外一个地方的
  • 【MATLAB第114期】基于MATLAB的SHAP可解释神经网络分类模型(敏感性分析方法)
  • [ctfshow web入门] web40
  • 【Kubernetes】Kubernetes 如何进行日志管理?Fluentd / Loki / ELK 适用于什么场景?
  • vue辅助工具(vue系列二)
  • MySQL8.0.31安装教程,附pdf资料和压缩包文件
  • 【Grok 大模型深度解析】第一期:技术溯源与核心突破
  • openEuler 24.03安装docker,docker compose
  • arthas之profiler火焰图基本使用和实践
  • 中山做网站建设联系电话/百度平台客服电话
  • 商务网站建设的必备功能/广告发布
  • 河南app定制/互联网seo是什么
  • 金坛市常州网络推广/江西seo
  • 如何做网站站内搜索/品牌营销的四大策略
  • 网站建设与维修/网络营销专业的就业方向