基于vue3实现画布操作的撤销与重做

该文介绍了一种在Vue3项目中使用canvas画布进行节点和连线操作的方法,包括保存操作记录、撤销和重做功能。通过监听canvasBox数据变化,保存不同操作状态到快照集合中,利用curIndex和maxLimit控制历史记录。撤销操作回退到上一状态,重做则前进到下一状态,确保画布状态的正确恢复。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于vue3实现画布操作的撤销与重做

前言
vue3项目中实现在canvas画布上实现画节点和连线功能,要求可以撤销重做

思路
canvasBox 画板数据是存放在对象里面;
snapshots存放操作记录;
curIndex表示当前操作索引的下标;
maxLimit表示最大保存的历史记录次数;
isSnapshot是否要生成快照,作用是防止撤销重做的时候也会添加快照,触发存储操作;

保存操作记录:
在移动节点、新建节点、删除节点、缩放节点、编辑节点属性、操作连线等操作时触发watch监听canvasBox画布数据变化,如果变化并且和前一次操作步骤不同,则推入历史记录;如果超过最大保存次数,则删除最开始记录,push最新记录。

撤销操作:
判断是否达到数组下限,如果为0,直接返回,否则就从当前下标的上一个取数据,按照获取的数据,重新绘制画布。

重做操作:
判断是否达到数组上限,如果达到,直接返回,否则从当前下标的下一个取数据,按照获取的数据,重新绘制画布。

实现代码
第一步:定义画布数据,定义数据快照数据

  //画板数据是存放在 canvasBox 对象
  const canvasBox = ref([] as any);
  //数据快照技术, 移动元素/新建元素/删除元素/缩放元素/编辑元素属性
  const recordManager = reactive({
      snapshots: [], //操作记录的集合
      curIndex: 0, //当前操作索引的下标
      maxLimit: 50, //最大保存的历史记录数
      isSnapshot: true, //是否要生成快照(isSnapshot),作用是防止撤销重做的时候也会添加快照
  })

第二步:监听画布数据,实现存储历史记录

const pushRecordFn = (
    state: {},
    prevState: {},
) => {
    const {snapshots, maxLimit, curIndex} = recordManager;
    //如果两个状态相同,则不推入历史记录
    if (!diff(state, snapshots[curIndex])) {
        return;
    }
    //如果在撤销的过程中重新执行了新的操作,则覆盖上一个状态
    if (snapshots.length - 1 !== curIndex) {
        snapshots.splice(curIndex + 1, snapshots.length);
    }
    //超过了最大记录
    if (snapshots.length >= maxLimit) {
        snapshots.shift();
    }
    if(recordManager.isSnapshot){//是否生成快照
    	recordManager.snapshots.push(cloneDeep(state));
    	recordManager.curIndex = recordManager.snapshots.length - 1;
     	console.log("更新操作记录器", recordManager.snapshots);
    }
}
//对监听进行防抖优化( 移动元素等时候高频操作, 对性能开销相对较大) // 
// debounce,cloneDeep需要引入 import {debounce, cloneDeep} from "lodash";
watch(canvasBox, debounce(pushRecordFn, 300), {deep: true});

//判断前后状态是否相同
const diff = (prev:any, next: any) => {
    let result = false;
    try{
        const prevCopy = JSON.stringify(prev);
        const nextCopy = JSON.stringify(next);
        result = prevCopy  !== nextCopy ;
    }catch(err) {
        console.log(err)
    }
    return result
}

第三步:撤销

const handleUndo = () => {
     const {snapshots, maxLimit, curIndex} = recordManager;
     //如果达到下限, 直接返回
     if (curIndex === 0) return;
     recordManager.isSnapshot = false;
     recordManager.curIndex--;
     canvasBox.value = 	cloneDeep(recordManager.snapshots[recordManager.curIndex]);
     // console.log("撤销", canvasBox.value);
     //重绘(需要根据项目自定义drawCanvas方法)
     drawCanvas(canvasBox.value);
 }

第四步: 重做

const handleRedo = () => {
   // console.log("重做");
     const {snapshots, maxLimit, curIndex} = recordManager;
     //如果达到上限,直接返回
     if (curIndex >= snapshots.length - 1) return;
     recordManager.isSnapshot = false;
     recordManager.curIndex++;
     canvasBox.value = cloneDeep(recordManager.snapshots[recordManager.curIndex]);
   	 //重绘(需要根据项目自定义drawCanvas方法)
     drawCanvas(canvasBox.value);
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值