2
2
/*
3
3
* @Author : 秦少卫
4
4
* @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
7
7
* @Description : 历史记录插件
8
8
*/
9
9
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' ;
19
11
12
+ type IEditor = Editor ;
20
13
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
- } ;
28
14
29
15
class HistoryPlugin implements IPluginTempl {
30
16
static pluginName = 'HistoryPlugin' ;
31
- static apis = [ 'undo' , 'redo' , 'historyUpdate' ] ;
17
+ static apis = [ 'undo' , 'redo' , 'historyUpdate' , 'clearAndSaveState' , 'saveState' ] ;
32
18
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
+
33
27
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 ) {
38
30
this . _init ( ) ;
39
31
}
40
32
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
+ // 更新历史记录状态
42
50
this . canvas . on ( 'history:append' , ( ) => {
43
51
this . historyUpdate ( ) ;
44
52
} ) ;
53
+
54
+ // 页面离开提示
45
55
window . addEventListener ( 'beforeunload' , ( e ) => {
46
- if ( this . canvas . historyStack . length > 0 ) {
56
+ const { undoCount } = this . getState ( ) ;
57
+ if ( undoCount > 0 ) {
47
58
( e || window . event ) . returnValue = '确认离开' ;
48
59
}
49
60
} ) ;
50
61
}
51
62
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
+ // 公开方法
52
122
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 ) ;
55
125
}
56
126
57
- // 导入模板之后,清理 History 缓存
58
127
hookImportAfter ( ) {
59
- this . canvas . clearHistory ( true ) ;
128
+ this . clear ( ) ;
60
129
this . historyUpdate ( ) ;
61
130
return Promise . resolve ( ) ;
62
131
}
63
132
64
133
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
+ }
67
142
}
68
143
69
144
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
+ }
72
153
}
73
154
74
- // 快捷键扩展回调
75
155
hotkeyEvent ( eventName : string , e : KeyboardEvent ) {
76
156
if ( e . type === 'keydown' ) {
77
157
switch ( eventName ) {
@@ -86,6 +166,13 @@ class HistoryPlugin implements IPluginTempl {
86
166
}
87
167
}
88
168
}
169
+
170
+ clearAndSaveState ( ) {
171
+ const currentState = this . getCurrentState ( ) ;
172
+ this . stack = [ currentState ] ; // 只保留当前状态作为第一条记录
173
+ this . currentIndex = 1 ;
174
+ this . historyUpdate ( ) ;
175
+ }
89
176
}
90
177
91
178
export default HistoryPlugin ;
0 commit comments