我有一个带有可添加对象的画布,以及Undo和Redo按钮。正如你在我的例子中看到的那样,我能够撤消/重做一次,但事情就会破裂;通过这个我的意思是我可以添加一个对象并删除它,但是如果我移动添加的对象并点击撤消,它应该移动到我以前的位置,但它会从画布中消失。
我正在使用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>
&#13;
答案 0 :(得分:7)
您需要添加某种状态管理功能来处理画布的状态,并能够在每次更改发生时恢复和更新状态。
可以通过添加或更新画布(画布上的object:added
,object:modified
处理程序来处理)或由undo
,{{ 1}}操作。
为避免这些redo
,undo
动作与历史记录发生冲突并添加重复项,您需要在它们发生时对其进行标记,这就是redo
回调很方便的地方在画布更新后实际上触发了一个动作。
我添加了一个功能示例,还添加了一些调试消息,以便使代码更易于理解。在您习惯之前,通常阅读起来有点密集)
canvas.loadFromJSON
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;
});
}
};