从零打造一个无依赖的Canvas图片编辑器

Canvas Drawing Editor 完整使用指南

一个强大的、零依赖的 Canvas 画布编辑器 Web Component

Gitee

GitHub:

NPM:

在线演示:


目录

  1. 简介
  2. [为什么选择 Canvas Drawing Editor](#为什么选择 Canvas Drawing Editor "#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-canvas-drawing-editor")
  3. 功能特性
  4. 快速开始
  5. 安装指南
  6. 框架集成
    • [原生 HTML](#原生 HTML "#%E5%8E%9F%E7%94%9F-html")
    • [Vue 3](#Vue 3 "#vue-3")
    • [Vue 2](#Vue 2 "#vue-2")
    • React
    • Angular
  7. 配置选项详解
  8. 工具配置
  9. 绘图工具使用
  10. 高级功能
  11. 热区功能
  12. [Tween 动画系统](#Tween 动画系统 "#tween-%E5%8A%A8%E7%94%BB%E7%B3%BB%E7%BB%9F")
  13. 事件监听
  14. 数据格式
  15. [API 参考](#API 参考 "#api-%E5%8F%82%E8%80%83")
  16. 移动端支持
  17. 键盘快捷键
  18. 最佳实践
  19. 常见问题

简介

Canvas Drawing Editor 是一个基于 HTML5 Canvas 的现代化画布编辑器,它以 Web Component 的形式封装,使其可以无缝集成到任何前端框架或原生 HTML 项目中。

核心优势

  • 零依赖:纯 JavaScript 实现,无需 React、Vue 等框架
  • 轻量级:gzip 压缩后仅约 33KB
  • 跨框架:支持 Vue 2/3、React、Angular 和原生 HTML
  • 功能丰富:涵盖绑图、编辑、导出等完整工作流
  • 移动端友好:支持触摸手势操作

为什么选择 Canvas Drawing Editor

在众多的画布编辑器库中,Canvas Drawing Editor 以其独特的优势脱颖而出:

1. 真正的零依赖

与市面上大多数需要依赖特定框架的画布库不同,Canvas Drawing Editor 是完全独立的。这意味着:

  • 没有框架锁定
  • 更小的包体积
  • 更快的加载速度
  • 更简单的集成方式

2. Web Component 标准

基于 Web Component 标准开发,享受原生浏览器支持:

html 复制代码

3. 企业级功能

虽然轻量,但功能完整:

  • 20+ 绑图工具
  • 完整的撤销/重做历史
  • 图层管理系统
  • 热区模板功能
  • Tween 动画引擎
  • 多种导出格式

Canvas Drawing Editor 与主流 Canvas 库对比

功能特性 Canvas Drawing Editor Fabric.js Konva.js Excalidraw tldraw
基本信息
打包体积 (gzip) ~33KB ~90KB ~60KB ~500KB+ ~300KB+
零依赖 ❌ (依赖 React) ❌ (依赖 React)
Web Component
TypeScript 支持
框架兼容性
原生 HTML ✅ 开箱即用
Vue 2/3 ✅ 开箱即用 ⚠️ 需要封装 ⚠️ 需要封装
React ✅ 开箱即用 ⚠️ 需要封装 ✅ react-konva ✅ 原生支持 ✅ 原生支持
Angular ✅ 开箱即用 ⚠️ 需要封装 ⚠️ 需要封装
绘图工具
铅笔/手绘
矩形/圆形
直线/箭头
多边形
星形/心形 ✅ 仅星形
贝塞尔曲线
文本输入
富文本 ⚠️ 有限支持
图片导入
编辑功能
撤销/重做 ✅ 内置 ❌ 需自行实现 ❌ 需自行实现 ✅ 内置 ✅ 内置
图层管理 ✅ 内置UI ❌ 需自行实现 ❌ 需自行实现
组合/取消组合
对齐/分布 ✅ 内置UI ❌ 需自行实现 ❌ 需自行实现
锁定/隐藏
旋转/缩放
高级功能
内置工具栏UI
图片滤镜
热区/模板变量
补间动画 ✅ 内置 ❌ 需自行实现 ✅ 内置
多种线条样式
导入/导出
JSON 保存/加载
PNG 导出
SVG 导出
移动端支持
触摸手势 ⚠️ 有限支持
双指缩放 ⚠️ 需自行实现
响应式布局 ❌ 需自行实现 ❌ 需自行实现
开发者体验
学习曲线 ⭐ 低 ⭐⭐⭐ 中高 ⭐⭐ 中等 ⭐ 低 ⭐ 低
开箱即用程度 ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐
API 灵活性 ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐
文档完善度 ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐

图例说明:

  • ✅ 完全支持 / 内置功能
  • ⚠️ 部分支持 / 需要额外实现
  • ❌ 不支持
  • 需自行实现 = 需要自己编写代码实现该功能

功能特性

🎨 绑图工具集

工具 快捷键 说明
选择工具 V 选择、移动、缩放对象
画笔 P 自由绑制路径
矩形 R 绑制矩形
圆形 C 绑制圆形
线条 L 绘制直线
箭头 A 绘制箭头(单向/双向)
多边形 - 绘制多边形(支持任意边数)
文本 T 添加文本
富文本 - 支持混合样式的富文本

🔷 更多形状

  • 星形:可自定义角数和内外半径
  • 心形:完美的心形曲线
  • 三角形:等边三角形
  • 菱形:标准菱形
  • 贝塞尔曲线:精确的曲线控制

✏️ 线条样式

支持三种线条样式,适用于所有形状:

  • 实线(Solid)
  • 虚线(Dashed)
  • 点线(Dotted)

📚 图层管理

  • 图层上移/下移
  • 置顶/置底
  • 可见性控制
  • 锁定/解锁

🔄 编辑功能

  • 撤销/重做:完整的历史记录支持(Ctrl+Z / Ctrl+Y)
  • 复制/粘贴:快速复制对象(Ctrl+C / Ctrl+V)
  • 删除:删除选中对象(Delete / Backspace)
  • 旋转:自由旋转任意角度
  • 等比缩放:Shift + 拖拽角点

💾 导入导出

功能 说明
JSON 保存 保存完整项目数据,支持后续编辑
JSON 加载 加载之前保存的项目
PNG 导出 导出为高质量 PNG 图片
清空画布 一键清空所有内容

快速开始

最简示例

只需三步,即可在页面中添加一个功能完整的画布编辑器:

第一步:引入库文件

html 复制代码

第二步:添加编辑器标签

html 复制代码

第三步:设置样式

css 复制代码
canvas-drawing-editor {
  width: 100%;
  height: 600px;
  display: block;
}

完整示例

html 复制代码
  
  
  Canvas Drawing Editor 示例
  
    canvas-drawing-editor {
      width: 100%;
      height: 100vh;
      display: block;
    }
  


  

  

  
    // 监听画布变化事件
    document.addEventListener('editor-change', (e) => {
      console.log('画布内容变化:', e.detail.objects);
    });
  

安装指南

NPM 安装(推荐)

bash 复制代码
npm install canvas-drawing-editor

或使用 yarn:

bash 复制代码
yarn add canvas-drawing-editor

或使用 pnpm:

bash 复制代码
pnpm add canvas-drawing-editor

CDN 引入

如果不使用包管理器,可以直接通过 CDN 引入:

html 复制代码

ES Module 引入

javascript 复制代码
// 在项目中导入
import 'canvas-drawing-editor';

// 或导入特定类型(TypeScript)
import { CanvasDrawingEditor, ToolType, CanvasObject } from 'canvas-drawing-editor';

框架集成

原生 HTML

原生 HTML 是最简单的集成方式,无需任何构建工具:

html 复制代码
  
  
  Canvas Drawing Editor
  
    * { margin: 0; padding: 0; box-sizing: border-box; }
    canvas-drawing-editor {
      width: 100vw;
      height: 100vh;
      display: block;
    }
  


  

  

  
    // 获取编辑器实例
    const editor = document.querySelector('canvas-drawing-editor');

    // 监听编辑器事件
    document.addEventListener('editor-change', (e) => {
      console.log('画布内容变化:', e.detail.objects);
      // 可以在这里保存数据到服务器或 localStorage
    });

    document.addEventListener('editor-close', () => {
      console.log('编辑器已关闭');
    });
  

Vue 3

在 Vue 3 项目中使用 Canvas Drawing Editor:

1. 安装

bash 复制代码
npm install canvas-drawing-editor

2. 基础使用

vue 复制代码
  



import 'canvas-drawing-editor';

3. 完整示例(带配置和事件监听)

vue 复制代码
  



import { ref, computed, onMounted, onUnmounted } from 'vue';
import 'canvas-drawing-editor';

// 配置项
const editorTitle = ref('Vue3 画板');
const lang = ref('zh');
const themeColor = ref('#5450dc');

// 工具配置
const toolConfig = ref({
  pencil: true,
  rectangle: true,
  circle: true,
  line: true,
  arrow: true,
  polygon: true,
  text: true,
  image: true,
  undo: true,
  redo: true,
  zoom: true,
  download: true,
  exportJson: true,
  importJson: true,
  clear: true,
  color: true,
  layers: true,
  group: true,
  align: true
});

// 转换为 JSON 字符串
const toolConfigStr = computed(() => JSON.stringify(toolConfig.value));

// 事件处理
const handleEditorChange = (e: CustomEvent) => {
  console.log('画布内容变化:', e.detail.objects);
  // 保存到 localStorage
  localStorage.setItem('canvas-data', JSON.stringify({ objects: e.detail.objects }));
};

onMounted(() => {
  document.addEventListener('editor-change', handleEditorChange as EventListener);
});

onUnmounted(() => {
  document.removeEventListener('editor-change', handleEditorChange as EventListener);
});



.editor {
  width: 100%;
  height: 100vh;
  display: block;
}

4. 消除控制台警告(可选)

如果控制台出现 Failed to resolve component: canvas-drawing-editor 警告,可在 vite.config.ts 中添加以下配置:

typescript 复制代码
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          // 将 canvas-drawing-editor 标记为自定义元素
          isCustomElement: (tag) => tag === 'canvas-drawing-editor'
        }
      }
    })
  ]
});

Vue 2

Vue 2 的集成方式与 Vue 3 类似:

1. main.js 配置

javascript 复制代码
import Vue from 'vue';
import App from './App.vue';

// 引入 Canvas Drawing Editor
import 'canvas-drawing-editor';

// 可选:消除控制台警告
Vue.config.ignoredElements = ['canvas-drawing-editor'];

new Vue({
  render: h => h(App)
}).$mount('#app');

2. 组件使用

vue 复制代码
  



export default {
  data() {
    return {
      themeColor: '#5450dc'
    };
  },
  mounted() {
    document.addEventListener('editor-change', this.handleChange);
  },
  beforeDestroy() {
    document.removeEventListener('editor-change', this.handleChange);
  },
  methods: {
    handleChange(e) {
      console.log('画布内容变化:', e.detail.objects);
    }
  }
};

React

在 React 项目中使用 Canvas Drawing Editor:

1. 安装

bash 复制代码
npm install canvas-drawing-editor

2. TypeScript 类型声明

为了获得更好的类型支持,可以添加类型声明:

tsx 复制代码
// types/canvas-drawing-editor.d.ts
declare global {
  namespace JSX {
    interface IntrinsicElements {
      'canvas-drawing-editor': React.DetailedHTMLProps<
        React.HTMLAttributes & {
          title?: string;
          lang?: string;
          'theme-color'?: string;
          'tool-config'?: string;
          'initial-data'?: string;
          'enable-hotzone'?: string;
          'hotzone-data'?: string;
        },
        HTMLElement
      >;
    }
  }
}

3. 完整使用示例

tsx 复制代码
import { useEffect, useRef } from 'react';
import 'canvas-drawing-editor';

function App() {
  const editorRef = useRef(null);

  // 工具配置
  const toolConfig = {
    pencil: true,
    rectangle: true,
    circle: true,
    line: true,
    arrow: true,
    polygon: true,
    text: true,
    image: true,
    undo: true,
    redo: true,
    zoom: true,
    download: true,
    exportJson: true,
    importJson: true,
    clear: true,
    color: true,
    layers: true,
    group: true,
    align: true
  };

  useEffect(() => {
    // 监听编辑器事件
    const handleEditorChange = (e: Event) => {
      const customEvent = e as CustomEvent;
      console.log('画布内容变化:', customEvent.detail.objects);
    };

    const handleEditorClose = () => {
      console.log('编辑器已关闭');
    };

    document.addEventListener('editor-change', handleEditorChange);
    document.addEventListener('editor-close', handleEditorClose);

    return () => {
      document.removeEventListener('editor-change', handleEditorChange);
      document.removeEventListener('editor-close', handleEditorClose);
    };
  }, []);

  return (
    <div>
      
    </div>
  );
}

export default App;

Angular

在 Angular 项目中使用 Canvas Drawing Editor:

1. 安装

bash 复制代码
npm install canvas-drawing-editor

2. 配置 CUSTOM_ELEMENTS_SCHEMA

Angular 需要添加 CUSTOM_ELEMENTS_SCHEMA 来支持 Web Component:

typescript 复制代码
// app.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

// 引入 Canvas Drawing Editor
import 'canvas-drawing-editor';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  schemas: [CUSTOM_ELEMENTS_SCHEMA],  // 添加这行
  bootstrap: [AppComponent]
})
export class AppModule { }

3. 组件使用

typescript 复制代码
// app.component.ts
import { Component, OnInit, OnDestroy, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import 'canvas-drawing-editor';

@Component({
  selector: 'app-root',
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    
  `
})
export class AppComponent implements OnInit, OnDestroy {
  editorTitle = 'Angular 画板';
  lang = 'zh';
  themeColor = '#5450dc';

  toolConfig = {
    pencil: true,
    rectangle: true,
    circle: true,
    line: true,
    arrow: true,
    polygon: true,
    text: true,
    image: true,
    undo: true,
    redo: true,
    zoom: true,
    download: true,
    exportJson: true,
    importJson: true,
    clear: true,
    color: true,
    layers: true,
    group: true,
    align: true
  };

  get toolConfigStr(): string {
    return JSON.stringify(this.toolConfig);
  }

  private handleEditorChange = (e: Event) => {
    const customEvent = e as CustomEvent;
    console.log('画布内容变化:', customEvent.detail.objects);
  };

  ngOnInit(): void {
    document.addEventListener('editor-change', this.handleEditorChange);
  }

  ngOnDestroy(): void {
    document.removeEventListener('editor-change', this.handleEditorChange);
  }
}

配置选项详解

Canvas Drawing Editor 提供了丰富的配置选项,让你可以完全自定义编辑器的行为和外观。

基础属性

属性 类型 默认值 说明
title string "Canvas Editor" 编辑器标题,显示在顶部
lang string "zh" 界面语言,支持 "zh"(中文)和 "en"(英文)
theme-color string "#5450dc" 主题色,影响按钮、悬停状态、选中状态等
initial-data string - 初始化 JSON 数据,用于加载已有内容
enable-hotzone boolean false 是否启用热区功能(管理员模式)
hotzone-data string - 热区变量数据(JSON 格式)
tool-config string - 工具配置对象(JSON 格式)

属性使用示例

html 复制代码

动态更新属性

可以通过 JavaScript 动态更新编辑器属性:

javascript 复制代码
const editor = document.querySelector('canvas-drawing-editor');

// 更新标题
editor.setAttribute('title', '新标题');

// 切换语言
editor.setAttribute('lang', 'en');

// 更改主题色
editor.setAttribute('theme-color', '#10b981');

// 加载新数据
editor.setAttribute('initial-data', JSON.stringify({
  objects: [
    { id: 'rect1', type: 'RECTANGLE', x: 50, y: 50, width: 100, height: 80, color: '#ef4444', lineWidth: 2 }
  ]
}));

工具配置

通过 tool-config 属性可以精确控制工具栏中显示哪些工具。

tool-config 完整选项

javascript 复制代码
const toolConfig = {
  // 绘图工具
  pencil: true,      // 画笔工具
  rectangle: true,   // 矩形工具
  circle: true,      // 圆形工具
  line: true,        // 线条工具
  arrow: true,       // 箭头工具
  polygon: true,     // 多边形工具
  text: true,        // 文本工具
  image: true,       // 图片导入

  // 操作工具
  undo: true,        // 撤销按钮
  redo: true,        // 重做按钮
  zoom: true,        // 缩放控制

  // 文件操作
  download: true,    // PNG 导出
  exportJson: true,  // JSON 保存
  importJson: true,  // JSON 加载
  clear: true,       // 清空画布

  // 样式工具
  color: true,       // 颜色选择器

  // 高级功能
  layers: true,      // 图层管理
  group: true,       // 组合/解组
  align: true        // 对齐/分布
};

// 使用

旧版属性(向后兼容)

除了 tool-config,还支持单独的 show-* 属性,但推荐使用 tool-config

属性 类型 默认值 说明
show-pencil boolean true 显示画笔工具
show-rectangle boolean true 显示矩形工具
show-circle boolean true 显示圆形工具
show-line boolean true 显示线条工具
show-arrow boolean true 显示箭头工具
show-polygon boolean true 显示多边形工具
show-text boolean true 显示文本工具
show-image boolean true 显示图片导入
show-undo boolean true 显示撤销按钮
show-redo boolean true 显示重做按钮
show-zoom boolean true 显示缩放控制
show-download boolean true 显示 PNG 导出
show-export boolean true 显示 JSON 保存
show-import boolean true 显示 JSON 加载
show-color boolean true 显示颜色选择器
show-clear boolean true 显示清空画布按钮
show-layers boolean true 显示图层管理
show-group boolean true 显示组合/解组
show-align boolean true 显示对齐/分布

绘图工具使用

选择工具(V)

选择工具是最基本的工具,用于选择、移动和变换对象。

操作方式:

  • 单击:选择单个对象
  • Shift + 点击:多选对象
  • 框选:拖拽创建选择框,选中框内所有对象
  • 拖拽:移动选中的对象
  • 拖拽角点:缩放对象
  • Shift + 拖拽角点:等比例缩放
  • 拖拽旋转手柄:旋转对象

画笔工具(P)

自由绑制路径,适合手绑签名、草图等。

操作方式:

  1. 选择画笔工具
  2. 在画布上按住鼠标左键绘制
  3. 松开鼠标完成绘制

属性:

  • 颜色:通过颜色选择器设置
  • 线宽:默认为 3px

矩形工具(R)

绘制矩形或正方形。

操作方式:

  1. 选择矩形工具
  2. 在画布上按住鼠标左键拖拽
  3. 松开鼠标完成绘制
  4. 按住 Shift:绘制正方形

圆形工具(C)

绘制椭圆或正圆。

操作方式:

  1. 选择圆形工具
  2. 在画布上按住鼠标左键拖拽
  3. 松开鼠标完成绘制
  4. 按住 Shift:绘制正圆

线条工具(L)

绘制直线,支持三种线条样式。

操作方式:

  1. 选择线条工具
  2. 在画布上按住鼠标左键拖拽
  3. 松开鼠标完成绘制

线条样式:

  • 实线(Solid)
  • 虚线(Dashed)
  • 点线(Dotted)

箭头工具(A)

绘制箭头,支持单向箭头和双向箭头。

操作方式:

  1. 选择箭头工具
  2. 在画布上按住鼠标左键拖拽
  3. 松开鼠标完成绘制

箭头类型:

  • 单向箭头(Single):终点有箭头
  • 双向箭头(Double):两端都有箭头

多边形工具

绘制正多边形,支持任意边数。

操作方式:

  1. 选择多边形工具
  2. 在画布上按住鼠标左键拖拽
  3. 调整多边形边数(3边=三角形,6边=六边形等)

文本工具(T)

添加文本内容。

操作方式:

  1. 选择文本工具
  2. 在画布上点击位置
  3. 输入文本内容
  4. Enter 确认,按 Esc 取消

文本属性:

  • 字体大小
  • 颜色
  • 加粗/斜体

富文本

支持在同一文本对象中使用不同的样式。

特性:

  • 部分文字加粗
  • 部分文字改色
  • 部分文字斜体
  • 混合样式

图片导入

导入本地图片到画布。

操作方式:

  1. 点击图片导入按钮
  2. 选择本地图片文件
  3. 图片将添加到画布中心

支持的格式:

  • PNG
  • JPG/JPEG
  • GIF
  • WebP
  • SVG

图片操作:

  • 缩放
  • 旋转
  • 应用滤镜(亮度/对比度)

高级功能

图层管理

Canvas Drawing Editor 提供完整的图层管理功能。

功能列表:

功能 说明
上移图层 将选中对象上移一层
下移图层 将选中对象下移一层
置于顶层 将选中对象移到最顶层
置于底层 将选中对象移到最底层
显示/隐藏 控制图层可见性
锁定/解锁 锁定图层防止误操作

对齐与分布

当选中多个对象时,可以使用对齐和分布功能。

对齐选项:

功能 说明
左对齐 所有对象左边缘对齐
水平居中 所有对象水平中心对齐
右对齐 所有对象右边缘对齐
顶对齐 所有对象顶边缘对齐
垂直居中 所有对象垂直中心对齐
底对齐 所有对象底边缘对齐

分布选项:

功能 说明
水平分布 对象之间水平间距相等
垂直分布 对象之间垂直间距相等

组合与解组

将多个对象组合成一个整体,方便统一操作。

操作方式:

  • 组合 :选中多个对象,按 Ctrl+G 或点击组合按钮
  • 解组 :选中组合对象,按 Ctrl+Shift+G 或点击解组按钮

组合特性:

  • 组合后的对象作为整体移动、缩放、旋转
  • 可以嵌套组合(组合中包含组合)
  • 解组后恢复为独立对象

图片滤镜

对导入的图片应用滤镜效果。

支持的滤镜:

滤镜 范围 说明
亮度 -100 ~ 100 调整图片亮度
对比度 -100 ~ 100 调整图片对比度
模糊 0 ~ 20 高斯模糊效果
灰度 0 ~ 100 转换为灰度图
饱和度 0 ~ 200 调整色彩饱和度

热区功能

热区(Hotzone)是 Canvas Drawing Editor 的一个强大特性,允许在文本中使用动态变量,实现模板化的内容展示。

什么是热区?

热区功能允许你在文本中定义变量占位符,然后在运行时动态替换这些变量的值。这在以下场景非常有用:

  • 证书生成:姓名、日期、编号等动态内容
  • 海报模板:活动名称、时间、地点等
  • 数据可视化:实时数据展示
  • 个性化内容:用户信息、订单详情等

热区使用流程

热区功能分为两个模式:

  1. 管理员模式:设计模板,定义变量
  2. 用户模式:查看渲染后的内容
管理员模式(设计模板)
html 复制代码

在管理员模式下:

  1. 添加文本对象
  2. 在文本中使用 {{变量名}} 语法定义变量
  3. 例如:尊敬的 {{userName}},您的订单 {{orderId}} 已确认
  4. 保存模板 JSON
用户模式(展示内容)
html 复制代码
javascript 复制代码
// 热区变量数据
const hotzoneVariables = JSON.stringify({
  userName: '张三',
  orderId: 'ORD-2024-001',
  date: '2024年1月15日'
});

热区变量语法

语法 说明 示例
{{变量名}} 基本变量 {{userName}}
{{日期}} 中文变量名 {{创建时间}}
{{data.name}} 嵌套变量 {{user.email}}

完整热区示例

管理员端(设计模板):

html 复制代码
  热区模板设计
  
    canvas-drawing-editor { width: 100%; height: 600px; display: block; }
  


  

  

  
    document.addEventListener('editor-change', (e) => {
      // 保存模板数据
      const templateData = JSON.stringify({ objects: e.detail.objects });
      localStorage.setItem('certificate-template', templateData);
      console.log('模板已保存');
    });
  

用户端(展示证书):

html 复制代码
  我的证书
  
    canvas-drawing-editor { width: 100%; height: 600px; display: block; }
  


  

  

  
    // 加载模板
    const templateData = localStorage.getItem('certificate-template');

    // 定义变量值
    const hotzoneData = {
      userName: '李明',
      courseName: 'Web 前端开发',
      completionDate: '2024年1月20日',
      certificateNo: 'CERT-2024-00123'
    };

    const editor = document.getElementById('certificate');
    editor.setAttribute('initial-data', templateData);
    editor.setAttribute('hotzone-data', JSON.stringify(hotzoneData));
  

Tween 动画系统

Canvas Drawing Editor 内置了强大的 Tween 动画系统,可以为画布上的对象添加流畅的动画效果。

动画基础

Tween 动画可以平滑地改变对象的属性值,如位置、大小、旋转角度、透明度等。

可动画属性

属性 说明 示例值
x X 坐标 100, 200, 300
y Y 坐标 50, 150, 250
width 宽度 100, 200
height 高度 80, 160
rotation 旋转角度(度) 0, 90, 180, 360
opacity 透明度 0 ~ 1
scaleX X 轴缩放 0.5, 1, 2
scaleY Y 轴缩放 0.5, 1, 2

缓动函数

Canvas Drawing Editor 提供了丰富的缓动函数,让动画更加生动:

缓动函数 说明
linear 线性,匀速运动
easeInQuad 二次方缓入
easeOutQuad 二次方缓出
easeInOutQuad 二次方缓入缓出
easeInCubic 三次方缓入
easeOutCubic 三次方缓出
easeInOutCubic 三次方缓入缓出
easeInQuart 四次方缓入
easeOutQuart 四次方缓出
easeInOutQuart 四次方缓入缓出
easeInElastic 弹性缓入
easeOutElastic 弹性缓出
easeInOutElastic 弹性缓入缓出
easeOutBounce 弹跳缓出

使用 tweenAnimate API

javascript 复制代码
// 获取编辑器实例
const editor = document.querySelector('canvas-drawing-editor');

// 基本动画
editor.tweenAnimate({
  objectId: 'rect-001',           // 目标对象 ID
  property: 'x',                   // 动画属性
  from: 100,                       // 起始值
  to: 500,                         // 结束值
  duration: 1000,                  // 持续时间(毫秒)
  easing: 'easeOutQuad'           // 缓动函数
});

// 旋转动画
editor.tweenAnimate({
  objectId: 'star-001',
  property: 'rotation',
  from: 0,
  to: 360,
  duration: 2000,
  easing: 'linear',
  loop: true                       // 循环播放
});

// 透明度动画(淡入淡出)
editor.tweenAnimate({
  objectId: 'text-001',
  property: 'opacity',
  from: 0,
  to: 1,
  duration: 500,
  easing: 'easeInOutQuad'
});

动画配置选项

typescript 复制代码
interface TweenConfig {
  objectId: string;        // 目标对象 ID
  property: string;        // 动画属性
  from: number;            // 起始值
  to: number;              // 结束值
  duration: number;        // 持续时间(毫秒)
  easing?: string;         // 缓动函数,默认 'linear'
  delay?: number;          // 延迟开始(毫秒)
  loop?: boolean;          // 是否循环
  yoyo?: boolean;          // 是否往返(配合 loop 使用)
  onStart?: () => void;    // 动画开始回调
  onUpdate?: (value: number) => void;  // 每帧更新回调
  onComplete?: () => void; // 动画完成回调
}

停止动画

javascript 复制代码
// 停止所有动画
editor.stopAllAnimations();

// 停止特定对象的动画
editor.stopAnimation('rect-001');

动画事件

javascript 复制代码
// 动画开始事件
document.addEventListener('animation-start', (e) => {
  console.log('动画开始:', e.detail.objectId, e.detail.property);
});

// 动画更新事件
document.addEventListener('animation-update', (e) => {
  console.log('动画进度:', e.detail.progress);
});

// 动画完成事件
document.addEventListener('animation-complete', (e) => {
  console.log('动画完成:', e.detail.objectId);
});

复杂动画示例

javascript 复制代码
// 组合动画:移动 + 旋转 + 缩放
const editor = document.querySelector('canvas-drawing-editor');

// 同时执行多个动画
editor.tweenAnimate({
  objectId: 'star-001',
  property: 'x',
  from: 100,
  to: 400,
  duration: 2000,
  easing: 'easeInOutQuad'
});

editor.tweenAnimate({
  objectId: 'star-001',
  property: 'rotation',
  from: 0,
  to: 720,
  duration: 2000,
  easing: 'linear'
});

editor.tweenAnimate({
  objectId: 'star-001',
  property: 'scaleX',
  from: 1,
  to: 1.5,
  duration: 1000,
  easing: 'easeOutElastic',
  yoyo: true,
  loop: true
});

事件监听

Canvas Drawing Editor 通过自定义事件与外部通信。

可用事件

事件名 触发时机 事件数据
editor-change 画布内容变化时 { objects: CanvasObject[] }
editor-close 编辑器关闭时
animation-start 动画开始时 { objectId, property }
animation-update 动画每帧更新时 { objectId, property, progress, value }
animation-complete 动画完成时 { objectId, property }

事件监听示例

javascript 复制代码
// 监听画布变化
document.addEventListener('editor-change', (e) => {
  const { objects } = e.detail;
  console.log('画布对象数量:', objects.length);

  // 自动保存到 localStorage
  localStorage.setItem('canvas-backup', JSON.stringify({ objects }));
});

// 监听编辑器关闭
document.addEventListener('editor-close', () => {
  console.log('编辑器已关闭');
  // 可以在这里执行清理操作
});

// 监听动画事件
document.addEventListener('animation-complete', (e) => {
  const { objectId, property } = e.detail;
  console.log(`对象 ${objectId} 的 ${property} 动画已完成`);
});

Vue 3 中监听事件

vue 复制代码
import { onMounted, onUnmounted } from 'vue';

const handleEditorChange = (e) => {
  console.log('画布变化:', e.detail.objects);
};

onMounted(() => {
  document.addEventListener('editor-change', handleEditorChange);
});

onUnmounted(() => {
  document.removeEventListener('editor-change', handleEditorChange);
});

React 中监听事件

tsx 复制代码
import { useEffect } from 'react';

function App() {
  useEffect(() => {
    const handleEditorChange = (e: CustomEvent) => {
      console.log('画布变化:', e.detail.objects);
    };

    document.addEventListener('editor-change', handleEditorChange as EventListener);

    return () => {
      document.removeEventListener('editor-change', handleEditorChange as EventListener);
    };
  }, []);

  return ;
}

数据格式

JSON 数据结构

Canvas Drawing Editor 使用 JSON 格式保存和加载画布数据。

typescript 复制代码
interface CanvasData {
  objects: CanvasObject[];
}

interface CanvasObject {
  id: string;           // 唯一标识符
  type: ToolType;       // 对象类型
  x: number;            // X 坐标
  y: number;            // Y 坐标
  color: string;        // 颜色
  lineWidth: number;    // 线宽
  rotation?: number;    // 旋转角度
  opacity?: number;     // 透明度
  locked?: boolean;     // 是否锁定
  visible?: boolean;    // 是否可见
  // ... 其他属性根据类型不同而不同
}

对象类型(ToolType)

类型 说明 特有属性
PENCIL 画笔路径 points: Point[]
RECTANGLE 矩形 width, height, fillColor, lineStyle
CIRCLE 圆形 radiusX, radiusY, fillColor
LINE 线条 endX, endY, lineStyle
ARROW 箭头 endX, endY, arrowType
POLYGON 多边形 sides, radius
TEXT 文本 text, fontSize, fontFamily, bold, italic
RICH_TEXT 富文本 segments: TextSegment[]
IMAGE 图片 src, width, height, filters
TRIANGLE 三角形 width, height
STAR 星形 points, innerRadius, outerRadius
HEART 心形 size
DIAMOND 菱形 width, height
BEZIER 贝塞尔曲线 controlPoints: Point[]
GROUP 组合 children: CanvasObject[]

矩形对象示例

json 复制代码
{
  &#34;id&#34;: &#34;rect-001&#34;,
  &#34;type&#34;: &#34;RECTANGLE&#34;,
  &#34;x&#34;: 100,
  &#34;y&#34;: 100,
  &#34;width&#34;: 200,
  &#34;height&#34;: 150,
  &#34;color&#34;: &#34;#3b82f6&#34;,
  &#34;fillColor&#34;: &#34;#93c5fd&#34;,
  &#34;lineWidth&#34;: 2,
  &#34;lineStyle&#34;: &#34;solid&#34;,
  &#34;rotation&#34;: 0,
  &#34;opacity&#34;: 1,
  &#34;locked&#34;: false,
  &#34;visible&#34;: true
}

文本对象示例

json 复制代码
{
  &#34;id&#34;: &#34;text-001&#34;,
  &#34;type&#34;: &#34;TEXT&#34;,
  &#34;x&#34;: 200,
  &#34;y&#34;: 200,
  &#34;text&#34;: &#34;Hello World&#34;,
  &#34;fontSize&#34;: 24,
  &#34;fontFamily&#34;: &#34;Arial&#34;,
  &#34;color&#34;: &#34;#1f2937&#34;,
  &#34;bold&#34;: false,
  &#34;italic&#34;: false,
  &#34;rotation&#34;: 0
}

图片对象示例

json 复制代码
{
  &#34;id&#34;: &#34;image-001&#34;,
  &#34;type&#34;: &#34;IMAGE&#34;,
  &#34;x&#34;: 50,
  &#34;y&#34;: 50,
  &#34;width&#34;: 300,
  &#34;height&#34;: 200,
  &#34;src&#34;: &#34;data:image/png;base64,...&#34;,
  &#34;rotation&#34;: 0,
  &#34;filters&#34;: {
    &#34;brightness&#34;: 0,
    &#34;contrast&#34;: 0,
    &#34;blur&#34;: 0,
    &#34;grayscale&#34;: 0,
    &#34;saturation&#34;: 100
  }
}

组合对象示例

json 复制代码
{
  &#34;id&#34;: &#34;group-001&#34;,
  &#34;type&#34;: &#34;GROUP&#34;,
  &#34;x&#34;: 100,
  &#34;y&#34;: 100,
  &#34;children&#34;: [
    {
      &#34;id&#34;: &#34;rect-002&#34;,
      &#34;type&#34;: &#34;RECTANGLE&#34;,
      &#34;x&#34;: 0,
      &#34;y&#34;: 0,
      &#34;width&#34;: 100,
      &#34;height&#34;: 80
    },
    {
      &#34;id&#34;: &#34;text-002&#34;,
      &#34;type&#34;: &#34;TEXT&#34;,
      &#34;x&#34;: 10,
      &#34;y&#34;: 30,
      &#34;text&#34;: &#34;标签&#34;
    }
  ]
}

API 参考

编辑器实例方法

获取编辑器实例后,可以调用以下方法:

javascript 复制代码
const editor = document.querySelector('canvas-drawing-editor');
exportJSON()

导出画布数据为 JSON 格式。

javascript 复制代码
const jsonData = editor.exportJSON();
console.log(jsonData);
// 输出: { objects: [...] }

// 保存到文件
const blob = new Blob([JSON.stringify(jsonData)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'canvas-data.json';
a.click();
exportPNG(options?)

导出画布为 PNG 图片。

javascript 复制代码
// 基本导出
const pngDataUrl = editor.exportPNG();

// 带选项导出
const pngDataUrl = editor.exportPNG({
  scale: 2,              // 缩放倍数,用于高清导出
  backgroundColor: '#ffffff'  // 背景色
});

// 下载图片
const a = document.createElement('a');
a.href = pngDataUrl;
a.download = 'canvas-image.png';
a.click();
tweenAnimate(config)

执行 Tween 动画。

javascript 复制代码
editor.tweenAnimate({
  objectId: 'rect-001',
  property: 'x',
  from: 100,
  to: 500,
  duration: 1000,
  easing: 'easeOutQuad'
});
stopAllAnimations()

停止所有正在进行的动画。

javascript 复制代码
editor.stopAllAnimations();
stopAnimation(objectId)

停止特定对象的动画。

javascript 复制代码
editor.stopAnimation('rect-001');

完整 API 列表

方法 参数 返回值 说明
exportJSON() CanvasData 导出 JSON 数据
exportPNG(options?) ExportOptions string 导出 PNG DataURL
tweenAnimate(config) TweenConfig TweenInstance 执行动画
stopAllAnimations() void 停止所有动画
stopAnimation(id) string void 停止特定动画

移动端支持

Canvas Drawing Editor 完全支持移动端触摸操作。

触摸手势

手势 操作
单指点击 选择对象
单指拖拽 移动对象/绘制
双指捏合 缩放画布
双指旋转 旋转画布
长按 显示上下文菜单

移动端优化

编辑器针对移动端做了以下优化:

  1. 触摸区域放大:控制手柄更大,便于触摸操作
  2. 手势识别:智能识别单指/双指操作
  3. 惯性滚动:平滑的画布滚动体验
  4. 响应式布局:工具栏自适应屏幕宽度

移动端示例

html 复制代码
  
  
  移动端画板
  
    * { margin: 0; padding: 0; }
    html, body {
      width: 100%;
      height: 100%;
      overflow: hidden;
      touch-action: none;
    }
    canvas-drawing-editor {
      width: 100%;
      height: 100%;
      display: block;
    }
  


  

  

键盘快捷键

Canvas Drawing Editor 支持丰富的键盘快捷键,提高操作效率。

工具快捷键

快捷键 功能
V 选择工具
P 画笔工具
R 矩形工具
C 圆形工具
L 线条工具
A 箭头工具
T 文本工具

编辑快捷键

快捷键 功能
Ctrl + Z 撤销
Ctrl + Y 重做
Ctrl + Shift + Z 重做(备选)
Ctrl + C 复制
Ctrl + V 粘贴
Ctrl + X 剪切
Ctrl + A 全选
Delete / Backspace 删除选中对象
Escape 取消选择/取消操作

组合快捷键

快捷键 功能
Ctrl + G 组合选中对象
Ctrl + Shift + G 解组

视图快捷键

快捷键 功能
Ctrl + + 放大
Ctrl + - 缩小
Ctrl + 0 重置缩放
Space + 拖拽 平移画布

辅助快捷键

快捷键 功能
Shift + 拖拽 等比例缩放
Alt + 拖拽 从中心缩放
Shift + 绘制 绘制正方形/正圆

最佳实践

1. 性能优化

控制对象数量

javascript 复制代码
// 定期清理不需要的对象
document.addEventListener('editor-change', (e) => {
  const { objects } = e.detail;
  if (objects.length > 500) {
    console.warn('画布对象过多,可能影响性能');
  }
});

使用适当的图片尺寸

javascript 复制代码
// 导入图片前压缩
function compressImage(file, maxWidth = 1920) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      const img = new Image();
      img.onload = () => {
        const canvas = document.createElement('canvas');
        const ratio = Math.min(maxWidth / img.width, 1);
        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        resolve(canvas.toDataURL('image/jpeg', 0.8));
      };
      img.src = e.target.result;
    };
    reader.readAsDataURL(file);
  });
}

2. 数据持久化

自动保存

javascript 复制代码
let saveTimeout;

document.addEventListener('editor-change', (e) => {
  // 防抖:500ms 内的多次变化只保存一次
  clearTimeout(saveTimeout);
  saveTimeout = setTimeout(() => {
    const data = JSON.stringify({ objects: e.detail.objects });
    localStorage.setItem('canvas-autosave', data);
    console.log('自动保存成功');
  }, 500);
});

加载保存的数据

javascript 复制代码
window.addEventListener('load', () => {
  const savedData = localStorage.getItem('canvas-autosave');
  if (savedData) {
    const editor = document.querySelector('canvas-drawing-editor');
    editor.setAttribute('initial-data', savedData);
  }
});

3. 错误处理

javascript 复制代码
// 监听加载错误
try {
  const editor = document.querySelector('canvas-drawing-editor');
  const data = JSON.parse(localStorage.getItem('canvas-data'));
  if (data && data.objects) {
    editor.setAttribute('initial-data', JSON.stringify(data));
  }
} catch (error) {
  console.error('加载画布数据失败:', error);
  // 清除损坏的数据
  localStorage.removeItem('canvas-data');
}

4. 响应式设计

css 复制代码
/* 桌面端 */
canvas-drawing-editor {
  width: 100%;
  height: calc(100vh - 60px);
}

/* 平板端 */
@media (max-width: 1024px) {
  canvas-drawing-editor {
    height: calc(100vh - 50px);
  }
}

/* 移动端 */
@media (max-width: 768px) {
  canvas-drawing-editor {
    height: calc(100vh - 40px);
  }
}

5. 与后端集成

javascript 复制代码
// 保存到服务器
async function saveToServer(objects) {
  try {
    const response = await fetch('/api/canvas/save', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ objects })
    });

    if (!response.ok) {
      throw new Error('保存失败');
    }

    const result = await response.json();
    console.log('保存成功:', result.id);
    return result;
  } catch (error) {
    console.error('保存到服务器失败:', error);
    throw error;
  }
}

// 从服务器加载
async function loadFromServer(canvasId) {
  try {
    const response = await fetch(`/api/canvas/${canvasId}`);
    if (!response.ok) {
      throw new Error('加载失败');
    }

    const data = await response.json();
    const editor = document.querySelector('canvas-drawing-editor');
    editor.setAttribute('initial-data', JSON.stringify(data));
  } catch (error) {
    console.error('从服务器加载失败:', error);
  }
}

常见问题

Q1: 编辑器不显示或显示空白

可能原因:

  1. 未正确引入库文件
  2. 未设置编辑器尺寸

解决方案:

html 复制代码
  canvas-drawing-editor {
    width: 100%;
    height: 600px;
    display: block;  /* 重要! */
  }

Q2: Vue/React 控制台出现警告

Vue 警告: Failed to resolve component: canvas-drawing-editor

解决方案(Vue 3 + Vite):

typescript 复制代码
// vite.config.ts
export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          isCustomElement: (tag) => tag === 'canvas-drawing-editor'
        }
      }
    })
  ]
});

解决方案(Vue 2):

javascript 复制代码
// main.js
Vue.config.ignoredElements = ['canvas-drawing-editor'];

Q3: 事件监听不生效

可能原因: 事件是在 document 上触发的,而不是编辑器元素上。

正确做法:

javascript 复制代码
// ✅ 正确
document.addEventListener('editor-change', handler);

// ❌ 错误
editor.addEventListener('editor-change', handler);

Q4: 初始数据加载失败

可能原因:

  1. JSON 格式错误
  2. 数据结构不正确

解决方案:

javascript 复制代码
// 确保数据格式正确
const validData = {
  objects: [
    {
      id: 'unique-id',  // 必须有唯一 ID
      type: 'RECTANGLE', // 必须是有效的类型
      x: 100,
      y: 100,
      width: 200,
      height: 150,
      color: '#3b82f6',
      lineWidth: 2
    }
  ]
};

editor.setAttribute('initial-data', JSON.stringify(validData));

Q5: 导出的 PNG 图片模糊

解决方案:

javascript 复制代码
// 使用 scale 参数导出高清图片
const pngDataUrl = editor.exportPNG({
  scale: 2  // 2倍分辨率
});

Q6: 移动端触摸不灵敏

解决方案:

html 复制代码
  html, body {
    touch-action: none;
    overflow: hidden;
  }

Q7: 如何获取特定对象

javascript 复制代码
document.addEventListener('editor-change', (e) => {
  const { objects } = e.detail;

  // 按 ID 查找
  const targetObject = objects.find(obj => obj.id === 'my-object-id');

  // 按类型筛选
  const rectangles = objects.filter(obj => obj.type === 'RECTANGLE');

  // 按属性筛选
  const redObjects = objects.filter(obj => obj.color === '#ff0000');
});

Q8: 如何实现协同编辑

Canvas Drawing Editor 本身不提供协同编辑功能,但可以通过以下方式实现:

javascript 复制代码
// 使用 WebSocket 同步数据
const ws = new WebSocket('wss://your-server.com/canvas');

// 发送本地变化
document.addEventListener('editor-change', (e) => {
  ws.send(JSON.stringify({
    type: 'canvas-update',
    objects: e.detail.objects
  }));
});

// 接收远程变化
ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  if (data.type === 'canvas-update') {
    const editor = document.querySelector('canvas-drawing-editor');
    editor.setAttribute('initial-data', JSON.stringify({ objects: data.objects }));
  }
};

总结

Canvas Drawing Editor 是一个功能强大、易于使用的 Canvas 画布编辑器。它的主要优势包括:

  • 零依赖:无需任何框架,纯 JavaScript 实现
  • 跨框架:支持 Vue、React、Angular 和原生 HTML
  • 功能丰富:20+ 绘图工具,完整的编辑功能
  • 高度可定制:灵活的配置选项和 API
  • 移动端友好:完整的触摸手势支持
  • 动画系统:内置 Tween 动画引擎
  • 热区功能:支持动态变量模板

无论是简单的绘图应用,还是复杂的图形编辑器,Canvas Drawing Editor 都能满足你的需求。


相关链接

  • Gitee 仓库
  • GitHub 仓库
  • NPM 包
  • 在线演示
  • 问题反馈

许可证

MIT License

Copyright (c) 2025 typsusan


感谢使用 Canvas Drawing Editor!如果这个项目对你有帮助,请给我们一个 ⭐ Star!

相关推荐
OpenTiny社区2 小时前
揭秘!TinyEngine低代码源码如何玩转双向转换?
前端·vue.js·低代码
灰灰勇闯IT2 小时前
RN跨端适配与沉浸式体验:适配不同设备与系统
javascript·react native·react.js
北辰alk2 小时前
Vuex Store 的核心属性及其作用详解
vue.js
YaeZed2 小时前
Vue3-父子组件通信
前端·vue.js
i_am_a_div_日积月累_3 小时前
el-table实现自动滚动;列表自动滚动
开发语言·javascript·vue.js
飞龙AI3 小时前
Tailwind CSS 隐藏滚动条(全场景适配)
javascript
爱上妖精的尾巴3 小时前
5-36 WPS JS宏综合实例应用-1(多工作表数据合并)
javascript·restful·wps
一过菜只因3 小时前
VUE快速入门
前端·javascript·vue.js
匠心网络科技3 小时前
前端学习手册-JavaScript条件判断语句全解析(十八)
开发语言·前端·javascript·学习·ecmascript