首页 > 解决方案 > 如何使用enlivenObjects在fabric js中撤消重做

问题描述

撤消重做功能适用于这段代码,但如何使用fabricjs enlivenObjects方法做同样的事情。我希望使用enlivenObjects而不是使用loadFromJSON方法来实现相同的结果。画布对象容器中的对象应该能够撤消为以及重做。

<input type="button" value="addrect" onclick="addrect()">
<input type="button" value="undo" onclick="undo()">
<input type="button" value="redo" onclick="redo()">
<input type="button" value="clear" onclick="clearcan()">
<br/>
<canvas id="fabriccanvas" width="600" height="200" style="border:1px solid #ccc"></canvas>

var canvas = new fabric.Canvas('fabriccanvas');
canvas.counter = 0;
var newleft = 0;
canvas.selection = false;

addrect = function addrect(top, left, width, height, fill) {
    canvas.add(new fabric.Rect({
        top: document.getElementById("fabriccanvas").height,
        name: 'rectangle ' + window.counter,
        left: 0 + newleft,
        width: 100,
        height: 100,
        fill: '#' + (0x1000000 + (Math.random()) * 0xffffff).toString(16).substr(1, 6),
        //fix attributes applied for all rects
        opacity: 0.75,
        lockRotation: true,
        originX: 'left',
        originY: 'bottom',
        cornerSize: 15,
        hasRotatingPoint: false,
        perPixelTargetFind: true,
        minScaleLimit: 1
    }));
    updateModifications(true);
    canvas.counter++;
    newleft += 100;
}
var state = [];
var mods = 0;
canvas.on(
    'object:modified', function () {
    updateModifications(true);
},
    'object:added', function () {
    updateModifications(true);
});

function updateModifications(savehistory) {
    if (savehistory === true) {
        myjson = JSON.stringify(canvas);
        state.push(myjson);
    }
}

undo = function undo() {
    if (mods < state.length) {
        canvas.clear().renderAll();
        canvas.loadFromJSON(state[state.length - 1 - mods - 1]);
        canvas.renderAll();
        //console.log("geladen " + (state.length-1-mods-1));
        //console.log("state " + state.length);
        mods += 1;
        //console.log("mods " + mods);
    }
}

redo = function redo() {
    if (mods > 0) {
        canvas.clear().renderAll();
        canvas.loadFromJSON(state[state.length - 1 - mods + 1]);
        canvas.renderAll();
        //console.log("geladen " + (state.length-1-mods+1));
        mods -= 1;
        //console.log("state " + state.length);
        //console.log("mods " + mods);
    }
}
clearcan = function clearcan() {
    canvas.clear().renderAll();
    newleft = 0;
}

标签: javascriptfabricjs

解决方案


我认为使用loadFromJSONenlivenObjects实现这一点并不是很好。如果您在运行撤消/重做时加载了大量 svg,则需要一些时间来渲染,重新加载所有内容以进行小的更改(如对象的位置)也没有多大意义。我的想法是使用两个堆栈,您将在其中使用结构事件保存对象的修改。你可以看看这里:

fabric.StaticCanvas.prototype.getObjectByName  = function(name){
  if(!name || typeof name === 'undefined'){
  return [];
  }
  return this._objects.filter(function(o) {
      return o.name === name;
    });
}
var canvas = new fabric.Canvas('fabriccanvas');

canvas.counter = 0;
var newleft = 0;
canvas.selection = false;
var undoStack = [];
var redoStack = [];
addrect = function addrect(top, left, width, height, fill) {
    var rect = new fabric.Rect({
        top: document.getElementById("fabriccanvas").height,
        name: 'rectangle ' + canvas.counter,
        left: 0 + newleft,
        width: 100,
        height: 100,
        fill: '#' + (0x1000000 + (Math.random()) * 0xffffff).toString(16).substr(1, 6),
        //fix attributes applied for all rects
        opacity: 0.75,
        lockRotation: true,
        originX: 'left',
        originY: 'bottom',
        cornerSize: 15,
        hasRotatingPoint: false,
        perPixelTargetFind: true,
        minScaleLimit: 1
    })
    canvas.add(rect);
    undoStack.push({
     type:'added',
     object : rect
    })
    canvas.counter++;
    newleft += 100;
    redoStack=[];
}
var state = [];
var mods = 0;
var props = {};
canvas.on(
    'mouse:down', function (e) {
    var block = e.target;
    if(block){
    	 props.oldStage = {
       left:block.left,
       top:block.top,
       width:block.width,
       height:block.height,
       scaleX:block.scaleX,
       scaleY:block.scaleY,
       }
    }
}).on(
    'mouse:up', function (e) {
    var block = e.target;
    if(block){
    	 props.newStage = {
       left:block.left,
       top:block.top,
       width:block.width,
       height:block.height,
       scaleX:block.scaleX,
       scaleY:block.scaleY,
       }
       undoStack.push({
        objectName : block.name,
        type:'modified',
        oldStage:props.oldStage,
        newStage:props.newStage
       });
       props={};
    }
});



undo = function undo() {
    if(undoStack.length){
     var undoData = undoStack.pop();
     if(undoData && undoData.type){
       switch(undoData.type){
         case 'added':
         var objectByName = canvas.getObjectByName(undoData.object.name);
         if(objectByName.length){
           canvas.remove(objectByName[0]);
          
         }
         break;
         case 'modified':
         var objectByName = canvas.getObjectByName(undoData.objectName);
         if(objectByName.length){
          	for(var key in undoData.oldStage){
              objectByName[0].set(key, undoData.oldStage[key]);
            }
         }
         break;
       }
       canvas.renderAll();
     }
     redoStack.push(undoData);
    }
}

redo = function redo() {
    if(redoStack.length){
     var redoData = redoStack.pop();
     if(redoData && redoData.type){
       switch(redoData.type){
         case 'added':
         if(redoData.object){
           canvas.add(redoData.object);
          
         }
         break;
         case 'modified':
         var objectByName = canvas.getObjectByName(redoData.objectName);
         if(objectByName.length){
          	for(var key in redoData.newStage){
              objectByName[0].set(key, redoData.newStage[key]);
            }
         }
         break;
       }
       canvas.renderAll();
     }
     undoStack.push(redoData);
    }
}
clearcan = function clearcan() {
    canvas.clear().renderAll();
    canvas.counter=0;
    undoStack=[];
    redoStack=[];
    newleft = 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.6/fabric.js"></script>
<input type="button" value="addrect" onclick="addrect()">
<input type="button" value="undo" onclick="undo()">
<input type="button" value="redo" onclick="redo()">
<input type="button" value="clear" onclick="clearcan()">
<br/>
<canvas id="fabriccanvas" width="600" height="200" style="border:1px solid #ccc"></canvas>


推荐阅读