ikuaitu / vue-fabric-editor

快图设计-基于fabric.js和Vue的开源图片编辑器,可自定义字体、素材、设计模板。fabric.js and Vue based image editor, can customize fonts, materials, design templates.
https://ikuaitu.github.io/doc/#/
MIT License
4.87k stars 911 forks source link

历史记录插件感觉有点问题 #466

Closed z910130233 closed 2 months ago

z910130233 commented 2 months ago

使用历史记录插件回退操作时,回退到最后一步时,形状left和top都为0,所以形状都跑到左上角去了,不知道对不对,我查看了记录时的json,我觉得可能是形状还没加载完成的时候,没有记录完整的形状属性数据,我根据这个code改动了下

`

import {Canvas} from 'fabric';
import Editor from '../Editor';
type IEditor = Editor;

type HistoryEventCallback = () => void;
class HistoryPlugin {
  private canvas: Canvas;
  private editor: IEditor;
  private historyUndo: string[];
  private historyRedo: string[];
  private extraProps: string[];
  private historyNextState: string;
  private historyProcessing: boolean;

  static pluginName = 'HistoryPlugin';
  static apis = ['undo', 'redo', 'clearHistory'];
  static events = ['historyUpdate'];
  public hotkeys: string[] = ['ctrl+z', 'ctrl+shift+z', '⌘+z', '⌘+shift+z'];

  constructor(canvas: Canvas, editor: IEditor) {
      this.canvas = canvas;
      this.editor = editor;
      this.historyUndo = [];
      this.historyRedo = [];
      this.extraProps = ['selectable', 'hasControls', 'left', 'top', 'id'];
      this.historyNextState = this._historyNext();
      this.historyProcessing = false;

      this._historyInit();
  }

  private _historyNext(): string {
      return JSON.stringify(this.canvas.toDatalessJSON(this.extraProps));
      // return JSON.stringify(this.canvas.toDatalessJSON());
      // return JSON.stringify(this.canvas.toJSON());
  }

  private _historyEvents() {
      return {
          'object:added': this._historySaveAction.bind(this),
          'object:removed': this._historySaveAction.bind(this),
          'object:modified': this._historySaveAction.bind(this),
          'object:skewing': this._historySaveAction.bind(this),
      };
  }

  private _historyInit() {
      this.canvas.on(this._historyEvents());

      window.addEventListener('beforeunload', (e) => {
          if (this.historyUndo.length > 0 || this.historyRedo.length > 0) {
              (e || window.event).returnValue = '确认离开';
          }
      });
  }

  private _historyDispose() {
      this.canvas.off(this._historyEvents());
  }

  private _historySaveAction() {
      if (this.historyProcessing) return;

      this.canvas.once('after:render', () => {
          const json = this.historyNextState;
          console.log('save', json)
          this.historyUndo.push(json);
          this.historyNextState = this._historyNext();
          this.canvas.fire('history:append', {json: json});

          this.historyUpdate();
      })
  }

  historyUpdate() {
      this.editor.emit('historyUpdate', this.historyUndo.length, this.historyRedo.length);
  }

  undo(callback?: HistoryEventCallback) {
      this.historyProcessing = true;

      const history = this.historyUndo.pop();
      if (history) {
          this.historyRedo.push(this._historyNext());
          this.historyNextState = history;
          this._loadHistory(history, 'history:undo', callback);
      } else {
          this.historyProcessing = false;
      }
      this.historyUpdate();
  }

  redo(callback?: HistoryEventCallback) {
      this.historyProcessing = true;

      const history = this.historyRedo.pop();
      if (history) {
          this.historyUndo.push(this._historyNext());
          this.historyNextState = history;
          this._loadHistory(history, 'history:redo', callback);
      } else {
          this.historyProcessing = false;
      }
      this.historyUpdate();
  }

  private _loadHistory(history: string, event: string, callback?: HistoryEventCallback) {
      const that = this;
      this.canvas.loadFromJSON(history, () => {
          that.canvas.renderAll();
          that.canvas.fire(event);
          that.historyProcessing = false;

          if (callback) callback();
      });
  }

  clearHistory() {
      this.historyUndo = [];
      this.historyRedo = [];
      this.canvas.fire('history:clear');
  }

  onHistory() {
      this.historyProcessing = false;
      this._historySaveAction();
  }

  canUndo(): boolean {
      return this.historyUndo.length > 0;
  }

  canRedo(): boolean {
      return this.historyRedo.length > 0;
  }

  offHistory() {
      this.historyProcessing = true;
  }

  // 快捷键扩展回调
  hotkeyEvent(eventName: string, e: any) {
      if (e.type === 'keydown') {
          switch (eventName) {
              case 'ctrl+z':
              case '⌘+z':
                  this.undo();
                  break;
              case 'ctrl+shift+z':
              case '⌘+shift+z':
                  this.redo();
                  break;
          }
      }
  }
  }
  export default HistoryPlugin;

`

nihaojob commented 2 months ago

这个问题下午已经修复了,这是相关代码哈 https://github.com/nihaojob/vue-fabric-editor/commit/64e3fb689640e30d40fb15cf9a1a8eef7e3a7c90