-
Notifications
You must be signed in to change notification settings - Fork 146
/
Copy pathUpdateManager.js
171 lines (112 loc) · 4.1 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
/**
* 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 default class UpdateManager {
/*
* 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.
*/
static requestUpdate( component, updateParsing, updateLayout, updateInner ) {
component.traverse( ( child ) => {
if ( !child.isUI ) return;
// request updates for all descendants of the passed components
if ( !this.requestedUpdates[ child.id ] ) {
this.requestedUpdates[ child.id ] = {
updateParsing,
updateLayout,
updateInner,
needCallback: ( updateParsing || updateLayout || updateInner )
};
} else {
if ( updateParsing ) this.requestedUpdates[ child.id ].updateParsing = true;
if ( updateLayout ) this.requestedUpdates[ child.id ].updateLayout = true;
if ( updateInner ) this.requestedUpdates[ child.id ].updateInner = true;
}
} );
}
/** Register a passed component for later updates */
static register( component ) {
if ( !this.components.includes( component ) ) {
this.components.push( component );
}
}
/** Unregister a component (when it's deleted for instance) */
static disposeOf( component ) {
const idx = this.components.indexOf( component );
if ( idx > -1 ) {
this.components.splice( idx, 1 );
}
}
/** Trigger all requested updates of registered components */
static update() {
if ( Object.keys( this.requestedUpdates ).length > 0 ) {
const roots = this.components.filter( ( component ) => {
return !component.parentUI;
} );
roots.forEach( root => this.traverseParsing( root ) );
roots.forEach( root => this.traverseUpdates( root ) );
}
}
/**
* Calls parseParams update of all components from parent to children
* @private
*/
static traverseParsing( component ) {
const request = this.requestedUpdates[ component.id ];
if ( request && request.updateParsing ) {
component.parseParams();
request.updateParsing = false;
}
component.childrenUIs.forEach( child => this.traverseParsing( child ) );
}
/**
* Calls updateLayout and updateInner functions of components that need an update
* @private
*/
static traverseUpdates( component ) {
const request = this.requestedUpdates[ component.id ];
// instant remove the requested update,
// allowing code below ( especially onAfterUpdate ) to add it without being directly remove
delete this.requestedUpdates[ component.id ];
//
if ( request && request.updateLayout ) {
request.updateLayout = false;
component.updateLayout();
}
//
if ( request && request.updateInner ) {
request.updateInner = false;
component.updateInner();
}
// Update any child
component.childrenUIs.forEach( ( childUI ) => {
this.traverseUpdates( childUI );
} );
// before sending onAfterUpdate
if ( request && request.needCallback ) {
component.onAfterUpdate();
}
}
}
// TODO move these into the class (Webpack unfortunately doesn't understand
// `static` property syntax, despite browsers already supporting this)
UpdateManager.components = [];
UpdateManager.requestedUpdates = {};