如何清除 canvas 属性和事件并将其添加回 fabricjs?

How to clear canvas properties and events and add it back in fabricjs?

上下文:

我有一个用例,我需要从堆栈中获取旧的 canvas 并将其分配给 dom 中当前活动的 canvas。我目前在这里面临的问题是 canvas 属性(例如 backgroundColor、高度等)被替换并正确显示,但是如果 canvas 中的对象,属性和事件看起来像是被替换了但是未显示在活动 canvas(DOM).

注意: 我没有选择使用 fabric 的 undo/redo 在这里。

我试图在我尝试执行以下操作的片段中重现该问题。

第 1 步: 我正在使用 addTextBox 方法创建 canvas 和具有默认属性的对象,应用默认属性后,我将存储 canvas 和名为备份状态的变量中的文本框对象。

第 2 步:

第 3 步: 撤消单击(按钮)我从 undoStack 获取 canvasOb 并将 i 分配给 canvas(活动 canvas).

In this step the when i do undo, the values/events on the canvas are supposed to be of backup canvas which was stored in the undoStack,but instead it has incorrect values. In simple words the canvas background is supposed to be "blue" and textbox object is supposed to have "Default text", even when i click on textbox it is supposed to have background "blue" and text as "Default text"

这是我目前面临的两个问题。

不确定我应该如何进一步进行。

任何建议都会很有帮助。

var canvas = new fabric.Canvas("c");
var t1 = null;
var state = {};
const addTextBox=(canvasParam)=>{
     t1 = new fabric.Textbox('Default text', {
              width: 100,
              top: 5,
              left: 5,
              fontSize: 16,
              textAlign: 'center',
               fill: '#ffffff',
     });
     canvas.on('selected', () => {
            canvas.clearContext(canvas.contextTop);
        });
     canvas.setBackgroundColor('blue');
     canvas.add(t1);
  state={id:1,canvasOb:canvas,t1Backup:t1};
}

addTextBox(canvas);

var undoStack =[];

//Update the text to hello and change canvas background to red
  $('#addHello').on('click', function() {
    undoStack.push({state:_.cloneDeep(state)})
    canvas.setBackgroundColor('red');
    t1.text="hello red"
    canvas.renderAll();
  })

 //apply previous canvas stored in the stack
  $('#undo').on('click', function() {
    console.log("TextBox value stored in undo stack: ",undoStack[0].state.canvasOb.getObjects()[0].text);
    console.log("TextBox value stored in undo stack: ",undoStack[0].state.t1Backup.text);
    canvas = undoStack[0].state.canvasOb;
    console.log("Textbox value that should be displayed currently instead of 'hello red':",canvas.getObjects()[0].text);
    canvas.renderAll();
  })
body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  width: 100vw;
}

#c {
  box-shadow: 0 0 20px 0px #9090908a;
  border-radius: 1rem;
  //padding:.5rem;
}

#app-container{
  height: 30vh;
  width: 30vw;
}
<script src="https://unpkg.com/lodash@4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.1.0/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div >
<html><body><div id="app-container">
<canvas id="c" width="200" height="300"></canvas>
  <button id="addHello" class="button">Add hello</button>
  <button id="undo" class="button">Undo</button>
 </div></body></html>

即使您将其 reference 保存在您的状态对象中,您也正在即时更改 canvas 对象。因此,当您更新文本或更改 canvas 等的背景颜色时,您实际上是在更改保存的状态,因为到目前为止您所做的只是保存对 fabric.js [=45] 的引用=] object

这篇文章可能很有趣: Reference vs Primitive values in Javascript

实现你想要实现的目标的最好方法(在我看来)是在你需要的时候使用 fabric.js toJSON method and save the state as JSON, then retrieve it again using loadFromJSON
这是一个例子:

var canvas = new fabric.Canvas('c');
var t1 = null;
var undoStack = [];


// Whenever you make a change and then you want to save it as a state
const addNewState = () => {
    var canvasObject = canvas.toJSON();
    undoStack.push(JSON.stringify(canvasObject));
};

const initCanvas = () => {
    t1 = new fabric.Textbox('Default text', {
        width: 100,
        top: 5,
        left: 5,
        fontSize: 16,
        textAlign: 'center',
        fill: '#ffffff',
    });
    canvas.on('selected', () => {
        canvas.clearContext(canvas.contextTop);
    });
    canvas.setBackgroundColor('blue');
    canvas.add(t1);
    addNewState(); // Our first state
};

// Init canvas with default parameters
initCanvas(canvas);

const retrieveLastState = () => {
    var latestState = undoStack[undoStack.length - 1]; // Get last state
    var parsedJSON = JSON.parse(latestState);
    canvas.loadFromJSON(parsedJSON);
};

// Update the text to hello and change canvas background to red
$('#addHello').on('click', function () {
    canvas.setBackgroundColor('red');
    t1.text = 'hello red';
    canvas.renderAll();
});

// apply previous canvas stored in the stack
// Retrieve the very last state
$('#undo').on('click', function () {
    retrieveLastState();
    canvas.renderAll();
});
body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    width: 100vw;
}

#c {
    box-shadow: 0 0 20px 0px #9090908a;
    border-radius: 1rem;
}

#app-container {
    height: 30vh;
    width: 30vw;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="app-container">
        <canvas id="c" width="200" height="300"></canvas>
        <button id="addHello" class="button">Add hello</button>
        <button id="undo" class="button">Undo</button>
    </div>
    <script src="https://unpkg.com/lodash@4/lodash.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.1.0/fabric.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</body>

</html>

但是,下次您单击 #addHello 按钮并尝试再次撤消它时,不会 起作用。原因是我们试图访问 t1 变量,它是 上的一个 fabric.js 对象 canvas。解决方案是为添加到 canvas 的每个对象分配一个 id,以便您可以再次检索它们(用于修改、删除等...)。这会使事情变得更加复杂,所以我没有把它放在这里。