簡體   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