Created
July 6, 2022 10:17
-
-
Save Vishal-Isharani/2accb72f5906bea9b648128f247b8d45 to your computer and use it in GitHub Desktop.
Konva js Undo/Redo History
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Konva from 'konva'; | |
import { ShapeConfig } from 'konva/lib/Shape'; | |
export class HistoryStat { | |
private static _instance: HistoryStat; | |
public _currentStat: any[] = []; | |
private _currentPointer: number = -1; | |
private _history: Array<Object[]> = []; | |
constructor() { | |
if (!HistoryStat._instance) { | |
HistoryStat._instance = this; | |
} else { | |
return HistoryStat._instance; | |
} | |
} | |
/** | |
* saveStateToHistory | |
* save state to history | |
* @param stat | |
* @param onlyUpdateTheHistory this flag is used to update the history without incrementing the current pointer | |
*/ | |
public saveStateToHistory(stat: Object[], onlyUpdateTheHistory: boolean = false): void { | |
this._history = [...this._history, stat]; | |
// only increment the pointer if we are not only updating the history | |
// this is used to update the history without incrementing the current pointer (specifically for undo/redo) | |
if (!onlyUpdateTheHistory) { | |
this._currentPointer++; | |
} | |
} | |
/** | |
* canUndo | |
* checks if there is any state to undo | |
*/ | |
public canUndo(): boolean { | |
return this._currentPointer > 0; | |
} | |
/** | |
* undo | |
* undo last state | |
*/ | |
public undo(): Object[] { | |
if (this._currentPointer === 0) { | |
return; | |
} | |
this._currentPointer--; | |
return this._history[this._currentPointer]; | |
} | |
/** | |
* canRedo | |
* checks if there is any state to redo | |
*/ | |
public canRedo(): boolean { | |
return this._currentPointer > -1 && this._currentPointer < this._history.length - 1; | |
} | |
/** | |
* redo | |
* redo last state | |
*/ | |
public redo(): Object[] { | |
if (this._currentPointer === this._history.length - 1) { | |
return; | |
} | |
this._currentPointer += 1; | |
return this._history[this._currentPointer]; | |
} | |
/** | |
* appendNewStat | |
* appends new stat to current stat | |
* @param shape | |
*/ | |
public appendNewStat(shape: Konva.Shape | Konva.Stage | Konva.Group): void { | |
// get shape attributes | |
const attrs = { ...shape.attrs }; | |
// verify if shape is a image then delete the image attribute from attrs because its hold HTMLImageElement | |
if (shape instanceof Konva.Image) { | |
// delete image attribute from attrs | |
delete attrs['image']; | |
} else if (shape instanceof Konva.Stage) { | |
// delete container attribute from attrs | |
delete attrs['container']; | |
} | |
this._currentStat = [...this._currentStat, attrs]; | |
this.saveStateToHistory(this._currentStat.slice()); | |
} | |
/** | |
* updateStat | |
* updates a stat in history and adds new stat to current stat | |
* @param shape | |
* @param attrs | |
* @param onlyUpdateTheHistory this flag is used to update the history without incrementing the current pointer | |
*/ | |
public updateState( | |
shape: Konva.Shape | Konva.Shape[] | Konva.Stage | Konva.Group, | |
attrs: Partial<ShapeConfig>, | |
onlyUpdateTheHistory: boolean = false | |
): void { | |
const shapes = Array.isArray(shape) ? shape : [shape]; | |
const shapeAttrs = { ...attrs }; | |
for (const shp of shapes) { | |
// verify if shape is a image then delete the image attribute from attrs because its hold HTMLImageElement | |
if (shp instanceof Konva.Image) { | |
// delete image attribute from attrs | |
delete shapeAttrs['image']; | |
} else if (shape instanceof Konva.Stage) { | |
// delete container attribute from attrs | |
delete attrs['container']; | |
} | |
// get stateIndex | |
const statIndex = this._currentStat.findIndex(stat => stat.id === shp.id()); | |
this._currentStat = this._currentStat.slice(); | |
// update the current state | |
this._currentStat[statIndex] = { | |
...this._currentStat[statIndex], | |
...shapeAttrs | |
}; | |
} | |
// save state to history | |
this.saveStateToHistory([...this._currentStat], onlyUpdateTheHistory); | |
} | |
/** | |
* removeStat | |
* removes a stat from history and adds new stat to current stat | |
* @param shape | |
*/ | |
public removeState(shape: Konva.Shape): void { | |
const statIndex = this._currentStat.findIndex(stat => stat.id === shape.id()); | |
this._currentStat = this._currentStat.filter((stat, index) => index !== statIndex); | |
this.saveStateToHistory(this._currentStat.slice()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How to use this library?