Skip to content

Commit 6895eca

Browse files
chaochao15“935183846@qq.com”
and
“935183846@qq.com”
authored
fix(修复撤销功能): 修复了撤销按钮无法撤销画布数据的bug (#562)
* fix(修复撤销功能): 修复了撤销按钮无法撤销画布数据的bug * fix(补充): 补充了上一个提交遗漏的代码 --------- Co-authored-by: “935183846@qq.com” <“935183846@qq.com”>
1 parent f5d6e02 commit 6895eca

File tree

5 files changed

+128
-35
lines changed

5 files changed

+128
-35
lines changed

packages/core/plugin/AddBaseTypePlugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ export default class AddBaseTypePlugin implements IPluginTempl {
4343
if (!event && center) {
4444
this._toCenter(item);
4545
}
46-
this.canvas.refreshHistory();
4746
this.canvas.setActiveObject(item);
4847
this.canvas.renderAll();
48+
this.editor.saveState();
4949
}
5050

5151
_toEvent(item: fabric.Object, event: DragEvent) {

packages/core/plugin/BarCodePlugin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ class BarCodePlugin implements IPluginTempl {
7777
this.canvas.add(imgEl);
7878
this.canvas.setActiveObject(imgEl);
7979
this.editor.position('center');
80+
this.canvas.renderAll();
81+
this.editor.saveState();
8082
},
8183
{ crossOrigin: 'anonymous' }
8284
);

packages/core/plugin/DrawLinePlugin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ class DrawLinePlugin implements IPluginTempl {
126126
this.lineToDraw.setCoords();
127127
this.isDrawingLine = false;
128128
canvas.discardActiveObject();
129+
canvas.renderAll();
130+
this.editor.saveState();
129131
});
130132
}
131133

packages/core/plugin/HistoryPlugin.ts

Lines changed: 121 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,76 +2,156 @@
22
/*
33
* @Author: 秦少卫
44
* @Date: 2023-06-20 13:06:31
5-
* @LastEditors: George GeorgeSmith163@163.com
6-
* @LastEditTime: 2024-10-15 09:35:35
5+
* @LastEditors: 秦少卫
6+
* @LastEditTime: 2025-01-06 17:22:47
77
* @Description: 历史记录插件
88
*/
99
import { fabric } from 'fabric';
10-
import '../utils/fabric-history.js';
11-
import type { IEditor, IPluginTempl } from '@kuaitu/core';
12-
13-
type IPlugin = Pick<HistoryPlugin, 'undo' | 'redo' | 'historyUpdate'>;
14-
15-
declare module '@kuaitu/core' {
16-
// eslint-disable-next-line @typescript-eslint/no-empty-interface
17-
interface IEditor extends IPlugin {}
18-
}
10+
import Editor from '../Editor';
1911

12+
type IEditor = Editor;
2013
type callback = () => void;
21-
type extendCanvas = {
22-
undo: (callback?: callback) => void;
23-
redo: (callback?: callback) => void;
24-
clearHistory: () => void;
25-
historyStack: any[];
26-
historyIndex: number;
27-
};
2814

2915
class HistoryPlugin implements IPluginTempl {
3016
static pluginName = 'HistoryPlugin';
31-
static apis = ['undo', 'redo', 'historyUpdate'];
17+
static apis = ['undo', 'redo', 'historyUpdate', 'clearAndSaveState', 'saveState'];
3218
static events = [];
19+
20+
// 历史记录相关属性
21+
private stack: string[] = [];
22+
private currentIndex = 0;
23+
private maxLength = 100;
24+
private isProcessing = false;
25+
private isLoading = false;
26+
3327
hotkeys: string[] = ['ctrl+z', 'ctrl+shift+z', '⌘+z', '⌘+shift+z'];
34-
constructor(public canvas: fabric.Canvas & extendCanvas, public editor: IEditor) {
35-
fabric.Canvas.prototype._historyNext = () => {
36-
return this.editor.getJson();
37-
};
28+
29+
constructor(public canvas: fabric.Canvas, public editor: IEditor) {
3830
this._init();
3931
}
4032

41-
_init() {
33+
private _init() {
34+
// 监听对象变更事件
35+
const events = {
36+
'object:removed': () => this.saveState(),
37+
'object:modified': () => this.saveState(),
38+
'object:skewing': () => this.saveState(),
39+
};
40+
41+
// 绑定事件
42+
Object.entries(events).forEach(([event, handler]) => {
43+
this.canvas.on(event, handler);
44+
});
45+
46+
// 初始化状态
47+
this.saveState();
48+
49+
// 更新历史记录状态
4250
this.canvas.on('history:append', () => {
4351
this.historyUpdate();
4452
});
53+
54+
// 页面离开提示
4555
window.addEventListener('beforeunload', (e) => {
46-
if (this.canvas.historyStack.length > 0) {
56+
const { undoCount } = this.getState();
57+
if (undoCount > 0) {
4758
(e || window.event).returnValue = '确认离开';
4859
}
4960
});
5061
}
5162

63+
// 获取当前状态
64+
private getCurrentState() {
65+
return this.editor.getJson();
66+
}
67+
68+
// 保存状态
69+
private saveState() {
70+
if (this.isProcessing) return;
71+
72+
// 清除当前索引后的记录
73+
this.stack.splice(this.currentIndex);
74+
this.stack.push(this.getCurrentState());
75+
76+
// 维护最大长度
77+
if (this.stack.length > this.maxLength) {
78+
this.stack.shift();
79+
} else {
80+
this.currentIndex++;
81+
}
82+
this.historyUpdate();
83+
}
84+
85+
// 加载状态
86+
private _loadState(state: string, eventName: string, callback?: callback) {
87+
this.isLoading = true;
88+
this.isProcessing = true;
89+
90+
// 处理 workspace 的特殊情况
91+
const parsedState = JSON.parse(state);
92+
const workspace = parsedState.objects?.find((item: any) => item.id === 'workspace');
93+
if (workspace) {
94+
workspace.evented = false;
95+
}
96+
97+
this.canvas.loadFromJSON(state, () => {
98+
this.canvas.renderAll();
99+
this.canvas.fire(eventName);
100+
this.isProcessing = false;
101+
this.isLoading = false;
102+
callback?.();
103+
});
104+
}
105+
106+
// 获取历史记录状态
107+
private getState() {
108+
return {
109+
undoCount: this.currentIndex - 1,
110+
redoCount: this.stack.length - this.currentIndex,
111+
};
112+
}
113+
114+
// 清空历史记录
115+
private clear() {
116+
this.stack = [];
117+
this.currentIndex = 0;
118+
this.saveState();
119+
}
120+
121+
// 公开方法
52122
historyUpdate() {
53-
const { historyStack, historyIndex } = this.canvas;
54-
this.editor.emit('historyUpdate', historyIndex, historyStack.length - historyIndex);
123+
const { undoCount, redoCount } = this.getState();
124+
this.editor.emit('historyUpdate', undoCount, redoCount);
55125
}
56126

57-
// 导入模板之后,清理 History 缓存
58127
hookImportAfter() {
59-
this.canvas.clearHistory(true);
128+
this.clear();
60129
this.historyUpdate();
61130
return Promise.resolve();
62131
}
63132

64133
undo() {
65-
this.canvas.undo();
66-
this.historyUpdate();
134+
if (this.isLoading || this.currentIndex <= 1) return;
135+
136+
this.currentIndex--;
137+
const state = this.stack[this.currentIndex - 1];
138+
if (state) {
139+
this._loadState(JSON.stringify(state), 'history:undo');
140+
this.historyUpdate();
141+
}
67142
}
68143

69144
redo() {
70-
this.canvas.redo();
71-
this.historyUpdate();
145+
if (this.isLoading || this.currentIndex >= this.stack.length) return;
146+
147+
const state = this.stack[this.currentIndex];
148+
if (state) {
149+
this._loadState(JSON.stringify(state), 'history:redo');
150+
this.currentIndex++;
151+
this.historyUpdate();
152+
}
72153
}
73154

74-
// 快捷键扩展回调
75155
hotkeyEvent(eventName: string, e: KeyboardEvent) {
76156
if (e.type === 'keydown') {
77157
switch (eventName) {
@@ -86,6 +166,13 @@ class HistoryPlugin implements IPluginTempl {
86166
}
87167
}
88168
}
169+
170+
clearAndSaveState() {
171+
const currentState = this.getCurrentState();
172+
this.stack = [currentState]; // 只保留当前状态作为第一条记录
173+
this.currentIndex = 1;
174+
this.historyUpdate();
175+
}
89176
}
90177

91178
export default HistoryPlugin;

packages/core/plugin/QrCodePlugin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ class QrCodePlugin implements IPluginTempl {
131131
this.canvas.add(imgEl);
132132
this.canvas.setActiveObject(imgEl);
133133
this.editor.position('center');
134+
this.canvas.renderAll();
135+
this.editor.saveState();
134136
},
135137
{ crossOrigin: 'anonymous' }
136138
);

0 commit comments

Comments
 (0)