gpt4 book ai didi

javascript - 对象 :removed Event Not Fired - Fabric JS

转载 作者:行者123 更新时间:2023-11-30 16:04:13 31 4
gpt4 key购买 nike

我面临一个关于织物事件的奇怪问题。看看这个片段

canvas.on('object:added', function(e) { 
console.log(e.target.type);
console.log("Something was Added");

});

canvas.on('object:removed', function(e) {
console.log(e.target.type);
console.log("Something was removed");

});

鉴于此代码库,我正在尝试撤消/重做功能。鉴于撤消和重做都可以添加、修改或删除对象,我希望在 Canvas 中添加或删除某些内容时收到通知(我不太担心在此阶段修改的对象)。

但奇怪的是,无论是否使用撤消/重做功能从 Canvas 中添加或删除对象。 我总是得到输出 - 添加了一些东西

撤消/重做例程:
// Undo Redo Clear
canvas.counter = 0;
var newleft = 0;
canvas.selection = false;
var state = [];
var mods = 0;
canvas.on(
'object:modified', function () {
updateModifications(true);
},
'object:added', function () {
updateModifications(true);
},
'object:removed' , function(e){
updateModifications(true);
console.log('test me');
});

function updateModifications(savehistory) {
if (savehistory === true) {
myjson = JSON.stringify(canvas);
state.push(myjson);
console.log(myjson);
}
}

undo = function undo() {
if (mods < state.length) {
canvas.clear().renderAll();
canvas.loadFromJSON(state[state.length - 1 - mods - 1]);
canvas.renderAll();

mods += 1;
//check_team();

//compare_states(state[state.length - 1 - mods - 1] , state[state.length - 1 - mods + 1])

}
//make_objects_selectable();
}

redo = function redo() {
if (mods > 0) {
canvas.clear().renderAll();
canvas.loadFromJSON(state[state.length - 1 - mods + 1]);
canvas.renderAll();

mods -= 1;

//check_team();
}
//make_objects_selectable();
}

clearcan = function clearcan() {
canvas.clear().renderAll();
newleft = 0;
}

Fabric 版本:“1.6.0-rc.1”

更新:在正常删除操作的情况下,该事件工作正常。因此,我添加了撤消和重做例程。

问候

最佳答案

您的撤消和重做功能基本上都做同样的事情,删除 Canvas ,加载新状态并渲染它。清除 Canvas 时,没有 object:removed事件,但触发了另一个事件,称为 canvas:cleared .这就是为什么你永远看不到你的 object:removed撤消/重做时触发的事件。另一方面,您确实看到了 object:added撤消和重做都被触发,因为我猜 canvas.renderAll将当前状态的每个对象添加到 Canvas 中(因为之前使用 canvas.clear() 删除了它)。

编辑

更好的解决方案是存储在 Canvas 上发生的每个操作,例如添加、修改或删除,并将每个操作与一些对象数据相关联。例如,您可以有一个 object_added与添加对象的序列化相关的操作,或 object_removed与已移除对象的序列化相关联的操作。对于 object_modified您将需要两个关联的对象序列化,一个是事先修改,一个是修改后。如果是 canvas_cleared操作,您必须将整个 Canvas 状态存储为关联数据。

一个简单的堆栈结构可以很好地用于操作存储。

function SimpleStackException(msg) {
this.message = msg;
this.name = 'SimpleStackException';
}

function SimpleStack() {
var MAX_ENTRIES = 2048;
var self = this;
self.sp = -1; // stack pointer
self.entries = []; // stack heap

self.push = function(newEntry) {
if (self.sp > MAX_ENTRIES - 1) {
throw new SimpleStackException('Can not push on a full stack.');
}
self.sp++;
self.entries[self.sp] = newEntry;
// make sure to clear the "future" stack after a push occurs
self.entries.splice(self.sp + 1, self.entries.length);
};

self.pop = function() {
if (self.sp < 0) {
throw new SimpleStackException('Can not pop from an empty stack.');
}
var entry = self.entries[self.sp];
self.sp--;
return entry;
};

self.reversePop = function() {
self.sp++;
if (!self.entries[self.sp]) {
self.sp--;
throw new SimpleStackException('Can not reverse pop an entry that has never been created.');
}
return self.entries[self.sp];
}

}


继续创建这样的结构: var actionHistory = new SimpleStack();
基于操作的撤消/重做工作所需的另一个功能是能够“引用” Canvas 中的对象。在 fabric.js 中,您可以引用 canvas.getObjects() 中的对象,但这是一个普通的 js 数组,并没有多大帮助。我以 UUID 的形式添加了对象 ID。
这是一个生成 UUID 的函数(在 SO 中某处拍摄,现在没有链接)

var lut = [];
for (var i = 0; i < 256; i++) {
lut[i] = (i < 16 ? '0' : '') + (i).toString(16);
}

function generateUuid() {
var d0 = Math.random() * 0xffffffff | 0;
var d1 = Math.random() * 0xffffffff | 0;
var d2 = Math.random() * 0xffffffff | 0;
var d3 = Math.random() * 0xffffffff | 0;
return lut[d0 & 0xff] + lut[d0 >> 8 & 0xff] + lut[d0 >> 16 & 0xff] + lut[d0 >> 24 & 0xff] + '-' +
lut[d1 & 0xff] + lut[d1 >> 8 & 0xff] + '-' + lut[d1 >> 16 & 0x0f | 0x40] + lut[d1 >> 24 & 0xff] + '-' +
lut[d2 & 0x3f | 0x80] + lut[d2 >> 8 & 0xff] + '-' + lut[d2 >> 16 & 0xff] + lut[d2 >> 24 & 0xff] +
lut[d3 & 0xff] + lut[d3 >> 8 & 0xff] + lut[d3 >> 16 & 0xff] + lut[d3 >> 24 & 0xff];
}


为了让结构对象有一个新的 uuid 属性,您需要将它添加到对象原型(prototype)以及对象序列化方法中

fabric.Object.prototype.uuid = "";
fabric.Object.prototype.toObject = (function(toObject) {
return function() {
return fabric.util.object.extend(toObject.call(this), {
uuid: this.uuid,
});
};
})(fabric.Object.prototype.toObject);


最后,您需要一个函数来通过此 uuid 属性“引用”对象。

function getFabricObjectByUuid(uuid) {
var fabricObject = null;
canvas.getObjects().forEach(function(object) {
if (object.uuid === uuid) {
fabricObject = object;
}
});
return fabricObject;
}


现在您需要监听 Canvas 上的事件,并更新 actionHistory因此:

canvas.on('path:created', function(path) {
var object = path.path;
object.uuid = generateUuid();
actionHistory.push({
type: 'object_added',
object: JSON.stringify(object)
});
});
canvas.on('object:added', function(e) {
var object = e.target;
// bypass the event for path objects, as they are handled by `path:created`
if (object.type === 'path') {
return;
}
// if the object has not been given an uuid, that means it is a fresh object created by this client
if (!object.uuid) {
object.uuid = generateUuid();
}
if (!object.bypassHistory) {
actionHistory.push({
type: 'object_added',
object: JSON.stringify(object)
});
}
});
canvas.on('object:modified', function(e) {
var object = e.target;
actionHistory.push({
type: 'object_modified',
objectOld: JSON.stringify(latestTouchedObject),
objectNew: JSON.stringify(object)
});
});
canvas.on('text:changed', function(e) {
var object = e.target;
actionHistory.push({
type: 'text_changed',
objectOld: JSON.stringify(latestTouchedObject),
objectNew: JSON.stringify(object)
});
});
canvas.on('object:removed', function(e) {
var object = e.target;
if (!object.bypassHistory) {
actionHistory.push({
type: 'object_removed',
object: JSON.stringify(object)
});
}
});
canvas.on('canvas:cleared', function(e) {
if (!canvas.bypassHistory) {
actionHistory.push({
type: 'canvas_cleared',
canvas: JSON.stringify(canvas)
});
}
});


仔细检查每个事件处理程序以了解将存储在 actionHistory 上的实际数据.还要注意 uuid属性实际上是添加到对象中的。关于上面的代码片段有两点需要注意。
  • bypassHistory 是 Canvas 对象和 Canvas 本身的自定义属性。您只想将用户愿意执行的操作存储到 Canvas 上。如果用户手绘一条线,您想保存该操作,您可以通过收听 path:cleared 来保存该操作。 .但是,在以编程方式绘制线的情况下(例如,在执行重做时),您可能不想存储操作。要添加此自定义属性,请执行以下操作:

    fabric.Object.prototype.bypassHistory = false;//默认值 false
  • object_modified 是一个特殊的 Action ,因为它需要存储两个对象表示:修改前和修改后。而“之后”版本可以通过 event.target 轻松获得的object:modified事件,必须以编程方式跟踪“之前”版本。在我的解决方案中,我有一个高水平 latestTouchedObject跟踪 Canvas 上最新修改对象的变量。


  • canvas.on('mouse:down', function(options) {
    if (options.target) {
    latestTouchedObject = fabric.util.object.clone(options.target);
    }
    });


    现在已经设置了 Action 存储和所有监听器,是时候实现撤消和重做功能了

    function undoAction() {
    var action, objectCandidate;
    try {
    action = actionHistory.pop();
    } catch (e) {
    console.log(e.message);
    return;
    }
    if (action.type === 'object_added') {
    objectCandidate = JSON.parse(action.object);
    var object = getFabricObjectByUuid(objectCandidate.uuid);
    object.bypassHistory = true;
    canvas.remove(object);
    } else if (action.type === 'object_removed') {
    objectCandidate = JSON.parse(action.object);
    fabric.util.enlivenObjects([objectCandidate], function(actualObjects) {
    actualObjects[0].uuid = objectCandidate.uuid;
    var object = actualObjects[0];
    object.bypassHistory = true;
    canvas.add(object);
    object.bypassHistory = false;
    });
    } else if (action.type === 'object_modified' || action.type === 'text_changed') {
    objectCandidate = JSON.parse(action.objectOld);
    fabric.util.enlivenObjects([objectCandidate], function(actualObjects) {
    actualObjects[0].uuid = objectCandidate.uuid;
    var object = actualObjects[0];
    var existingObject = getFabricObjectByUuid(objectCandidate.uuid);
    if (existingObject) {
    existingObject.bypassRemoveEvent = true;
    existingObject.bypassHistory = true;
    canvas.remove(existingObject);
    }
    object.bypassHistory = true;
    canvas.add(object);
    object.bypassHistory = false;
    });
    } else if (action.type === 'canvas_cleared') {
    var canvasPresentation = JSON.parse(action.canvas);
    canvas.bypassHistory = true;
    canvas.loadFromJSON(canvasPresentation);
    canvas.renderAll();
    canvas.bypassHistory = false;
    }
    }

    function redoAction() {
    var action, objectCandidate;
    try {
    action = actionHistory.reversePop();
    } catch (e) {
    console.log(e.message);
    return;
    }
    if (action.type === 'object_added') {
    objectCandidate = JSON.parse(action.object);
    fabric.util.enlivenObjects([objectCandidate], function(actualObjects) {
    actualObjects[0].uuid = objectCandidate.uuid;
    var object = actualObjects[0];
    object.bypassHistory = true;
    canvas.add(object);
    object.bypassHistory = false;
    });
    } else if (action.type === 'object_removed') {
    objectCandidate = JSON.parse(action.object);
    var object = getFabricObjectByUuid(objectCandidate.uuid);
    object.bypassHistory = true;
    canvas.remove(object);
    object.bypassHistory = false;
    } else if (action.type === 'object_modified' || action.type === 'text_changed') {
    objectCandidate = JSON.parse(action.objectNew);
    fabric.util.enlivenObjects([objectCandidate], function(actualObjects) {
    actualObjects[0].uuid = objectCandidate.uuid;
    var object = actualObjects[0];
    var existingObject = getFabricObjectByUuid(objectCandidate.uuid);
    if (existingObject) {
    existingObject.bypassRemoveEvent = true;
    existingObject.bypassHistory = true;
    canvas.remove(existingObject);
    }
    object.bypassHistory = true;
    canvas.add(object);
    object.bypassHistory = false;
    });
    } else if (action.type === 'canvas_cleared') {
    canvas.clear();
    }
    }


    我不知道这个解决方案(和代码)是否符合您的开箱即用需求。也许它在某种程度上与我的特定应用程序有关。我希望你能理解我的建议并加以利用。

    关于javascript - 对象 :removed Event Not Fired - Fabric JS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37273470/

    31 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com