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

Posted

技术标签:

【中文标题】如何清除画布属性和事件并将其添加回fabricjs?【英文标题】:How to clear canvas properties and events and add it back in fabricjs? 【发布时间】:2021-03-26 23:19:08 【问题描述】:

上下文:

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

注意:我这里没有使用fabric的撤消/重做的选项。

我试图在我尝试执行以下操作的 sn-p 中重新创建问题。

第 1 步:我正在使用 addTextBox 方法创建具有默认属性的画布和对象,一旦应用了默认属性,我会将画布和文本框对象存储在一个称为状态的变量中以进行备份。

第 2 步:

在 AddHelloText Click(Button) 上,我正在推送已记录的状态 在上一步中撤消堆栈。

之后,我将“hello”分配给文本框并“更改背景颜色” 变红”。

第 3 步: 在 Undo Click(Button) 上,我从 undoStack 获取 canvasOb 并将 i 分配给 canvas(活动画布)。

在这一步中,当我撤消时,画布上的值/事件应该是存储在 undoStack 中的备份画布,但它的值不正确。 简单来说,画布背景应该是“蓝色”,文本框对象应该有“默认文本”,即使我点击 在文本框上它应该有背景“蓝色”和文本 “默认文本”

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

只有背景更新,文本对象保持不变。

单击边界框(文本框)时,会显示画布的另一个实例(具有 step2 值的实例)

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

任何建议都会很有帮助。

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"  ></canvas>
  <button id="addHello" class="button">Add hello</button>
  <button id="undo" class="button">Undo</button>
 </div></body></html>

【问题讨论】:

您好,我相信您想在不使用原生 fabric.js 撤消/重做 API 的情况下实现撤消/重做选项,对吧? 是的,我已经部分实现了,有些部分工作但不完全,以上是我目前面临的问题。 【参考方案1】:

即使您将其引用保存在您的状态对象中,您仍在动态更改画布对象。因此,当您更新文本或更改画布的背景颜色等时,实际上您正在更改保存的状态,因为到目前为止您所做的只是保存对 fabric.js 画布object 的引用。 这篇文章可能很有趣: Reference vs Primitive values in javascript 完成您想要实现的目标的最佳方法(在我看来)是使用fabric.js toJSON 方法并将状态保存为JSON,然后在需要时使用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"  ></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 对象。解决方案是为添加到画布的每个对象分配一个 id,以便您可以再次检索它们(用于修改、删除等)。这会让事情变得更复杂,所以我没有把它放在这里。

【讨论】:

感谢 shahbazi 这行得通,似乎是一种更好的方法。但是撤消后的“选定”事件不会调用jsfiddle.net/fierce_trailblazer/ao7xc412/5 我认为事件需要以不同的方式重新初始化 Nevermind loadFromJson 第三个参数可以用来重新初始化事件***.com/questions/49697408/… 或者,您可以监听selection:created 事件,并检查对象是否是您想要的对象:fabricjs.com/events

以上是关于如何清除画布属性和事件并将其添加回fabricjs?的主要内容,如果未能解决你的问题,请参考以下文章

使用fabricjs从json到图像

画布上的 FabricJS AngularJS 事件

Fabricjs HTML5 Canvas:为啥图像大小调整得这么差?

如何使用自定义钩子访问织物对象以将背景图像添加到画布?

如何在fabricJS中向圆形对象添加文本?

将图像拖放到画布(FabricJS)