如何使用enlivenObjects在Fabric JS中撤消重做 [英] How to do undo redo in fabric js using enlivenObjects

查看:96
本文介绍了如何使用enlivenObjects在Fabric JS中撤消重做的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

撤消重做功能可以很好地配合这段代码,但是如何使用fabricjs的enlivenObjects方法执行相同的操作.我希望使用enlivenObjects而不是loadFromJSON方法实现相同的结果.canvas对象容器中的对象应该能够撤消和重做.

The undo redo functionality works fine with this piece code but how to do the same with using fabricjs enlivenObjects method.I want the same results to implemented using enlivenObjects instead of using loadFromJSON method.the objects in the canvas objects container should be able to undo as well as redo.

<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;
}

推荐答案

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

I think is not such a good to use loadFromJSON or enlivenObjects to implement this. If you will have a large number of svgs loaded when you run undo/redo it will take some time to render, also doesn't make to much sense to reload everything for a small change (like position of a object). My ideea is to use two stacks where you will save the modifications of objects using fabric events. You can take a look here:

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>

这篇关于如何使用enlivenObjects在Fabric JS中撤消重做的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆