GithubHelp home page GithubHelp logo

Fabric v6 support about fabric-history HOT 2 OPEN

alimozdemir avatar alimozdemir commented on June 14, 2024 1
Fabric v6 support

from fabric-history.

Comments (2)

StringKe avatar StringKe commented on June 14, 2024 1

try this

histroy.ts

import { Canvas } from 'fabric';

type HistoryEventCallback = () => void;

class HistoryFeature {
    private canvas: Canvas;
    private historyUndo: string[];
    private historyRedo: string[];
    private extraProps: string[];
    private historyNextState: string;
    private historyProcessing: boolean;

    constructor(canvas: Canvas) {
        this.canvas = canvas;
        this.historyUndo = [];
        this.historyRedo = [];
        this.extraProps = ['selectable', 'editable'];
        this.historyNextState = this._historyNext();
        this.historyProcessing = false;

        this._historyInit();
    }

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

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

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

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

        const json = this.historyNextState;
        this.historyUndo.push(json);
        this.historyNextState = this._historyNext();
        this.canvas.fire('history:append', { json: json });
    }

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

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

    private _loadHistory(history: string, event: string, callback?: HistoryEventCallback) {
        this.canvas.loadFromJSON(history, () => {
            this.canvas.renderAll();
            this.canvas.fire(event);
            this.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;
    }
}

export default HistoryFeature;

from fabric-history.

insinfo avatar insinfo commented on June 14, 2024

In Dart I did this because I believe it is more efficient than saving the entire canvas at all times

import 'package:rava_frontend/src/shared/js_interop/fabric_interop.dart';

extension ListExtension on List {
  List splice2(int index, [num howMany = 0, dynamic elements]) {
    var endIndex = index + howMany.truncate();
    removeRange(index, endIndex >= length ? length : endIndex);
    if (elements != null) {
      insertAll(index, elements is List ? elements : [elements]);
    }
    return this;
  }

  List splice(int start, [int deleteCount = 0]) {
    final deletedElements = sublist(start, start + deleteCount);
    removeRange(start, start + deleteCount);
    return deletedElements;
  }
}

enum StateAction { create, modify, remove, none }

class TransformCommand {
  FabricObject element;
  Map<String, dynamic> state = {};
  Map<String, dynamic> prevState = {};
  List<String> statePropertiesKeys = [];
  StateAction action = StateAction.none;
  CommandHistory? history;

  TransformCommand(this.element, this.action) {
    switch (action) {
      case StateAction.create:
        element.saveState(); // save state for initialize
        init();
        break;
      case StateAction.modify:
        init();
        element.saveState(); // save new state
        break;
      case StateAction.remove:
        break;
      case StateAction.none:
    }
  }
  void init() {
    _initStateProperties();
    _saveState();
    _savePrevState();
  }

  /// redo
  void execute() {
    // print('TransformCommand execute  ${action.name}');
    if (action == StateAction.create) {
      history?.canvas.add(element);
    } else if (action == StateAction.remove) {
      history?.canvas.remove(element);
    } else {
      _restoreState();
      element.setCoords();
    }
  }

  /// undo
  void undo() {
    // print('TransformCommand undo ${action.name}');
    if (action == StateAction.create) {
      history?.canvas.remove(element);
    } else if (action == StateAction.remove) {
      history?.canvas.add(element);
    } else {
      _restorePrevState();
      element.setCoords();
    }
  }

  // private
  void _initStateProperties() {
    statePropertiesKeys = element.statePropertiesKeys;
  }

  void _restoreState() {
    _restore(state);
  }

  void _restorePrevState() {
    _restore(prevState);
  }

  void _restore(Map<String, dynamic> state) {
    for (var prop in statePropertiesKeys) {
      element.set(prop, state[prop]);
    }
  }

  void _saveState() {
    for (var prop in statePropertiesKeys) {
      state[prop] = element.get(prop);
    }
    //print('_saveState $state');
  }

  void _savePrevState() {
    final oldState = element.statePropertiesAsMap;

    if (oldState != null) {
      for (var prop in statePropertiesKeys) {
        prevState[prop] = oldState[prop];
      }
    } else {
      throw Exception(
          'TransformCommand@_savePrevState não foi possivel obter o estado anterior');
    }
    //print('_savePrevState $oldState');
  }
}

class CommandHistory {
  final commands = <TransformCommand>[];
  int index = 0;

  final Canvas canvas;

  CommandHistory(this.canvas);

  int getIndex() {
    return index;
  }

  CommandHistory add(TransformCommand command) {
    command.history = this;
    if (commands.isNotEmpty) {
      commands.splice(index, commands.length - index);
    }
    commands.add(command);
    index++;
    return this;
  }

  /// undo
  CommandHistory back() {
    //print(' CommandHistory back() ${commands.length} $index');
    if (index > 0) {
      final command = commands[--index];
      command.undo();
    }
    return this;
  }

  /// redo
  CommandHistory forward() {
    //print(' CommandHistory forward()  ${commands.length} $index');
    if (index < commands.length) {
      final command = commands[index++];
      command.execute();
    }
    return this;
  }

  CommandHistory clear() {
    commands.clear();
    commands.length = 0;
    index = 0;
    return this;
  }
}
...
  final canvas = Canvas( canvasEl );
  final history = CommandHistory(canvas);
...
 void onModifiedObject(ModifiedEvent event) {  
    final target = event.target;
    history.add(TransformCommand(target, StateAction.modify));    
  }

from fabric-history.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.