Comments (2)
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.
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)
- Uncaught TypeError: canvas.undo is not a function HOT 2
- Create an ability to store custom properties HOT 2
- Preserve selected objects on each state when undo/redo
- custom added properties are removed HOT 2
- [Bug] Undo after first drawing on a Canvas with background image set not working properly HOT 2
- Non-Selectable objects still get selectable eventually HOT 2
- Insufficient Granularity of Access Control in JSDom - https://github.com/advisories/GHSA-f4c9-cqv8-9v98 HOT 2
- Clear canvas isn't part of the history HOT 3
- Undo/Redo functions are removing 2 objects at same time HOT 2
- Undo after setting scaled background image ends in white space in the right corner.
- Keep objects selected after undo/redo
- when load from json it removes all items on undo but I want initial json HOT 2
- Type definition HOT 2
- Skip Action to become part of history HOT 1
- Could Undo Redo with Erasing? HOT 3
- Undo Redo remove my object's attributes HOT 2
- Is there a way to exclude some object saving in history? HOT 2
- Multiple canvases: managing history undo and redo
- Makes browser to crash.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from fabric-history.