-
Notifications
You must be signed in to change notification settings - Fork 94
/
Copy pathReactPixiFiber.js
338 lines (289 loc) · 10.7 KB
/
ReactPixiFiber.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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
import ReactFiberReconciler from "react-reconciler";
import emptyObject from "fbjs/lib/emptyObject";
import invariant from "fbjs/lib/invariant";
import warning from "fbjs/lib/warning";
import now from "performance-now";
import * as PIXI from "pixi.js";
import {
unstable_scheduleCallback as scheduleDeferredCallback,
unstable_cancelCallback as cancelDeferredCallback,
} from "scheduler";
import { createInjectedTypeInstance, isInjectedType } from "./inject";
import { CHILDREN, DEFAULT_PROPS } from "./props";
import { TYPES } from "./types";
import { filterByKey, including, includingReservedProps, not, setPixiValue, unique } from "./utils";
/* Render Methods */
// TODO consider whitelisting props based on component type
export function defaultApplyProps(instance, oldProps, newProps) {
Object.keys(oldProps)
.concat(Object.keys(newProps))
.filter(unique)
.filter(not(includingReservedProps))
.forEach(propName => {
const defaultValue = DEFAULT_PROPS[propName];
const currentValue = instance[propName];
const newValue = newProps[propName];
// Set value if defined
if (typeof newValue !== "undefined") {
setPixiValue(instance, propName, newValue);
}
// Reset to default value (if it is defined) when display object had prop set and no longer has
else if (typeof currentValue !== "undefined" && typeof defaultValue !== "undefined") {
warning(false, `setting default value: ${propName} was ${currentValue} is ${newValue} for %O`, instance);
setPixiValue(instance, propName, defaultValue);
} else {
warning(false, `ignoring prop: ${propName} was ${instance[propName]} is ${newValue} for %O`, instance);
}
});
}
export function applyProps(instance, oldProps, newProps) {
if (typeof instance._customApplyProps === "function") {
instance._customApplyProps(instance, oldProps, newProps);
} else {
defaultApplyProps(instance, oldProps, newProps);
}
}
// Calculate the diff between the two objects.
// See: https://github.com/facebook/react/blob/97e2911/packages/react-dom/src/client/ReactDOMFiberComponent.js#L546
export function diffProps(pixiElement, type, lastRawProps, nextRawProps, rootContainerElement) {
let updatePayload = null;
let lastProps = lastRawProps;
let nextProps = nextRawProps;
let propKey;
for (propKey in lastProps) {
if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] == null) {
continue;
}
if (propKey === CHILDREN) {
// Noop. Text children not supported
} else {
// For all other deleted properties we add it to the queue. We use
// the whitelist in the commit phase instead.
(updatePayload = updatePayload || []).push(propKey, null);
}
}
for (propKey in nextProps) {
const nextProp = nextProps[propKey];
const lastProp = lastProps != null ? lastProps[propKey] : undefined;
if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp || (nextProp == null && lastProp == null)) {
continue;
}
if (propKey === CHILDREN) {
// Noop. Text children not supported
} else {
// For any other property we always add it to the queue and then we
// filter it out using the whitelist during the commit.
(updatePayload = updatePayload || []).push(propKey, nextProp);
}
}
return updatePayload;
}
/* PixiJS Renderer */
export function appendChild(parentInstance, child) {
// TODO do we need to remove the child first if it's already added?
parentInstance.removeChild(child);
parentInstance.addChild(child);
if (typeof child._customDidAttach === "function") {
child._customDidAttach(child);
}
}
export function removeChild(parentInstance, child) {
if (typeof child._customWillDetach === "function") {
child._customWillDetach(child);
}
parentInstance.removeChild(child);
child.destroy({ children: true });
}
export function insertBefore(parentInstance, child, beforeChild) {
invariant(child !== beforeChild, "ReactPixiFiber cannot insert node before itself");
const childExists = parentInstance.children.indexOf(child) !== -1;
if (childExists) {
parentInstance.removeChild(child);
}
const index = parentInstance.getChildIndex(beforeChild);
parentInstance.addChildAt(child, index);
}
export function commitUpdate(instance, updatePayload, type, lastRawProps, nextRawProps, internalInstanceHandle) {
// injected types need to have full control over passed props
if (isInjectedType(type)) {
applyProps(instance, lastRawProps, nextRawProps);
return;
}
// updatePayload is in the form of [propKey1, propValue1, ...]
const updatedPropKeys = including(updatePayload.filter((item, i) => i % 2 === 0));
const oldProps = filterByKey(lastRawProps, updatedPropKeys);
const newProps = filterByKey(nextRawProps, updatedPropKeys);
// regular components only receive props that have changed
applyProps(instance, oldProps, newProps);
}
export function createInstance(type, props, internalInstanceHandle) {
let instance;
switch (type) {
case TYPES.BITMAP_TEXT:
const style =
typeof props.style !== "undefined"
? props.style
: {
align: props.align,
font: props.font,
tint: props.tint,
};
try {
instance = new PIXI.extras.BitmapText(props.text, style);
} catch (e) {
instance = new PIXI.BitmapText(props.text, style);
}
break;
case TYPES.CONTAINER:
instance = new PIXI.Container();
break;
case TYPES.GRAPHICS:
instance = new PIXI.Graphics();
break;
case TYPES.NINE_SLICE_PLANE:
try {
instance = new PIXI.mesh.NineSlicePlane(
props.texture,
props.leftWidth,
props.topHeight,
props.rightWidth,
props.bottomHeight
);
} catch (e) {
instance = new PIXI.NineSlicePlane(
props.texture,
props.leftWidth,
props.topHeight,
props.rightWidth,
props.bottomHeight
);
}
break;
case TYPES.PARTICLE_CONTAINER:
try {
instance = new PIXI.particles.ParticleContainer(
props.maxSize,
props.properties,
props.batchSize,
props.autoResize
);
} catch (e) {
instance = new PIXI.ParticleContainer(props.maxSize, props.properties, props.batchSize, props.autoResize);
}
break;
case TYPES.SPRITE:
instance = new PIXI.Sprite(props.texture);
break;
case TYPES.TEXT:
instance = new PIXI.Text(props.text, props.style, props.canvas);
break;
case TYPES.TILING_SPRITE:
try {
instance = new PIXI.extras.TilingSprite(props.texture, props.width, props.height);
} catch (e) {
instance = new PIXI.TilingSprite(props.texture, props.width, props.height);
}
break;
default:
instance = createInjectedTypeInstance(type, props, internalInstanceHandle, defaultApplyProps);
break;
}
invariant(instance, "ReactPixiFiber does not support the type: `%s`.", type);
applyProps(instance, {}, props);
return instance;
}
export function createTextInstance(text, rootContainerInstance, internalInstanceHandle) {
invariant(false, "ReactPixiFiber does not support text instances. Use Text component instead.");
}
export function finalizeInitialChildren(pixiElement, type, props, rootContainerInstance, hostContext) {
return false;
}
export function getChildHostContext(parentHostContext, type) {
return emptyObject;
}
export function getRootHostContext(rootContainerInstance) {
return emptyObject;
}
export function getPublicInstance(inst) {
return inst;
}
export function prepareForCommit() {
// Noop
}
export function prepareUpdate(pixiElement, type, oldProps, newProps, rootContainerInstance, hostContext) {
return diffProps(pixiElement, type, oldProps, newProps, rootContainerInstance);
}
export function resetAfterCommit() {
// Noop
}
export function resetTextContent(pixiElement) {
// Noop
}
export function shouldDeprioritizeSubtree(type, props) {
const isAlphaVisible = typeof props.alpha === "undefined" || props.alpha > 0;
const isRenderable = typeof props.renderable === "undefined" || props.renderable === true;
const isVisible = typeof props.visible === "undefined" || props.visible === true;
return !(isAlphaVisible && isRenderable && isVisible);
}
export function shouldSetTextContent(type, props) {
return false;
}
export function commitTextUpdate(textInstance, oldText, newText) {
// Noop
}
export function commitMount(instance, type, newProps) {
// Noop
}
export function hideInstance(instance) {
instance.visible = false;
}
export function unhideInstance(instance, props) {
instance.visible = typeof props.visible !== "undefined" ? props.visible : true;
}
export function hideTextInstance(instance) {
// Noop
}
export function unhideTextInstance(instance, props) {
// Noop
}
export const supportsMutation = true;
const hostConfig = {
appendChild: appendChild,
appendChildToContainer: appendChild,
appendInitialChild: appendChild,
cancelPassiveEffects: cancelDeferredCallback,
commitMount: commitMount,
commitTextUpdate: commitTextUpdate,
commitUpdate: commitUpdate,
createInstance: createInstance,
createTextInstance: createTextInstance,
finalizeInitialChildren: finalizeInitialChildren,
getChildHostContext: getChildHostContext,
getRootHostContext: getRootHostContext,
getPublicInstance: getPublicInstance,
hideInstance: hideInstance,
hideTextInstance: hideTextInstance,
insertBefore: insertBefore,
insertInContainerBefore: insertBefore,
now: now,
prepareForCommit: prepareForCommit,
prepareUpdate: prepareUpdate,
removeChild: removeChild,
removeChildFromContainer: removeChild,
resetAfterCommit: resetAfterCommit,
resetTextContent: resetTextContent,
scheduleDeferredCallback: scheduleDeferredCallback,
schedulePassiveEffects: scheduleDeferredCallback,
shouldDeprioritizeSubtree: shouldDeprioritizeSubtree,
shouldSetTextContent: shouldSetTextContent,
supportsMutation: supportsMutation,
unhideInstance: unhideInstance,
unhideTextInstance: unhideTextInstance,
};
// React Pixi Fiber renderer is primary if used without React DOM
export const ReactPixiFiberAsPrimaryRenderer = ReactFiberReconciler({ ...hostConfig, isPrimaryRenderer: true });
// React Pixi Fiber renderer is secondary to React DOM renderer if used together
export const ReactPixiFiberAsSecondaryRenderer = ReactFiberReconciler({ ...hostConfig, isPrimaryRenderer: false });
// If use ReactDOM to render, try use ReactDOM.unstable_batchedUpdates
export const unstable_batchedUpdates = ReactPixiFiberAsPrimaryRenderer.batchedUpdates;
export default ReactPixiFiberAsSecondaryRenderer;