繁体   English   中英

对象:移除的事件未触发-Fabric JS

[英]object:removed Event Not Fired - Fabric JS

我在面料活动方面面临着一个怪异的问题。 看看这个片段

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");

        });

给定此代码库,我正在尝试使用撤消/重做功能。 鉴于undo和redo都可以添加,修改或删除对象,因此如果在画布中添加或删除了某些对象,我希望得到通知(我不担心在此阶段修改对象)。

但是,无论使用撤消/重做功能从画布中添加对象还是从画布中删除对象,这都足够奇怪。 我总是得到输出-添加了一些东西

撤消/重做例程:

// 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;
        }

结构版本:“ 1.6.0-rc.1”

更新:如果执行正常的删除操作,则事件运行正常。 因此,我添加了撤消和重做例程。

问候

撤消和重做功能基本上都做相同的事情,擦除画布,加载新状态并进行渲染。 当您清除画布时,没有object:removed事件,但会触发另一个事件,称为canvas:cleared 这就是为什么在执行撤消/重做操作时从未看到过触发object:removed事件的原因。 另一方面,您确实看到了在撤消和重做时均触发的object:added ,因为我猜测canvas.renderAll将当前状态下的每个对象canvas.renderAll添加到画布中(因为先前已使用canvas.clear()删除了它)。

编辑

更好的解决方案是存储在画布上发生的每个动作,例如添加,修改或删除,并使每个动作与某些对象数据相关联。 例如,您可能具有与添加的对象的序列化关联的object_added动作,或与已移除的对象的序列化关联的object_removed动作。 对于object_modified您将需要两个关联的对象序列化,一个在修改之前,一个在修改之后。 如果使用canvas_cleared动作,则必须将整个画布状态存储为关联数据。

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

 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();

要使基于动作的撤消/重做工作需要的另一个功能是能够“引用”画布中的对象。 在fabric.js中,您可以从canvas.getObjects()引用对象,但这是一个普通的js数组,并没有太大帮助。 我以UUID的形式添加了对象ID。 这是一个函数(在SO中某个地方使用,现在没有链接)可以生成UUID

 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]; } 

为了使Fabric对象具有新的uuid属性,您需要将其添加到对象原型以及对象序列化方法中

 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; } 

现在,您需要在画布上侦听事件,并相应地更新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属性实际添加到对象时也要注意。 关于以上代码段,有两点需要注意。

  1. passbyHistory是画布对象和画布本身的自定义属性。 您只想将用户愿意执行的操作存储在画布上。 如果用户手绘线,则要保存该动作,可以通过监听path:cleared 但是,在以编程方式绘制线的情况下(例如,执行重做时),您可能不想存储该动作。 要添加此自定义属性,请执行以下操作:

    fabric.Object.prototype.bypassHistory = false; //默认值false

  2. object_modified是一种特殊的操作,因为它需要存储两个对象表示形式:修改之前和之后。 尽管可以通过object:modified事件的event.target轻松获得“之后”版本,但必须以编程方式跟踪“之前”版本。 在我的解决方案中,我有一个高级的latestTouchedObject变量,可跟踪画布上最新修改的对象。

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

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

 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(); } } 

我不知道此解决方案(和代码)是否可以立即满足您的需求。 也许在某种程度上与我的特定应用有关。 希望您设法理解我的建议并加以利用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM