Vue3 在线 PDF 编辑 2.0 撤回、反撤回

各位开发者朋友!继上次分享批注回显与文字批注优化后,今天为大家带来 1.0 版本的重磅更新 ------撤回与反撤回功能的实现解析。这两个功能堪称 "手滑救星",让批注操作更具灵活性。话不多说,直接拆解技术细节!

一、撤回批注:操作记录的 "时光机"

实现撤回功能的核心在于实时记录画布操作状态,并在需要时还原历史版本。我们通过监听 fabric-Canvas 的事件来捕捉操作变化:

1. 挂载操作监听事件

在渲染 PDF 时,为每个画布绑定元素删除、修改事件:

less 复制代码
fabricCanvas.on('object:removed', saveState.bind(fabricCanvas, {
    page: i - 1,
    canvas: fabricCanvas,
    type: 'remove'
})); // 删除元素监听
fabricCanvas.on('object:modified', saveState.bind(fabricCanvas, {
    page: i - 1,
    canvas: fabricCanvas,
    type: 'modify'
})); // 修改元素监听

对于画线、图形等增量添加操作,由于object:added事件无法完整捕捉绘制过程,需在操作结束时手动记录:

typescript 复制代码
const stopDrwa = (event: { page: string, canvas: any }, e: any) => {
    if (isDraw) {
        saveState({ ...event, type: 'add' }); // 绘制结束时记录添加操作
    }
    // 省略其他逻辑
}

2. 状态存储与栈管理

使用两个栈结构管理操作历史:

  • undoStack:存储所有操作状态(添加、修改、删除)
  • redoStack:存储撤回操作的状态,用于反撤回
ini 复制代码
const undoStack: any[] = []; // 撤回栈
const redoStack: any[] = []; // 反撤回栈
let isUndo = false; // 标记当前是否为撤回操作,避免重复记录
const saveState = (event: any) => {
    if (!isUndo) { // 非撤回操作时才记录
        const state = event.canvas.toJSON(); // 序列化画布状态
        undoStack.push({
            state,
            canvas: event.canvas,
            page: event.page,
            type: event.type,
        });
        redoStack.length = 0; // 清空反撤回栈,避免脏数据
    }
}

3. 撤回逻辑实现

根据操作类型(添加 / 修改 / 删除)执行不同的还原策略:

ini 复制代码
const undo = () => {
    if (undoStack.length === 0) return;
    
    isUndo = true;
    const currentState = undoStack.pop(); // 取出当前状态
    redoStack.push(currentState); // 存入反撤回栈
    
    if (currentState.type === 'add') {
        // 添加操作:删除最后一个对象
        const prevState = undoStack[undoStack.length - 1] || { state: { objects: [] } };
        currentState.canvas.loadFromJSON(prevState.state, () => {
            currentState.canvas.renderAll();
            isUndo = false;
        });
    } else {
        // 修改/删除操作:直接还原上一个状态
        currentState.canvas.loadFromJSON(currentState.prevState || currentState.state, () => {
            currentState.canvas.renderAll();
            isUndo = false;
        });
    }
}

二、反撤回批注:撤回操作的 "逆过程"

反撤回功能依赖redoStack中存储的撤回记录,直接还原最近一次撤回的状态:

ini 复制代码
const redo = () => {
    if (redoStack.length === 0) return;
    
    isUndo = true;
    const stateToRestore = redoStack.pop(); // 取出撤回记录
    undoStack.push(stateToRestore); // 重新存入撤回栈
    
    stateToRestore.canvas.loadFromJSON(stateToRestore.state, () => {
        stateToRestore.canvas.renderAll();
        isUndo = false;
    });
}

通过这种 "栈乒乓" 机制,实现了操作历史的双向追溯。

三、功能展示

总结

本次新增的撤回与反撤回功能,让 PDF 批注操作更加容错和灵活。目前已形成批注编辑→保存→回显→撤回 / 反撤回的完整闭环。接下来,我们将实现图片插入、右侧批注记录、清空画布等功能。

欢迎前往 项目仓库 体验完整功能,也期待你在评论区分享对功能扩展的想法 ------ 你的建议可能成为下一个版本的核心特性! 🚀

相关推荐
岁岁岁平安5 分钟前
Vue3学习(组合式API——计算属性computed详解)
前端·javascript·vue.js·学习·computed·计算属性
HWL56791 小时前
Express项目解决跨域问题
前端·后端·中间件·node.js·express
刺客-Andy1 小时前
React 第三十九节 React Router 中的 unstable_usePrompt Hook的详细用法及案例
前端·javascript·react.js
Go_going_1 小时前
【js基础笔记] - 包含es6 类的使用
前端·javascript·笔记
说私域2 小时前
基于开源AI大模型与S2B2C生态的个人品牌优势挖掘与标签重构研究
人工智能·小程序·开源·零售
浩~~2 小时前
HTML5 浮动(Float)详解
前端·html·html5
love530love2 小时前
家用或办公 Windows 电脑玩人工智能开源项目配备核显的必要性(含 NPU 及显卡类型补充)
人工智能·windows·python·开源·电脑
AI大模型顾潇2 小时前
[特殊字符] 本地大模型编程实战(29):用大语言模型LLM查询图数据库NEO4J(2)
前端·数据库·人工智能·语言模型·自然语言处理·prompt·neo4j
工业互联网专业3 小时前
基于springboot+vue的医院门诊管理系统
java·vue.js·spring boot·毕业设计·源码·课程设计·医院门诊管理系统
九月TTS3 小时前
TTS-Web-Vue系列:Vue3实现内嵌iframe文档显示功能
前端·javascript·vue.js