撤消和重做画布不按预期工作 [英] Undo and Redo Canvas Not Working as Expected

查看:151
本文介绍了撤消和重做画布不按预期工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有可添加对象的画布,以及Undo和Redo按钮。正如你在我的例子中所看到的,我能够撤消/重做一次,但事情就会破裂;通过这个我的意思是我可以添加一个对象并删除它,但如果我移动添加的对象并点击撤消,它应该移动到我以前的位置,但它会从画布中消失。

I have a canvas with an addable objects, as well as Undo and Redo buttons. As you can see in my example, I'm able to undo/redo 1 time but things break; by this I mean I can add an object and remove it but if for example I move the added object and hit undo, it should move to where I had it previously but instead it disappears from the canvas.

我正在使用fabric.js 1.7.22。

I'm using fabric.js 1.7.22.

我的代码:

var canvas = this.__canvas = new fabric.Canvas('canvas', {
  backgroundColor: 'grey',
  centeredScaling: true
});
canvas.setWidth(400);
canvas.setHeight(600);

canvas. preserveObjectStacking = true;

// Add Text
function Addtext() {
  var text = new fabric.IText("Tape and Type...", {
    fontSize: 30,
    top: 10,
    left: 10,
    textAlign: "center",
  });
  canvas.add(text);
  canvas.centerObject(text);
  canvas.setActiveObject(text);
  text.enterEditing();
  text.selectAll();
  canvas.renderAll();
  canvas.isDrawingMode = false;
}

// Undo Redo
canvas.on('object:added',function(){
  if(!isRedoing){
    h = [];
  }
  isRedoing = false;
});

var isRedoing = false;
var h = [];
function undo(){
  if(canvas._objects.length>0){
   h.push(canvas._objects.pop());
   canvas.renderAll();
  }
}
function redo(){
  
  if(h.length>0){
    isRedoing = true;
   canvas.add(h.pop());
  }
}

<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
    crossorigin="anonymous"></script>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
    crossorigin="anonymous">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.min.js"></script>
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  
        <a href="#" class="btn btn-dark" onclick="Addtext()">Add Text</a>

<button onclick="undo()" type="button" class="btn btn-sm btn-dark">
        <i class="material-icons">undo</i>
      </button>

      <button onclick="redo()" type="button" class="btn btn-sm btn-dark">
        <i class="material-icons">redo</i>
      </button>
      
      <canvas id="canvas"></canvas>

推荐答案

您需要添加一些处理画布状态的状态管理函数,并且每次发生更改时都能够恢复和更新状态。

You need to add some kind of state management functions that handle the state of the canvas, and that is able to restore and to update the state each time a change occurs.

可以通过添加或更新画布来触发更改(对象:已添加对象:修改画布上的处理程序处理此问题)或撤消重做操作。

The changes could be triggered by either the adding or updating of the canvas (the object:added, object:modified handlers on the canvas take care of this) or by the undo, redo actions.

要避免那些撤消重做操作与历史记录冲突并添加重复项,以便在它们发生时标记它们,这就是 canvas.loadFromJSON 回调在实际触发操作后派上用场的用武之地画布已更新。

To avoid those undo, redo actions colliding with the history and adding duplicates you need to flag them when they are happening, and that is where the canvas.loadFromJSON callback comes in handy to actually trigger an action after the canvas has been updated.

我添加了一个功能示例,并添加了一些调试消息,以便代码更多一些nderstandable。在你习惯它之前,大部分是阅读有点密集)

I added a functional example of it, and also added a couple of debugging messages so that the code is a little more understandable. Mostly is a little dense to read until you get used to it)

const canvas = new fabric.Canvas('canvas', {
    backgroundColor: 'grey',
    centeredScaling: true
});
canvas.setWidth(400);
canvas.setHeight(600);
canvas.preserveObjectStacking = true;

var Addtext = () => {
    const text = new fabric.IText('Tape and Type...', {
        fontSize: 30,
        top: 10,
        left: 10,
        textAlign: 'center',
    });
    canvas.add(text);
    canvas.centerObject(text);
    canvas.setActiveObject(text);
    text.enterEditing();
    text.selectAll();
    canvas.renderAll();
    canvas.isDrawingMode = false;
};

var canvasHistory = {
    state: [],
    currentStateIndex: -1,
    undoStatus: false,
    redoStatus: false,
    undoFinishedStatus: true,
    redoFinishedStatus: true,
};

const updateHistory = () => {
    if (canvasHistory.undoStatus === true || canvasHistory.redoStatus === true) {
        console.log('Do not do anything, this got triggered automatically while the undo and redo actions were performed');
    } else {
        const jsonData = canvas.toJSON();
        const canvasAsJson = JSON.stringify(jsonData);

        // NOTE: This is to replace the canvasHistory when it gets rewritten 20180912:Alevale
        if (canvasHistory.currentStateIndex < canvasHistory.state.length - 1) {

            const indexToBeInserted = canvasHistory.currentStateIndex + 1;
            canvasHistory.state[indexToBeInserted] = canvasAsJson;
            const elementsToKeep = indexToBeInserted + 1;
            console.log(`History rewritten, preserved ${elementsToKeep} items`);
            canvasHistory.state = canvasHistory.state.splice(0, elementsToKeep);

        // NOTE: This happens when there is a new item pushed to the canvasHistory (normal case) 20180912:Alevale
        } else {
            console.log('push to canvasHistory');
            canvasHistory.state.push(canvasAsJson);
        }

        canvasHistory.currentStateIndex = canvasHistory.state.length - 1;
    }
};

canvas.on('object:added', () => {
    updateHistory();
});
canvas.on('object:modified', () => {
    updateHistory();
});

var undo = () => {
    if (canvasHistory.currentStateIndex - 1 === -1) {
        console.log('do not do anything anymore, you are going far to the past, before creation, there was nothing');
        return;
    }

    if (canvasHistory.undoFinishedStatus) {
        canvasHistory.undoFinishedStatus = false;
        canvasHistory.undoStatus = true;
        canvas.loadFromJSON(canvasHistory.state[canvasHistory.currentStateIndex - 1], () => {
            canvas.renderAll();
            canvasHistory.undoStatus = false;
            canvasHistory.currentStateIndex--;
            canvasHistory.undoFinishedStatus = true;
        });
    }
};

var redo = () => {
    if (canvasHistory.currentStateIndex + 1 === canvasHistory.state.length) {
        console.log('do not do anything anymore, you do not know what is after the present, do not mess with the future');
        return;
    }

    if (canvasHistory.redoFinishedStatus) {
        canvasHistory.redoFinishedStatus = false;
        canvasHistory.redoStatus = true;
        canvas.loadFromJSON(canvasHistory.state[canvasHistory.currentStateIndex + 1], () => {
            canvas.renderAll();
            canvasHistory.redoStatus = false;
            canvasHistory.currentStateIndex++;
            canvasHistory.redoFinishedStatus = true;
        });
    }
};

<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
        crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
      crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.min.js"></script>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

<a href="#" class="btn btn-dark" onclick="Addtext()">Add Text</a>

<button onclick="undo()" type="button" class="btn btn-sm btn-dark">
    <i class="material-icons">undo</i>
</button>

<button onclick="redo()" type="button" class="btn btn-sm btn-dark">
    <i class="material-icons">redo</i>
</button>

<canvas id="canvas"></canvas>

这篇关于撤消和重做画布不按预期工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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