2016-05-17 2 views
1

私はファブリックイベントで奇妙な問題に直面しています。このスニペットをご覧くださいオブジェクト:removedイベントが実行されません - 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"); 

     }); 

このコードベースでは、元に戻す/やり直しの機能を試しています。両方とも元に戻す& 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"

更新:イベントが正常に動作しています通常の削除操作の場合。そこで私は、元に戻すとやり直しルーチンを追加しました。

よろしく

答えて

1

どちらもあなたの元に戻すと機能をやり直しは基本的に同じことを行う、キャンバスを消去し、新しい状態をロードし、それをレンダリングします。キャンバスを消去すると、というイベントが発生しますが、object:removedイベントは発生しません。そのため、元に戻す/やり直しを行うときに、object:removedイベントが発生することはありません。一方、canvas.renderAllは、キャンバスに現在の状態のすべてのオブジェクトを追加すると思います(以前はcanvas.clear()で削除されていたため)、アンドゥとリドゥの両方で、object:addedが発生しました。追加、変更または削除、およびいくつかのオブジェクトデータに関連付けられた各作用を有するよう

EDIT

は、より良い解決策は、キャンバス上で起こるすべてのアクションを格納することです。たとえば、追加されたオブジェクトのシリアライゼーションに関連付けられたアクションをobject_addedに、または削除されたオブジェクトのシリアライズに関連付けられたアクションを、object_removedに割り当てることができます。 object_modifiedの場合、2つの関連するオブジェクトの直列化が必要になります.1つは変更前、もう1つは変更後です。 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配列なのであまり役に立ちません。オブジェクトIDをUUIDの形式で追加しました。ここ は(、SOのどこかに取られ、今のリンクを持っていけない)関数である股関節は、布地オブジェクトはあなたにそれを追加する必要が新しいUUIDプロパティを持つようにするためには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.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プロパティが実際にオブジェクトに追加されたときにも注意してください。上記のスニペットには2つの注意点があります。

  1. bypassHistoryは、キャンバスオブジェクトとキャンバス自体のカスタムプロパティです。キャンバスにユーザが気に入った行動を保存したいだけです。ユーザーが線を手で描く場合は、path:clearedを聞いてその行を保存します。ただし、プログラムで描画された行(たとえば、やり直しを実行する場合)の場合は、アクションを保存したくない場合があります。このカスタムプロパティを追加するには、次のようにします。

    fabric.Object.prototype.bypassHistory = false; //デフォルト値false

  2. object_modifiedは、変更前と変更後の2つのオブジェクト表現を格納する必要があるため、特別なアクションです。 "after"バージョンはobject:modifiedイベントのevent.target経由で簡単に取得できますが、 "before"バージョンはプログラムでトラッキングする必要があります。私の解決策では、キャンバス上の最新の修正されたオブジェクトを追跡する高いレベルの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(); 
 
    } 
 
}

をアンドゥを実装し、やり直ししてみましょう私は、このソリューション(およびコード)があなたのニーズをすぐに受け入れることができるかどうかは分かりません。たぶん、それは私の特定のアプリケーションにある程度結びついています。私はあなたが私の提案するものを理解し、それを利用することができれば幸いです。

+0

うん、そうかもしれない。あなたは手で問題を克服するために何を提案しますか?私はオブジェクトが現在の状態から削除されているかどうかを知る必要があります。私は何とかjsonオブジェクトを比較し、欠けているものを見つけ出すと考えていました。しかしそれはそれの考えによって退屈なように聞こえる。 –

+0

私はあなたがあなたのアプローチでそれを達成できるとは思わない。各「状態スロット」にキャンバス全体のプレーンなシリアル化を保存するだけで、個々のオブジェクトを照会するのは困難です。私は、キャンバス全体のストレージではなく、個々のオブジェクトとアクションを持つスタックに基づいた私のソリューションで、最新の回答(後日)をお届けします。 –

+0

これは非常に役に立ちます –

関連する問題