-
Notifications
You must be signed in to change notification settings - Fork 146
/
Copy pathUpdateManager.js
224 lines (126 loc) · 3.99 KB
/
UpdateManager.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*
Job:
- recording components required updates
- trigger those updates when 'update' is called
This module is a bit special. It is, with FontLibrary, one of the only modules in the 'component'
directory not to be used in component composition (Object.assign).
When MeshUIComponent is instanciated, it calls UpdateManager.register().
Then when MeshUIComponent receives new attributes, it doesn't update the component right away.
Instead, it calls UpdateManager.requestUpdate(), so that the component is updated when the user
decides it (usually in the render loop).
This is best for performance, because when a UI is created, thousands of componants can
potentially be instantiated. If they called updates function on their ancestors right away,
a given component could be updated thousands of times in one frame, which is very ineficient.
Instead, redundant update request are moot, the component will update once when the use calls
update() in their render loop.
*/
export { update }
export default {
requestUpdate,
register,
disposeOf
};
//
const components = [];
const requestedUpdates = {};
// const timestamp = Date.now();
// get called by MeshUIComponent when component.set has been used.
// It registers this component and all its descendants for the different types of updates that were required.
function requestUpdate( component, updateParsing, updateLayout, updateInner ) {
component.traverse( (child)=> {
if ( !child.isUI ) return
// request updates for all descendants of the passed components
if ( !requestedUpdates[ child.id ] ) {
requestedUpdates[ child.id ] = {
updateParsing,
updateLayout,
updateInner
};
} else {
if (updateParsing) requestedUpdates[ child.id ].updateParsing = true;
if (updateLayout) requestedUpdates[ child.id ].updateLayout = true;
if (updateInner) requestedUpdates[ child.id ].updateInner = true;
}
});
}
// Register a passed component for later updates
function register( component ) {
if ( !components.includes(component) ) {
components.push( component );
}
}
// Unregister a component (when it's deleted for instance)
function disposeOf( component ) {
const idx = components.indexOf( component );
if ( idx > -1 ) {
components.splice( idx, 1 );
}
}
// Trigger all requested updates of registered components
function update() {
if ( Object.keys(requestedUpdates).length > 0 ) {
const roots = components.filter( (component)=> {
return !component.getUIParent()
});
//
Promise.all( roots.map( (component)=> {
return callParsingUpdateOf( component );
}))
.then( ()=> {
roots.forEach( (component)=> {
callUpdatesOf( component );
});
})
.catch( (err)=> {
console.error(err)
});
}
}
// Synchronously calls parseParams update of all components from parent to children
function callParsingUpdateOf( component ) {
return new Promise( (resolve)=> {
new Promise( (resolveThisComponent, reject)=> {
const request = requestedUpdates[ component.id ];
if ( request && request.updateParsing ) {
request.updateParsing = false;
component.parseParams( resolveThisComponent, reject );
} else {
resolveThisComponent();
}
})
.then( ()=> {
Promise.all( component.getUIChildren().map( (childUI)=> {
return callParsingUpdateOf( childUI );
}))
.then( ()=> {
resolve();
})
.catch( (err)=> {
console.error( err );
});
})
.catch( (err)=> {
console.error( err );
});
});
}
// Calls updateLayout and updateInner functions of components that need an update
function callUpdatesOf( component ) {
const request = requestedUpdates[ component.id ]
//
if ( request && request.updateLayout ) {
request.updateLayout = false;
component.updateLayout();
}
//
if ( request && request.updateInner ) {
request.updateInner = false;
component.updateInner();
}
//
delete requestedUpdates[ component.id ];
//
component.getUIChildren().forEach( (childUI)=> {
callUpdatesOf( childUI );
});
}