Контекст:
У меня есть вариант использования, когда мне нужно получить старый холст из стека и назначить его текущему активному холсту в dom. Проблема, с которой я сейчас сталкиваюсь, заключается в том, что свойства холста (например, backgroundColor, высота и т. д.) заменяются и отображаются правильно, но в случае объектов на холсте свойства и события выглядят так, как будто они заменяются, но не отображаются в активном холст(ДОМ).
Примечание. У меня нет возможности использовать здесь отмену/возврат ткани.
Я попытался воссоздать проблему во фрагменте, где я пытаюсь сделать следующее.
Шаг 1: я создаю холст и объекты со свойствами по умолчанию, используя метод addTextBox, после применения свойств по умолчанию я сохраняю объект холста и текстового поля в переменной, называемой состоянием для резервного копирования.
Шаг 2:
при нажатии AddHelloText (кнопке) я нажимаю состояние, которое было записано на предыдущем шаге, чтобы отменить стек.
После этого я назначаю «привет» текстовому полю и «меняю цвет фона до красного».
Шаг 3: при нажатии кнопки отмены (кнопка) я получаю canvasOb из undoStack и назначаю i холсту (активному холсту).
На этом шаге, когда я делаю отмену, значения/события на холсте должны относиться к резервному холсту, который был сохранен в стеке отмены, но вместо этого он имеет неверные значения. Проще говоря, фон холста должен быть «синим», а объект текстового поля должен иметь «текст по умолчанию», даже когда я нажимаю в текстовом поле предполагается, что фон "синий" и текст как «Текст по умолчанию»
Это две проблемы, с которыми я сталкиваюсь в настоящее время.
Обновляется только фон, а текстовый объект остается прежним.
При нажатии на ограничивающую рамку (текстовое поле) отображается другой экземпляр холста (тот, который имеет значения шага 2).
Не уверен, как именно мне следует действовать дальше.
Любые предложения будут действительно полезны.
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.info("TextBox value stored in undo stack: ",undoStack[0].state.canvasOb.getObjects()[0].text);
console.info("TextBox value stored in undo stack: ",undoStack[0].state.t1Backup.text);
canvas = undoStack[0].state.canvasOb;
console.info("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>Да, я уже реализовал это частично, некоторые части работают, но не полностью, это проблема, с которой я сталкиваюсь в настоящее время.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы изменяете объект холста на лету, даже если сохраняете его ссылку внутри своего объекта состояния. Таким образом, когда вы обновляете текст или меняете цвет фона холста и т. д., вы фактически меняете свое сохраненное состояние, потому что все, что вы сделали до сих пор, — это сохранили ссылку на холст fabric.js object.
Эта статья может быть интересна:
Справочные и примитивные значения в 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" 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 на самом первом холсте. Решением было бы назначить идентификатор каждому объекту, который вы добавляете на холст, чтобы вы могли снова получить их (для изменения, удаления и т. д.). Это сильно усложнило бы задачу, поэтому я не привожу это здесь.
Благодаря shahbazi, это работает и кажется лучшим подходом. Но «выбранное» событие после отмены не вызывается jsfiddle.net/fierce_trailblazer/ao7xc412/5
Я думаю, что события нужно повторно инициализировать по-другому
Неважно, третий параметр loadFromJson можно использовать для повторной инициализации событий stackoverflow.com/questions/49697408/…
Кроме того, вы можете прослушать событие selection:created и проверить, является ли объект тем, который вам нужен: Fabricjs.com/events
Здравствуйте, я полагаю, вы хотите реализовать опцию отмены/повторения без использования собственного API отмены/повторения Fabric.js, верно?