@react-three/fiber 8.13.9 → 8.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/declarations/src/native/polyfills.d.ts +1 -1
- package/native/dist/react-three-fiber-native.cjs.dev.js +210 -152
- package/native/dist/react-three-fiber-native.cjs.prod.js +210 -152
- package/native/dist/react-three-fiber-native.esm.js +210 -150
- package/package.json +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function polyfills():
|
|
1
|
+
export declare function polyfills(): void;
|
|
@@ -9,15 +9,15 @@ var THREE = require('three');
|
|
|
9
9
|
var reactNative = require('react-native');
|
|
10
10
|
var expoGl = require('expo-gl');
|
|
11
11
|
var itsFine = require('its-fine');
|
|
12
|
-
var
|
|
12
|
+
var expoAsset = require('expo-asset');
|
|
13
|
+
var fs = require('expo-file-system');
|
|
14
|
+
var base64Js = require('base64-js');
|
|
13
15
|
require('react-reconciler/constants');
|
|
14
16
|
require('zustand');
|
|
15
17
|
require('react-reconciler');
|
|
16
18
|
require('scheduler');
|
|
17
19
|
require('suspend-react');
|
|
18
20
|
|
|
19
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
20
|
-
|
|
21
21
|
function _interopNamespace(e) {
|
|
22
22
|
if (e && e.__esModule) return e;
|
|
23
23
|
var n = Object.create(null);
|
|
@@ -38,28 +38,7 @@ function _interopNamespace(e) {
|
|
|
38
38
|
|
|
39
39
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
40
40
|
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
41
|
-
var
|
|
42
|
-
|
|
43
|
-
/* eslint-enable import/default, import/no-named-as-default, import/no-named-as-default-member */
|
|
44
|
-
|
|
45
|
-
const EVENTS = {
|
|
46
|
-
PRESS: 'onPress',
|
|
47
|
-
PRESSIN: 'onPressIn',
|
|
48
|
-
PRESSOUT: 'onPressOut',
|
|
49
|
-
LONGPRESS: 'onLongPress',
|
|
50
|
-
HOVERIN: 'onHoverIn',
|
|
51
|
-
HOVEROUT: 'onHoverOut',
|
|
52
|
-
PRESSMOVE: 'onPressMove'
|
|
53
|
-
};
|
|
54
|
-
const DOM_EVENTS = {
|
|
55
|
-
[EVENTS.PRESS]: 'onClick',
|
|
56
|
-
[EVENTS.PRESSIN]: 'onPointerDown',
|
|
57
|
-
[EVENTS.PRESSOUT]: 'onPointerUp',
|
|
58
|
-
[EVENTS.LONGPRESS]: 'onDoubleClick',
|
|
59
|
-
[EVENTS.HOVERIN]: 'onPointerOver',
|
|
60
|
-
[EVENTS.HOVEROUT]: 'onPointerOut',
|
|
61
|
-
[EVENTS.PRESSMOVE]: 'onPointerMove'
|
|
62
|
-
};
|
|
41
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
63
42
|
|
|
64
43
|
/** Default R3F event manager for react-native */
|
|
65
44
|
function createTouchEvents(store) {
|
|
@@ -75,9 +54,26 @@ function createTouchEvents(store) {
|
|
|
75
54
|
event.nativeEvent.offsetY = event.nativeEvent.locationY;
|
|
76
55
|
|
|
77
56
|
// Emulate DOM event
|
|
78
|
-
const callback = handlePointer(
|
|
79
|
-
|
|
57
|
+
const callback = handlePointer(name);
|
|
58
|
+
callback(event.nativeEvent);
|
|
59
|
+
return true;
|
|
80
60
|
};
|
|
61
|
+
const responder = reactNative.PanResponder.create({
|
|
62
|
+
onStartShouldSetPanResponder: () => true,
|
|
63
|
+
onMoveShouldSetPanResponder: () => true,
|
|
64
|
+
onMoveShouldSetPanResponderCapture: () => true,
|
|
65
|
+
onPanResponderTerminationRequest: () => true,
|
|
66
|
+
onStartShouldSetPanResponderCapture: e => handleTouch(e, 'onPointerCapture'),
|
|
67
|
+
onPanResponderStart: e => handleTouch(e, 'onPointerDown'),
|
|
68
|
+
onPanResponderMove: e => handleTouch(e, 'onPointerMove'),
|
|
69
|
+
onPanResponderEnd: (e, state) => {
|
|
70
|
+
handleTouch(e, 'onPointerUp');
|
|
71
|
+
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'onClick');
|
|
72
|
+
},
|
|
73
|
+
onPanResponderRelease: e => handleTouch(e, 'onPointerLeave'),
|
|
74
|
+
onPanResponderTerminate: e => handleTouch(e, 'onLostPointerCapture'),
|
|
75
|
+
onPanResponderReject: e => handleTouch(e, 'onLostPointerCapture')
|
|
76
|
+
});
|
|
81
77
|
return {
|
|
82
78
|
priority: 1,
|
|
83
79
|
enabled: true,
|
|
@@ -88,17 +84,16 @@ function createTouchEvents(store) {
|
|
|
88
84
|
state.raycaster.setFromCamera(state.pointer, state.camera);
|
|
89
85
|
},
|
|
90
86
|
connected: undefined,
|
|
91
|
-
handlers:
|
|
92
|
-
...acc,
|
|
93
|
-
[name]: event => handleTouch(event, name)
|
|
94
|
-
}), {}),
|
|
87
|
+
handlers: responder.panHandlers,
|
|
95
88
|
update: () => {
|
|
96
89
|
var _internal$lastEvent;
|
|
97
90
|
const {
|
|
98
91
|
events,
|
|
99
92
|
internal
|
|
100
93
|
} = store.getState();
|
|
101
|
-
if ((_internal$lastEvent = internal.lastEvent) != null && _internal$lastEvent.current && events.handlers)
|
|
94
|
+
if ((_internal$lastEvent = internal.lastEvent) != null && _internal$lastEvent.current && events.handlers) {
|
|
95
|
+
handlePointer('onPointerMove')(internal.lastEvent.current);
|
|
96
|
+
}
|
|
102
97
|
},
|
|
103
98
|
connect: () => {
|
|
104
99
|
const {
|
|
@@ -106,131 +101,24 @@ function createTouchEvents(store) {
|
|
|
106
101
|
events
|
|
107
102
|
} = store.getState();
|
|
108
103
|
events.disconnect == null ? void 0 : events.disconnect();
|
|
109
|
-
const connected = new Pressability__default["default"](events.handlers);
|
|
110
104
|
set(state => ({
|
|
111
105
|
events: {
|
|
112
106
|
...state.events,
|
|
113
|
-
connected
|
|
107
|
+
connected: true
|
|
114
108
|
}
|
|
115
109
|
}));
|
|
116
|
-
const handlers = connected.getEventHandlers();
|
|
117
|
-
return handlers;
|
|
118
110
|
},
|
|
119
111
|
disconnect: () => {
|
|
120
112
|
const {
|
|
121
|
-
set
|
|
122
|
-
events
|
|
113
|
+
set
|
|
123
114
|
} = store.getState();
|
|
124
|
-
|
|
125
|
-
events
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
...state.events,
|
|
129
|
-
connected: undefined
|
|
130
|
-
}
|
|
131
|
-
}));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Check if expo-asset is installed (available with expo modules)
|
|
138
|
-
let expAsset;
|
|
139
|
-
try {
|
|
140
|
-
var _require;
|
|
141
|
-
expAsset = (_require = require('expo-asset')) == null ? void 0 : _require.Asset;
|
|
142
|
-
} catch (_) {}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Generates an asset based on input type.
|
|
146
|
-
*/
|
|
147
|
-
function getAsset(input) {
|
|
148
|
-
switch (typeof input) {
|
|
149
|
-
case 'string':
|
|
150
|
-
return expAsset.fromURI(input);
|
|
151
|
-
case 'number':
|
|
152
|
-
return expAsset.fromModule(input);
|
|
153
|
-
default:
|
|
154
|
-
throw new Error('R3F: Invalid asset! Must be a URI or module.');
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
let injected = false;
|
|
158
|
-
function polyfills() {
|
|
159
|
-
if (!expAsset || injected) return;
|
|
160
|
-
injected = true;
|
|
161
|
-
|
|
162
|
-
// Don't pre-process urls, let expo-asset generate an absolute URL
|
|
163
|
-
const extractUrlBase = THREE__namespace.LoaderUtils.extractUrlBase.bind(THREE__namespace.LoaderUtils);
|
|
164
|
-
THREE__namespace.LoaderUtils.extractUrlBase = url => typeof url === 'string' ? extractUrlBase(url) : './';
|
|
165
|
-
|
|
166
|
-
// There's no Image in native, so create a data texture instead
|
|
167
|
-
const prevTextureLoad = THREE__namespace.TextureLoader.prototype.load;
|
|
168
|
-
THREE__namespace.TextureLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
|
|
169
|
-
if (this.path) url = this.path + url;
|
|
170
|
-
const texture = new THREE__namespace.Texture();
|
|
171
|
-
|
|
172
|
-
// @ts-ignore
|
|
173
|
-
texture.isDataTexture = true;
|
|
174
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
175
|
-
texture.image = {
|
|
176
|
-
data: asset,
|
|
177
|
-
width: asset.width,
|
|
178
|
-
height: asset.height
|
|
179
|
-
};
|
|
180
|
-
texture.flipY = true;
|
|
181
|
-
texture.unpackAlignment = 1;
|
|
182
|
-
texture.needsUpdate = true;
|
|
183
|
-
onLoad == null ? void 0 : onLoad(texture);
|
|
184
|
-
}).catch(onError);
|
|
185
|
-
return texture;
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
// Fetches assets via XMLHttpRequest
|
|
189
|
-
const prevFileLoad = THREE__namespace.FileLoader.prototype.load;
|
|
190
|
-
THREE__namespace.FileLoader.prototype.load = function (url, onLoad, onProgress, onError) {
|
|
191
|
-
if (this.path) url = this.path + url;
|
|
192
|
-
const request = new XMLHttpRequest();
|
|
193
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
194
|
-
request.open('GET', asset.uri, true);
|
|
195
|
-
request.addEventListener('load', event => {
|
|
196
|
-
if (request.status === 200) {
|
|
197
|
-
onLoad == null ? void 0 : onLoad(request.response);
|
|
198
|
-
this.manager.itemEnd(url);
|
|
199
|
-
} else {
|
|
200
|
-
onError == null ? void 0 : onError(event);
|
|
201
|
-
this.manager.itemError(url);
|
|
202
|
-
this.manager.itemEnd(url);
|
|
115
|
+
set(state => ({
|
|
116
|
+
events: {
|
|
117
|
+
...state.events,
|
|
118
|
+
connected: false
|
|
203
119
|
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
onProgress == null ? void 0 : onProgress(event);
|
|
207
|
-
}, false);
|
|
208
|
-
request.addEventListener('error', event => {
|
|
209
|
-
onError == null ? void 0 : onError(event);
|
|
210
|
-
this.manager.itemError(url);
|
|
211
|
-
this.manager.itemEnd(url);
|
|
212
|
-
}, false);
|
|
213
|
-
request.addEventListener('abort', event => {
|
|
214
|
-
onError == null ? void 0 : onError(event);
|
|
215
|
-
this.manager.itemError(url);
|
|
216
|
-
this.manager.itemEnd(url);
|
|
217
|
-
}, false);
|
|
218
|
-
if (this.responseType) request.responseType = this.responseType;
|
|
219
|
-
if (this.withCredentials) request.withCredentials = this.withCredentials;
|
|
220
|
-
for (const header in this.requestHeader) {
|
|
221
|
-
request.setRequestHeader(header, this.requestHeader[header]);
|
|
222
|
-
}
|
|
223
|
-
request.send(null);
|
|
224
|
-
this.manager.itemStart(url);
|
|
225
|
-
});
|
|
226
|
-
return request;
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
// Cleanup function
|
|
230
|
-
return () => {
|
|
231
|
-
THREE__namespace.LoaderUtils.extractUrlBase = extractUrlBase;
|
|
232
|
-
THREE__namespace.TextureLoader.prototype.load = prevTextureLoad;
|
|
233
|
-
THREE__namespace.FileLoader.prototype.load = prevFileLoad;
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
234
122
|
};
|
|
235
123
|
}
|
|
236
124
|
|
|
@@ -286,9 +174,6 @@ const CanvasImpl = /*#__PURE__*/React__namespace.forwardRef(({
|
|
|
286
174
|
if (error) throw error;
|
|
287
175
|
const viewRef = React__namespace.useRef(null);
|
|
288
176
|
const root = React__namespace.useRef(null);
|
|
289
|
-
|
|
290
|
-
// Inject and cleanup RN polyfills if able
|
|
291
|
-
React__namespace.useLayoutEffect(() => polyfills(), []);
|
|
292
177
|
const onLayout = React__namespace.useCallback(e => {
|
|
293
178
|
const {
|
|
294
179
|
width,
|
|
@@ -347,8 +232,7 @@ const CanvasImpl = /*#__PURE__*/React__namespace.forwardRef(({
|
|
|
347
232
|
// Overwrite onCreated to apply RN bindings
|
|
348
233
|
onCreated: state => {
|
|
349
234
|
// Bind events after creation
|
|
350
|
-
|
|
351
|
-
setBind(handlers);
|
|
235
|
+
setBind(state.events.handlers);
|
|
352
236
|
|
|
353
237
|
// Bind render to RN bridge
|
|
354
238
|
const context = state.gl.getContext();
|
|
@@ -396,6 +280,180 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(function CanvasWrapper(p
|
|
|
396
280
|
})));
|
|
397
281
|
});
|
|
398
282
|
|
|
283
|
+
function polyfills() {
|
|
284
|
+
// Patch Blob for ArrayBuffer if unsupported
|
|
285
|
+
try {
|
|
286
|
+
new Blob([new ArrayBuffer(4)]);
|
|
287
|
+
} catch (_) {
|
|
288
|
+
global.Blob = class extends Blob {
|
|
289
|
+
constructor(parts, options) {
|
|
290
|
+
super(parts == null ? void 0 : parts.map(part => {
|
|
291
|
+
if (part instanceof ArrayBuffer || ArrayBuffer.isView(part)) {
|
|
292
|
+
part = base64Js.fromByteArray(new Uint8Array(part));
|
|
293
|
+
}
|
|
294
|
+
return part;
|
|
295
|
+
}), options);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function uuidv4() {
|
|
300
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
|
301
|
+
const r = Math.random() * 16 | 0,
|
|
302
|
+
v = c == 'x' ? r : r & 0x3 | 0x8;
|
|
303
|
+
return v.toString(16);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
async function getAsset(input) {
|
|
307
|
+
if (typeof input === 'string') {
|
|
308
|
+
// Point to storage if preceded with fs path
|
|
309
|
+
if (input.startsWith('file:')) return {
|
|
310
|
+
localUri: input
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// Unpack Blobs from react-native BlobManager
|
|
314
|
+
if (input.startsWith('blob:')) {
|
|
315
|
+
const blob = await new Promise((res, rej) => {
|
|
316
|
+
const xhr = new XMLHttpRequest();
|
|
317
|
+
xhr.open('GET', input);
|
|
318
|
+
xhr.responseType = 'blob';
|
|
319
|
+
xhr.onload = () => res(xhr.response);
|
|
320
|
+
xhr.onerror = rej;
|
|
321
|
+
xhr.send();
|
|
322
|
+
});
|
|
323
|
+
const data = await new Promise((res, rej) => {
|
|
324
|
+
const reader = new FileReader();
|
|
325
|
+
reader.onload = () => res(reader.result);
|
|
326
|
+
reader.onerror = rej;
|
|
327
|
+
reader.readAsText(blob);
|
|
328
|
+
});
|
|
329
|
+
input = `data:${blob.type};base64,${data}`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Create safe URI for JSI
|
|
333
|
+
if (input.startsWith('data:')) {
|
|
334
|
+
const [header, data] = input.split(',');
|
|
335
|
+
const [, type] = header.split('/');
|
|
336
|
+
const localUri = fs__namespace.cacheDirectory + uuidv4() + `.${type}`;
|
|
337
|
+
await fs__namespace.writeAsStringAsync(localUri, data, {
|
|
338
|
+
encoding: fs__namespace.EncodingType.Base64
|
|
339
|
+
});
|
|
340
|
+
return {
|
|
341
|
+
localUri
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Download bundler module or external URL
|
|
347
|
+
const asset = expoAsset.Asset.fromModule(input);
|
|
348
|
+
|
|
349
|
+
// Unpack assets in Android Release Mode
|
|
350
|
+
if (!asset.uri.includes(':')) {
|
|
351
|
+
const localUri = `${fs__namespace.cacheDirectory}ExponentAsset-${asset.hash}.${asset.type}`;
|
|
352
|
+
await fs__namespace.copyAsync({
|
|
353
|
+
from: asset.uri,
|
|
354
|
+
to: localUri
|
|
355
|
+
});
|
|
356
|
+
return {
|
|
357
|
+
localUri
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Otherwise, resolve from registry
|
|
362
|
+
return asset.downloadAsync();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Don't pre-process urls, let expo-asset generate an absolute URL
|
|
366
|
+
const extractUrlBase = THREE__namespace.LoaderUtils.extractUrlBase.bind(THREE__namespace.LoaderUtils);
|
|
367
|
+
THREE__namespace.LoaderUtils.extractUrlBase = url => typeof url === 'string' ? extractUrlBase(url) : './';
|
|
368
|
+
|
|
369
|
+
// There's no Image in native, so create a data texture instead
|
|
370
|
+
THREE__namespace.TextureLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
|
|
371
|
+
if (this.path) url = this.path + url;
|
|
372
|
+
const texture = new THREE__namespace.Texture();
|
|
373
|
+
getAsset(url).then(async asset => {
|
|
374
|
+
const uri = asset.localUri || asset.uri;
|
|
375
|
+
if (!asset.width || !asset.height) {
|
|
376
|
+
const {
|
|
377
|
+
width,
|
|
378
|
+
height
|
|
379
|
+
} = await new Promise((res, rej) => reactNative.Image.getSize(uri, (width, height) => res({
|
|
380
|
+
width,
|
|
381
|
+
height
|
|
382
|
+
}), rej));
|
|
383
|
+
asset.width = width;
|
|
384
|
+
asset.height = height;
|
|
385
|
+
}
|
|
386
|
+
texture.image = {
|
|
387
|
+
data: {
|
|
388
|
+
localUri: uri
|
|
389
|
+
},
|
|
390
|
+
width: asset.width,
|
|
391
|
+
height: asset.height
|
|
392
|
+
};
|
|
393
|
+
texture.flipY = true;
|
|
394
|
+
texture.unpackAlignment = 1;
|
|
395
|
+
texture.needsUpdate = true;
|
|
396
|
+
|
|
397
|
+
// Force non-DOM upload for EXGL fast paths
|
|
398
|
+
// @ts-ignore
|
|
399
|
+
texture.isDataTexture = true;
|
|
400
|
+
onLoad == null ? void 0 : onLoad(texture);
|
|
401
|
+
}).catch(onError);
|
|
402
|
+
return texture;
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
// Fetches assets via XMLHttpRequest
|
|
406
|
+
THREE__namespace.FileLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
|
|
407
|
+
if (this.path) url = this.path + url;
|
|
408
|
+
const request = new XMLHttpRequest();
|
|
409
|
+
getAsset(url).then(async asset => {
|
|
410
|
+
let uri = asset.localUri || asset.uri;
|
|
411
|
+
|
|
412
|
+
// Make FS paths web-safe
|
|
413
|
+
if (asset.uri.startsWith('file://')) {
|
|
414
|
+
const data = await fs__namespace.readAsStringAsync(asset.uri, {
|
|
415
|
+
encoding: fs__namespace.EncodingType.Base64
|
|
416
|
+
});
|
|
417
|
+
uri = `data:application/octet-stream;base64,${data}`;
|
|
418
|
+
}
|
|
419
|
+
request.open('GET', uri, true);
|
|
420
|
+
request.addEventListener('load', event => {
|
|
421
|
+
if (request.status === 200) {
|
|
422
|
+
onLoad == null ? void 0 : onLoad(request.response);
|
|
423
|
+
this.manager.itemEnd(url);
|
|
424
|
+
} else {
|
|
425
|
+
onError == null ? void 0 : onError(event);
|
|
426
|
+
this.manager.itemError(url);
|
|
427
|
+
this.manager.itemEnd(url);
|
|
428
|
+
}
|
|
429
|
+
}, false);
|
|
430
|
+
request.addEventListener('progress', event => {
|
|
431
|
+
onProgress == null ? void 0 : onProgress(event);
|
|
432
|
+
}, false);
|
|
433
|
+
request.addEventListener('error', event => {
|
|
434
|
+
onError == null ? void 0 : onError(event);
|
|
435
|
+
this.manager.itemError(url);
|
|
436
|
+
this.manager.itemEnd(url);
|
|
437
|
+
}, false);
|
|
438
|
+
request.addEventListener('abort', event => {
|
|
439
|
+
onError == null ? void 0 : onError(event);
|
|
440
|
+
this.manager.itemError(url);
|
|
441
|
+
this.manager.itemEnd(url);
|
|
442
|
+
}, false);
|
|
443
|
+
if (this.responseType) request.responseType = this.responseType;
|
|
444
|
+
if (this.withCredentials) request.withCredentials = this.withCredentials;
|
|
445
|
+
for (const header in this.requestHeader) {
|
|
446
|
+
request.setRequestHeader(header, this.requestHeader[header]);
|
|
447
|
+
}
|
|
448
|
+
request.send(null);
|
|
449
|
+
this.manager.itemStart(url);
|
|
450
|
+
}).catch(onError);
|
|
451
|
+
return request;
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
polyfills();
|
|
456
|
+
|
|
399
457
|
exports.ReactThreeFiber = index.threeTypes;
|
|
400
458
|
exports._roots = index.roots;
|
|
401
459
|
exports.act = index.act;
|
|
@@ -9,15 +9,15 @@ var THREE = require('three');
|
|
|
9
9
|
var reactNative = require('react-native');
|
|
10
10
|
var expoGl = require('expo-gl');
|
|
11
11
|
var itsFine = require('its-fine');
|
|
12
|
-
var
|
|
12
|
+
var expoAsset = require('expo-asset');
|
|
13
|
+
var fs = require('expo-file-system');
|
|
14
|
+
var base64Js = require('base64-js');
|
|
13
15
|
require('react-reconciler/constants');
|
|
14
16
|
require('zustand');
|
|
15
17
|
require('react-reconciler');
|
|
16
18
|
require('scheduler');
|
|
17
19
|
require('suspend-react');
|
|
18
20
|
|
|
19
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
20
|
-
|
|
21
21
|
function _interopNamespace(e) {
|
|
22
22
|
if (e && e.__esModule) return e;
|
|
23
23
|
var n = Object.create(null);
|
|
@@ -38,28 +38,7 @@ function _interopNamespace(e) {
|
|
|
38
38
|
|
|
39
39
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
40
40
|
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
41
|
-
var
|
|
42
|
-
|
|
43
|
-
/* eslint-enable import/default, import/no-named-as-default, import/no-named-as-default-member */
|
|
44
|
-
|
|
45
|
-
const EVENTS = {
|
|
46
|
-
PRESS: 'onPress',
|
|
47
|
-
PRESSIN: 'onPressIn',
|
|
48
|
-
PRESSOUT: 'onPressOut',
|
|
49
|
-
LONGPRESS: 'onLongPress',
|
|
50
|
-
HOVERIN: 'onHoverIn',
|
|
51
|
-
HOVEROUT: 'onHoverOut',
|
|
52
|
-
PRESSMOVE: 'onPressMove'
|
|
53
|
-
};
|
|
54
|
-
const DOM_EVENTS = {
|
|
55
|
-
[EVENTS.PRESS]: 'onClick',
|
|
56
|
-
[EVENTS.PRESSIN]: 'onPointerDown',
|
|
57
|
-
[EVENTS.PRESSOUT]: 'onPointerUp',
|
|
58
|
-
[EVENTS.LONGPRESS]: 'onDoubleClick',
|
|
59
|
-
[EVENTS.HOVERIN]: 'onPointerOver',
|
|
60
|
-
[EVENTS.HOVEROUT]: 'onPointerOut',
|
|
61
|
-
[EVENTS.PRESSMOVE]: 'onPointerMove'
|
|
62
|
-
};
|
|
41
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
63
42
|
|
|
64
43
|
/** Default R3F event manager for react-native */
|
|
65
44
|
function createTouchEvents(store) {
|
|
@@ -75,9 +54,26 @@ function createTouchEvents(store) {
|
|
|
75
54
|
event.nativeEvent.offsetY = event.nativeEvent.locationY;
|
|
76
55
|
|
|
77
56
|
// Emulate DOM event
|
|
78
|
-
const callback = handlePointer(
|
|
79
|
-
|
|
57
|
+
const callback = handlePointer(name);
|
|
58
|
+
callback(event.nativeEvent);
|
|
59
|
+
return true;
|
|
80
60
|
};
|
|
61
|
+
const responder = reactNative.PanResponder.create({
|
|
62
|
+
onStartShouldSetPanResponder: () => true,
|
|
63
|
+
onMoveShouldSetPanResponder: () => true,
|
|
64
|
+
onMoveShouldSetPanResponderCapture: () => true,
|
|
65
|
+
onPanResponderTerminationRequest: () => true,
|
|
66
|
+
onStartShouldSetPanResponderCapture: e => handleTouch(e, 'onPointerCapture'),
|
|
67
|
+
onPanResponderStart: e => handleTouch(e, 'onPointerDown'),
|
|
68
|
+
onPanResponderMove: e => handleTouch(e, 'onPointerMove'),
|
|
69
|
+
onPanResponderEnd: (e, state) => {
|
|
70
|
+
handleTouch(e, 'onPointerUp');
|
|
71
|
+
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'onClick');
|
|
72
|
+
},
|
|
73
|
+
onPanResponderRelease: e => handleTouch(e, 'onPointerLeave'),
|
|
74
|
+
onPanResponderTerminate: e => handleTouch(e, 'onLostPointerCapture'),
|
|
75
|
+
onPanResponderReject: e => handleTouch(e, 'onLostPointerCapture')
|
|
76
|
+
});
|
|
81
77
|
return {
|
|
82
78
|
priority: 1,
|
|
83
79
|
enabled: true,
|
|
@@ -88,17 +84,16 @@ function createTouchEvents(store) {
|
|
|
88
84
|
state.raycaster.setFromCamera(state.pointer, state.camera);
|
|
89
85
|
},
|
|
90
86
|
connected: undefined,
|
|
91
|
-
handlers:
|
|
92
|
-
...acc,
|
|
93
|
-
[name]: event => handleTouch(event, name)
|
|
94
|
-
}), {}),
|
|
87
|
+
handlers: responder.panHandlers,
|
|
95
88
|
update: () => {
|
|
96
89
|
var _internal$lastEvent;
|
|
97
90
|
const {
|
|
98
91
|
events,
|
|
99
92
|
internal
|
|
100
93
|
} = store.getState();
|
|
101
|
-
if ((_internal$lastEvent = internal.lastEvent) != null && _internal$lastEvent.current && events.handlers)
|
|
94
|
+
if ((_internal$lastEvent = internal.lastEvent) != null && _internal$lastEvent.current && events.handlers) {
|
|
95
|
+
handlePointer('onPointerMove')(internal.lastEvent.current);
|
|
96
|
+
}
|
|
102
97
|
},
|
|
103
98
|
connect: () => {
|
|
104
99
|
const {
|
|
@@ -106,131 +101,24 @@ function createTouchEvents(store) {
|
|
|
106
101
|
events
|
|
107
102
|
} = store.getState();
|
|
108
103
|
events.disconnect == null ? void 0 : events.disconnect();
|
|
109
|
-
const connected = new Pressability__default["default"](events.handlers);
|
|
110
104
|
set(state => ({
|
|
111
105
|
events: {
|
|
112
106
|
...state.events,
|
|
113
|
-
connected
|
|
107
|
+
connected: true
|
|
114
108
|
}
|
|
115
109
|
}));
|
|
116
|
-
const handlers = connected.getEventHandlers();
|
|
117
|
-
return handlers;
|
|
118
110
|
},
|
|
119
111
|
disconnect: () => {
|
|
120
112
|
const {
|
|
121
|
-
set
|
|
122
|
-
events
|
|
113
|
+
set
|
|
123
114
|
} = store.getState();
|
|
124
|
-
|
|
125
|
-
events
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
...state.events,
|
|
129
|
-
connected: undefined
|
|
130
|
-
}
|
|
131
|
-
}));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Check if expo-asset is installed (available with expo modules)
|
|
138
|
-
let expAsset;
|
|
139
|
-
try {
|
|
140
|
-
var _require;
|
|
141
|
-
expAsset = (_require = require('expo-asset')) == null ? void 0 : _require.Asset;
|
|
142
|
-
} catch (_) {}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Generates an asset based on input type.
|
|
146
|
-
*/
|
|
147
|
-
function getAsset(input) {
|
|
148
|
-
switch (typeof input) {
|
|
149
|
-
case 'string':
|
|
150
|
-
return expAsset.fromURI(input);
|
|
151
|
-
case 'number':
|
|
152
|
-
return expAsset.fromModule(input);
|
|
153
|
-
default:
|
|
154
|
-
throw new Error('R3F: Invalid asset! Must be a URI or module.');
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
let injected = false;
|
|
158
|
-
function polyfills() {
|
|
159
|
-
if (!expAsset || injected) return;
|
|
160
|
-
injected = true;
|
|
161
|
-
|
|
162
|
-
// Don't pre-process urls, let expo-asset generate an absolute URL
|
|
163
|
-
const extractUrlBase = THREE__namespace.LoaderUtils.extractUrlBase.bind(THREE__namespace.LoaderUtils);
|
|
164
|
-
THREE__namespace.LoaderUtils.extractUrlBase = url => typeof url === 'string' ? extractUrlBase(url) : './';
|
|
165
|
-
|
|
166
|
-
// There's no Image in native, so create a data texture instead
|
|
167
|
-
const prevTextureLoad = THREE__namespace.TextureLoader.prototype.load;
|
|
168
|
-
THREE__namespace.TextureLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
|
|
169
|
-
if (this.path) url = this.path + url;
|
|
170
|
-
const texture = new THREE__namespace.Texture();
|
|
171
|
-
|
|
172
|
-
// @ts-ignore
|
|
173
|
-
texture.isDataTexture = true;
|
|
174
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
175
|
-
texture.image = {
|
|
176
|
-
data: asset,
|
|
177
|
-
width: asset.width,
|
|
178
|
-
height: asset.height
|
|
179
|
-
};
|
|
180
|
-
texture.flipY = true;
|
|
181
|
-
texture.unpackAlignment = 1;
|
|
182
|
-
texture.needsUpdate = true;
|
|
183
|
-
onLoad == null ? void 0 : onLoad(texture);
|
|
184
|
-
}).catch(onError);
|
|
185
|
-
return texture;
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
// Fetches assets via XMLHttpRequest
|
|
189
|
-
const prevFileLoad = THREE__namespace.FileLoader.prototype.load;
|
|
190
|
-
THREE__namespace.FileLoader.prototype.load = function (url, onLoad, onProgress, onError) {
|
|
191
|
-
if (this.path) url = this.path + url;
|
|
192
|
-
const request = new XMLHttpRequest();
|
|
193
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
194
|
-
request.open('GET', asset.uri, true);
|
|
195
|
-
request.addEventListener('load', event => {
|
|
196
|
-
if (request.status === 200) {
|
|
197
|
-
onLoad == null ? void 0 : onLoad(request.response);
|
|
198
|
-
this.manager.itemEnd(url);
|
|
199
|
-
} else {
|
|
200
|
-
onError == null ? void 0 : onError(event);
|
|
201
|
-
this.manager.itemError(url);
|
|
202
|
-
this.manager.itemEnd(url);
|
|
115
|
+
set(state => ({
|
|
116
|
+
events: {
|
|
117
|
+
...state.events,
|
|
118
|
+
connected: false
|
|
203
119
|
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
onProgress == null ? void 0 : onProgress(event);
|
|
207
|
-
}, false);
|
|
208
|
-
request.addEventListener('error', event => {
|
|
209
|
-
onError == null ? void 0 : onError(event);
|
|
210
|
-
this.manager.itemError(url);
|
|
211
|
-
this.manager.itemEnd(url);
|
|
212
|
-
}, false);
|
|
213
|
-
request.addEventListener('abort', event => {
|
|
214
|
-
onError == null ? void 0 : onError(event);
|
|
215
|
-
this.manager.itemError(url);
|
|
216
|
-
this.manager.itemEnd(url);
|
|
217
|
-
}, false);
|
|
218
|
-
if (this.responseType) request.responseType = this.responseType;
|
|
219
|
-
if (this.withCredentials) request.withCredentials = this.withCredentials;
|
|
220
|
-
for (const header in this.requestHeader) {
|
|
221
|
-
request.setRequestHeader(header, this.requestHeader[header]);
|
|
222
|
-
}
|
|
223
|
-
request.send(null);
|
|
224
|
-
this.manager.itemStart(url);
|
|
225
|
-
});
|
|
226
|
-
return request;
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
// Cleanup function
|
|
230
|
-
return () => {
|
|
231
|
-
THREE__namespace.LoaderUtils.extractUrlBase = extractUrlBase;
|
|
232
|
-
THREE__namespace.TextureLoader.prototype.load = prevTextureLoad;
|
|
233
|
-
THREE__namespace.FileLoader.prototype.load = prevFileLoad;
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
234
122
|
};
|
|
235
123
|
}
|
|
236
124
|
|
|
@@ -286,9 +174,6 @@ const CanvasImpl = /*#__PURE__*/React__namespace.forwardRef(({
|
|
|
286
174
|
if (error) throw error;
|
|
287
175
|
const viewRef = React__namespace.useRef(null);
|
|
288
176
|
const root = React__namespace.useRef(null);
|
|
289
|
-
|
|
290
|
-
// Inject and cleanup RN polyfills if able
|
|
291
|
-
React__namespace.useLayoutEffect(() => polyfills(), []);
|
|
292
177
|
const onLayout = React__namespace.useCallback(e => {
|
|
293
178
|
const {
|
|
294
179
|
width,
|
|
@@ -347,8 +232,7 @@ const CanvasImpl = /*#__PURE__*/React__namespace.forwardRef(({
|
|
|
347
232
|
// Overwrite onCreated to apply RN bindings
|
|
348
233
|
onCreated: state => {
|
|
349
234
|
// Bind events after creation
|
|
350
|
-
|
|
351
|
-
setBind(handlers);
|
|
235
|
+
setBind(state.events.handlers);
|
|
352
236
|
|
|
353
237
|
// Bind render to RN bridge
|
|
354
238
|
const context = state.gl.getContext();
|
|
@@ -396,6 +280,180 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(function CanvasWrapper(p
|
|
|
396
280
|
})));
|
|
397
281
|
});
|
|
398
282
|
|
|
283
|
+
function polyfills() {
|
|
284
|
+
// Patch Blob for ArrayBuffer if unsupported
|
|
285
|
+
try {
|
|
286
|
+
new Blob([new ArrayBuffer(4)]);
|
|
287
|
+
} catch (_) {
|
|
288
|
+
global.Blob = class extends Blob {
|
|
289
|
+
constructor(parts, options) {
|
|
290
|
+
super(parts == null ? void 0 : parts.map(part => {
|
|
291
|
+
if (part instanceof ArrayBuffer || ArrayBuffer.isView(part)) {
|
|
292
|
+
part = base64Js.fromByteArray(new Uint8Array(part));
|
|
293
|
+
}
|
|
294
|
+
return part;
|
|
295
|
+
}), options);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function uuidv4() {
|
|
300
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
|
301
|
+
const r = Math.random() * 16 | 0,
|
|
302
|
+
v = c == 'x' ? r : r & 0x3 | 0x8;
|
|
303
|
+
return v.toString(16);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
async function getAsset(input) {
|
|
307
|
+
if (typeof input === 'string') {
|
|
308
|
+
// Point to storage if preceded with fs path
|
|
309
|
+
if (input.startsWith('file:')) return {
|
|
310
|
+
localUri: input
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// Unpack Blobs from react-native BlobManager
|
|
314
|
+
if (input.startsWith('blob:')) {
|
|
315
|
+
const blob = await new Promise((res, rej) => {
|
|
316
|
+
const xhr = new XMLHttpRequest();
|
|
317
|
+
xhr.open('GET', input);
|
|
318
|
+
xhr.responseType = 'blob';
|
|
319
|
+
xhr.onload = () => res(xhr.response);
|
|
320
|
+
xhr.onerror = rej;
|
|
321
|
+
xhr.send();
|
|
322
|
+
});
|
|
323
|
+
const data = await new Promise((res, rej) => {
|
|
324
|
+
const reader = new FileReader();
|
|
325
|
+
reader.onload = () => res(reader.result);
|
|
326
|
+
reader.onerror = rej;
|
|
327
|
+
reader.readAsText(blob);
|
|
328
|
+
});
|
|
329
|
+
input = `data:${blob.type};base64,${data}`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Create safe URI for JSI
|
|
333
|
+
if (input.startsWith('data:')) {
|
|
334
|
+
const [header, data] = input.split(',');
|
|
335
|
+
const [, type] = header.split('/');
|
|
336
|
+
const localUri = fs__namespace.cacheDirectory + uuidv4() + `.${type}`;
|
|
337
|
+
await fs__namespace.writeAsStringAsync(localUri, data, {
|
|
338
|
+
encoding: fs__namespace.EncodingType.Base64
|
|
339
|
+
});
|
|
340
|
+
return {
|
|
341
|
+
localUri
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Download bundler module or external URL
|
|
347
|
+
const asset = expoAsset.Asset.fromModule(input);
|
|
348
|
+
|
|
349
|
+
// Unpack assets in Android Release Mode
|
|
350
|
+
if (!asset.uri.includes(':')) {
|
|
351
|
+
const localUri = `${fs__namespace.cacheDirectory}ExponentAsset-${asset.hash}.${asset.type}`;
|
|
352
|
+
await fs__namespace.copyAsync({
|
|
353
|
+
from: asset.uri,
|
|
354
|
+
to: localUri
|
|
355
|
+
});
|
|
356
|
+
return {
|
|
357
|
+
localUri
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Otherwise, resolve from registry
|
|
362
|
+
return asset.downloadAsync();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Don't pre-process urls, let expo-asset generate an absolute URL
|
|
366
|
+
const extractUrlBase = THREE__namespace.LoaderUtils.extractUrlBase.bind(THREE__namespace.LoaderUtils);
|
|
367
|
+
THREE__namespace.LoaderUtils.extractUrlBase = url => typeof url === 'string' ? extractUrlBase(url) : './';
|
|
368
|
+
|
|
369
|
+
// There's no Image in native, so create a data texture instead
|
|
370
|
+
THREE__namespace.TextureLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
|
|
371
|
+
if (this.path) url = this.path + url;
|
|
372
|
+
const texture = new THREE__namespace.Texture();
|
|
373
|
+
getAsset(url).then(async asset => {
|
|
374
|
+
const uri = asset.localUri || asset.uri;
|
|
375
|
+
if (!asset.width || !asset.height) {
|
|
376
|
+
const {
|
|
377
|
+
width,
|
|
378
|
+
height
|
|
379
|
+
} = await new Promise((res, rej) => reactNative.Image.getSize(uri, (width, height) => res({
|
|
380
|
+
width,
|
|
381
|
+
height
|
|
382
|
+
}), rej));
|
|
383
|
+
asset.width = width;
|
|
384
|
+
asset.height = height;
|
|
385
|
+
}
|
|
386
|
+
texture.image = {
|
|
387
|
+
data: {
|
|
388
|
+
localUri: uri
|
|
389
|
+
},
|
|
390
|
+
width: asset.width,
|
|
391
|
+
height: asset.height
|
|
392
|
+
};
|
|
393
|
+
texture.flipY = true;
|
|
394
|
+
texture.unpackAlignment = 1;
|
|
395
|
+
texture.needsUpdate = true;
|
|
396
|
+
|
|
397
|
+
// Force non-DOM upload for EXGL fast paths
|
|
398
|
+
// @ts-ignore
|
|
399
|
+
texture.isDataTexture = true;
|
|
400
|
+
onLoad == null ? void 0 : onLoad(texture);
|
|
401
|
+
}).catch(onError);
|
|
402
|
+
return texture;
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
// Fetches assets via XMLHttpRequest
|
|
406
|
+
THREE__namespace.FileLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
|
|
407
|
+
if (this.path) url = this.path + url;
|
|
408
|
+
const request = new XMLHttpRequest();
|
|
409
|
+
getAsset(url).then(async asset => {
|
|
410
|
+
let uri = asset.localUri || asset.uri;
|
|
411
|
+
|
|
412
|
+
// Make FS paths web-safe
|
|
413
|
+
if (asset.uri.startsWith('file://')) {
|
|
414
|
+
const data = await fs__namespace.readAsStringAsync(asset.uri, {
|
|
415
|
+
encoding: fs__namespace.EncodingType.Base64
|
|
416
|
+
});
|
|
417
|
+
uri = `data:application/octet-stream;base64,${data}`;
|
|
418
|
+
}
|
|
419
|
+
request.open('GET', uri, true);
|
|
420
|
+
request.addEventListener('load', event => {
|
|
421
|
+
if (request.status === 200) {
|
|
422
|
+
onLoad == null ? void 0 : onLoad(request.response);
|
|
423
|
+
this.manager.itemEnd(url);
|
|
424
|
+
} else {
|
|
425
|
+
onError == null ? void 0 : onError(event);
|
|
426
|
+
this.manager.itemError(url);
|
|
427
|
+
this.manager.itemEnd(url);
|
|
428
|
+
}
|
|
429
|
+
}, false);
|
|
430
|
+
request.addEventListener('progress', event => {
|
|
431
|
+
onProgress == null ? void 0 : onProgress(event);
|
|
432
|
+
}, false);
|
|
433
|
+
request.addEventListener('error', event => {
|
|
434
|
+
onError == null ? void 0 : onError(event);
|
|
435
|
+
this.manager.itemError(url);
|
|
436
|
+
this.manager.itemEnd(url);
|
|
437
|
+
}, false);
|
|
438
|
+
request.addEventListener('abort', event => {
|
|
439
|
+
onError == null ? void 0 : onError(event);
|
|
440
|
+
this.manager.itemError(url);
|
|
441
|
+
this.manager.itemEnd(url);
|
|
442
|
+
}, false);
|
|
443
|
+
if (this.responseType) request.responseType = this.responseType;
|
|
444
|
+
if (this.withCredentials) request.withCredentials = this.withCredentials;
|
|
445
|
+
for (const header in this.requestHeader) {
|
|
446
|
+
request.setRequestHeader(header, this.requestHeader[header]);
|
|
447
|
+
}
|
|
448
|
+
request.send(null);
|
|
449
|
+
this.manager.itemStart(url);
|
|
450
|
+
}).catch(onError);
|
|
451
|
+
return request;
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
polyfills();
|
|
456
|
+
|
|
399
457
|
exports.ReactThreeFiber = index.threeTypes;
|
|
400
458
|
exports._roots = index.roots;
|
|
401
459
|
exports.act = index.act;
|
|
@@ -3,37 +3,18 @@ export { t as ReactThreeFiber, w as _roots, v as act, o as addAfterEffect, n as
|
|
|
3
3
|
import _extends from '@babel/runtime/helpers/esm/extends';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import * as THREE from 'three';
|
|
6
|
-
import { PixelRatio, View, StyleSheet } from 'react-native';
|
|
6
|
+
import { PanResponder, PixelRatio, View, StyleSheet, Image } from 'react-native';
|
|
7
7
|
import { GLView } from 'expo-gl';
|
|
8
8
|
import { FiberProvider, useContextBridge } from 'its-fine';
|
|
9
|
-
import
|
|
9
|
+
import { Asset } from 'expo-asset';
|
|
10
|
+
import * as fs from 'expo-file-system';
|
|
11
|
+
import { fromByteArray } from 'base64-js';
|
|
10
12
|
import 'react-reconciler/constants';
|
|
11
13
|
import 'zustand';
|
|
12
14
|
import 'react-reconciler';
|
|
13
15
|
import 'scheduler';
|
|
14
16
|
import 'suspend-react';
|
|
15
17
|
|
|
16
|
-
/* eslint-enable import/default, import/no-named-as-default, import/no-named-as-default-member */
|
|
17
|
-
|
|
18
|
-
const EVENTS = {
|
|
19
|
-
PRESS: 'onPress',
|
|
20
|
-
PRESSIN: 'onPressIn',
|
|
21
|
-
PRESSOUT: 'onPressOut',
|
|
22
|
-
LONGPRESS: 'onLongPress',
|
|
23
|
-
HOVERIN: 'onHoverIn',
|
|
24
|
-
HOVEROUT: 'onHoverOut',
|
|
25
|
-
PRESSMOVE: 'onPressMove'
|
|
26
|
-
};
|
|
27
|
-
const DOM_EVENTS = {
|
|
28
|
-
[EVENTS.PRESS]: 'onClick',
|
|
29
|
-
[EVENTS.PRESSIN]: 'onPointerDown',
|
|
30
|
-
[EVENTS.PRESSOUT]: 'onPointerUp',
|
|
31
|
-
[EVENTS.LONGPRESS]: 'onDoubleClick',
|
|
32
|
-
[EVENTS.HOVERIN]: 'onPointerOver',
|
|
33
|
-
[EVENTS.HOVEROUT]: 'onPointerOut',
|
|
34
|
-
[EVENTS.PRESSMOVE]: 'onPointerMove'
|
|
35
|
-
};
|
|
36
|
-
|
|
37
18
|
/** Default R3F event manager for react-native */
|
|
38
19
|
function createTouchEvents(store) {
|
|
39
20
|
const {
|
|
@@ -48,9 +29,26 @@ function createTouchEvents(store) {
|
|
|
48
29
|
event.nativeEvent.offsetY = event.nativeEvent.locationY;
|
|
49
30
|
|
|
50
31
|
// Emulate DOM event
|
|
51
|
-
const callback = handlePointer(
|
|
52
|
-
|
|
32
|
+
const callback = handlePointer(name);
|
|
33
|
+
callback(event.nativeEvent);
|
|
34
|
+
return true;
|
|
53
35
|
};
|
|
36
|
+
const responder = PanResponder.create({
|
|
37
|
+
onStartShouldSetPanResponder: () => true,
|
|
38
|
+
onMoveShouldSetPanResponder: () => true,
|
|
39
|
+
onMoveShouldSetPanResponderCapture: () => true,
|
|
40
|
+
onPanResponderTerminationRequest: () => true,
|
|
41
|
+
onStartShouldSetPanResponderCapture: e => handleTouch(e, 'onPointerCapture'),
|
|
42
|
+
onPanResponderStart: e => handleTouch(e, 'onPointerDown'),
|
|
43
|
+
onPanResponderMove: e => handleTouch(e, 'onPointerMove'),
|
|
44
|
+
onPanResponderEnd: (e, state) => {
|
|
45
|
+
handleTouch(e, 'onPointerUp');
|
|
46
|
+
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'onClick');
|
|
47
|
+
},
|
|
48
|
+
onPanResponderRelease: e => handleTouch(e, 'onPointerLeave'),
|
|
49
|
+
onPanResponderTerminate: e => handleTouch(e, 'onLostPointerCapture'),
|
|
50
|
+
onPanResponderReject: e => handleTouch(e, 'onLostPointerCapture')
|
|
51
|
+
});
|
|
54
52
|
return {
|
|
55
53
|
priority: 1,
|
|
56
54
|
enabled: true,
|
|
@@ -61,17 +59,16 @@ function createTouchEvents(store) {
|
|
|
61
59
|
state.raycaster.setFromCamera(state.pointer, state.camera);
|
|
62
60
|
},
|
|
63
61
|
connected: undefined,
|
|
64
|
-
handlers:
|
|
65
|
-
...acc,
|
|
66
|
-
[name]: event => handleTouch(event, name)
|
|
67
|
-
}), {}),
|
|
62
|
+
handlers: responder.panHandlers,
|
|
68
63
|
update: () => {
|
|
69
64
|
var _internal$lastEvent;
|
|
70
65
|
const {
|
|
71
66
|
events,
|
|
72
67
|
internal
|
|
73
68
|
} = store.getState();
|
|
74
|
-
if ((_internal$lastEvent = internal.lastEvent) != null && _internal$lastEvent.current && events.handlers)
|
|
69
|
+
if ((_internal$lastEvent = internal.lastEvent) != null && _internal$lastEvent.current && events.handlers) {
|
|
70
|
+
handlePointer('onPointerMove')(internal.lastEvent.current);
|
|
71
|
+
}
|
|
75
72
|
},
|
|
76
73
|
connect: () => {
|
|
77
74
|
const {
|
|
@@ -79,131 +76,24 @@ function createTouchEvents(store) {
|
|
|
79
76
|
events
|
|
80
77
|
} = store.getState();
|
|
81
78
|
events.disconnect == null ? void 0 : events.disconnect();
|
|
82
|
-
const connected = new Pressability(events.handlers);
|
|
83
79
|
set(state => ({
|
|
84
80
|
events: {
|
|
85
81
|
...state.events,
|
|
86
|
-
connected
|
|
82
|
+
connected: true
|
|
87
83
|
}
|
|
88
84
|
}));
|
|
89
|
-
const handlers = connected.getEventHandlers();
|
|
90
|
-
return handlers;
|
|
91
85
|
},
|
|
92
86
|
disconnect: () => {
|
|
93
87
|
const {
|
|
94
|
-
set
|
|
95
|
-
events
|
|
88
|
+
set
|
|
96
89
|
} = store.getState();
|
|
97
|
-
|
|
98
|
-
events
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
...state.events,
|
|
102
|
-
connected: undefined
|
|
103
|
-
}
|
|
104
|
-
}));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Check if expo-asset is installed (available with expo modules)
|
|
111
|
-
let expAsset;
|
|
112
|
-
try {
|
|
113
|
-
var _require;
|
|
114
|
-
expAsset = (_require = require('expo-asset')) == null ? void 0 : _require.Asset;
|
|
115
|
-
} catch (_) {}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Generates an asset based on input type.
|
|
119
|
-
*/
|
|
120
|
-
function getAsset(input) {
|
|
121
|
-
switch (typeof input) {
|
|
122
|
-
case 'string':
|
|
123
|
-
return expAsset.fromURI(input);
|
|
124
|
-
case 'number':
|
|
125
|
-
return expAsset.fromModule(input);
|
|
126
|
-
default:
|
|
127
|
-
throw new Error('R3F: Invalid asset! Must be a URI or module.');
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
let injected = false;
|
|
131
|
-
function polyfills() {
|
|
132
|
-
if (!expAsset || injected) return;
|
|
133
|
-
injected = true;
|
|
134
|
-
|
|
135
|
-
// Don't pre-process urls, let expo-asset generate an absolute URL
|
|
136
|
-
const extractUrlBase = THREE.LoaderUtils.extractUrlBase.bind(THREE.LoaderUtils);
|
|
137
|
-
THREE.LoaderUtils.extractUrlBase = url => typeof url === 'string' ? extractUrlBase(url) : './';
|
|
138
|
-
|
|
139
|
-
// There's no Image in native, so create a data texture instead
|
|
140
|
-
const prevTextureLoad = THREE.TextureLoader.prototype.load;
|
|
141
|
-
THREE.TextureLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
|
|
142
|
-
if (this.path) url = this.path + url;
|
|
143
|
-
const texture = new THREE.Texture();
|
|
144
|
-
|
|
145
|
-
// @ts-ignore
|
|
146
|
-
texture.isDataTexture = true;
|
|
147
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
148
|
-
texture.image = {
|
|
149
|
-
data: asset,
|
|
150
|
-
width: asset.width,
|
|
151
|
-
height: asset.height
|
|
152
|
-
};
|
|
153
|
-
texture.flipY = true;
|
|
154
|
-
texture.unpackAlignment = 1;
|
|
155
|
-
texture.needsUpdate = true;
|
|
156
|
-
onLoad == null ? void 0 : onLoad(texture);
|
|
157
|
-
}).catch(onError);
|
|
158
|
-
return texture;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
// Fetches assets via XMLHttpRequest
|
|
162
|
-
const prevFileLoad = THREE.FileLoader.prototype.load;
|
|
163
|
-
THREE.FileLoader.prototype.load = function (url, onLoad, onProgress, onError) {
|
|
164
|
-
if (this.path) url = this.path + url;
|
|
165
|
-
const request = new XMLHttpRequest();
|
|
166
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
167
|
-
request.open('GET', asset.uri, true);
|
|
168
|
-
request.addEventListener('load', event => {
|
|
169
|
-
if (request.status === 200) {
|
|
170
|
-
onLoad == null ? void 0 : onLoad(request.response);
|
|
171
|
-
this.manager.itemEnd(url);
|
|
172
|
-
} else {
|
|
173
|
-
onError == null ? void 0 : onError(event);
|
|
174
|
-
this.manager.itemError(url);
|
|
175
|
-
this.manager.itemEnd(url);
|
|
90
|
+
set(state => ({
|
|
91
|
+
events: {
|
|
92
|
+
...state.events,
|
|
93
|
+
connected: false
|
|
176
94
|
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
onProgress == null ? void 0 : onProgress(event);
|
|
180
|
-
}, false);
|
|
181
|
-
request.addEventListener('error', event => {
|
|
182
|
-
onError == null ? void 0 : onError(event);
|
|
183
|
-
this.manager.itemError(url);
|
|
184
|
-
this.manager.itemEnd(url);
|
|
185
|
-
}, false);
|
|
186
|
-
request.addEventListener('abort', event => {
|
|
187
|
-
onError == null ? void 0 : onError(event);
|
|
188
|
-
this.manager.itemError(url);
|
|
189
|
-
this.manager.itemEnd(url);
|
|
190
|
-
}, false);
|
|
191
|
-
if (this.responseType) request.responseType = this.responseType;
|
|
192
|
-
if (this.withCredentials) request.withCredentials = this.withCredentials;
|
|
193
|
-
for (const header in this.requestHeader) {
|
|
194
|
-
request.setRequestHeader(header, this.requestHeader[header]);
|
|
195
|
-
}
|
|
196
|
-
request.send(null);
|
|
197
|
-
this.manager.itemStart(url);
|
|
198
|
-
});
|
|
199
|
-
return request;
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
// Cleanup function
|
|
203
|
-
return () => {
|
|
204
|
-
THREE.LoaderUtils.extractUrlBase = extractUrlBase;
|
|
205
|
-
THREE.TextureLoader.prototype.load = prevTextureLoad;
|
|
206
|
-
THREE.FileLoader.prototype.load = prevFileLoad;
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
207
97
|
};
|
|
208
98
|
}
|
|
209
99
|
|
|
@@ -259,9 +149,6 @@ const CanvasImpl = /*#__PURE__*/React.forwardRef(({
|
|
|
259
149
|
if (error) throw error;
|
|
260
150
|
const viewRef = React.useRef(null);
|
|
261
151
|
const root = React.useRef(null);
|
|
262
|
-
|
|
263
|
-
// Inject and cleanup RN polyfills if able
|
|
264
|
-
React.useLayoutEffect(() => polyfills(), []);
|
|
265
152
|
const onLayout = React.useCallback(e => {
|
|
266
153
|
const {
|
|
267
154
|
width,
|
|
@@ -320,8 +207,7 @@ const CanvasImpl = /*#__PURE__*/React.forwardRef(({
|
|
|
320
207
|
// Overwrite onCreated to apply RN bindings
|
|
321
208
|
onCreated: state => {
|
|
322
209
|
// Bind events after creation
|
|
323
|
-
|
|
324
|
-
setBind(handlers);
|
|
210
|
+
setBind(state.events.handlers);
|
|
325
211
|
|
|
326
212
|
// Bind render to RN bridge
|
|
327
213
|
const context = state.gl.getContext();
|
|
@@ -369,4 +255,178 @@ const Canvas = /*#__PURE__*/React.forwardRef(function CanvasWrapper(props, ref)
|
|
|
369
255
|
})));
|
|
370
256
|
});
|
|
371
257
|
|
|
258
|
+
function polyfills() {
|
|
259
|
+
// Patch Blob for ArrayBuffer if unsupported
|
|
260
|
+
try {
|
|
261
|
+
new Blob([new ArrayBuffer(4)]);
|
|
262
|
+
} catch (_) {
|
|
263
|
+
global.Blob = class extends Blob {
|
|
264
|
+
constructor(parts, options) {
|
|
265
|
+
super(parts == null ? void 0 : parts.map(part => {
|
|
266
|
+
if (part instanceof ArrayBuffer || ArrayBuffer.isView(part)) {
|
|
267
|
+
part = fromByteArray(new Uint8Array(part));
|
|
268
|
+
}
|
|
269
|
+
return part;
|
|
270
|
+
}), options);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function uuidv4() {
|
|
275
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
|
276
|
+
const r = Math.random() * 16 | 0,
|
|
277
|
+
v = c == 'x' ? r : r & 0x3 | 0x8;
|
|
278
|
+
return v.toString(16);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
async function getAsset(input) {
|
|
282
|
+
if (typeof input === 'string') {
|
|
283
|
+
// Point to storage if preceded with fs path
|
|
284
|
+
if (input.startsWith('file:')) return {
|
|
285
|
+
localUri: input
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// Unpack Blobs from react-native BlobManager
|
|
289
|
+
if (input.startsWith('blob:')) {
|
|
290
|
+
const blob = await new Promise((res, rej) => {
|
|
291
|
+
const xhr = new XMLHttpRequest();
|
|
292
|
+
xhr.open('GET', input);
|
|
293
|
+
xhr.responseType = 'blob';
|
|
294
|
+
xhr.onload = () => res(xhr.response);
|
|
295
|
+
xhr.onerror = rej;
|
|
296
|
+
xhr.send();
|
|
297
|
+
});
|
|
298
|
+
const data = await new Promise((res, rej) => {
|
|
299
|
+
const reader = new FileReader();
|
|
300
|
+
reader.onload = () => res(reader.result);
|
|
301
|
+
reader.onerror = rej;
|
|
302
|
+
reader.readAsText(blob);
|
|
303
|
+
});
|
|
304
|
+
input = `data:${blob.type};base64,${data}`;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Create safe URI for JSI
|
|
308
|
+
if (input.startsWith('data:')) {
|
|
309
|
+
const [header, data] = input.split(',');
|
|
310
|
+
const [, type] = header.split('/');
|
|
311
|
+
const localUri = fs.cacheDirectory + uuidv4() + `.${type}`;
|
|
312
|
+
await fs.writeAsStringAsync(localUri, data, {
|
|
313
|
+
encoding: fs.EncodingType.Base64
|
|
314
|
+
});
|
|
315
|
+
return {
|
|
316
|
+
localUri
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Download bundler module or external URL
|
|
322
|
+
const asset = Asset.fromModule(input);
|
|
323
|
+
|
|
324
|
+
// Unpack assets in Android Release Mode
|
|
325
|
+
if (!asset.uri.includes(':')) {
|
|
326
|
+
const localUri = `${fs.cacheDirectory}ExponentAsset-${asset.hash}.${asset.type}`;
|
|
327
|
+
await fs.copyAsync({
|
|
328
|
+
from: asset.uri,
|
|
329
|
+
to: localUri
|
|
330
|
+
});
|
|
331
|
+
return {
|
|
332
|
+
localUri
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Otherwise, resolve from registry
|
|
337
|
+
return asset.downloadAsync();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Don't pre-process urls, let expo-asset generate an absolute URL
|
|
341
|
+
const extractUrlBase = THREE.LoaderUtils.extractUrlBase.bind(THREE.LoaderUtils);
|
|
342
|
+
THREE.LoaderUtils.extractUrlBase = url => typeof url === 'string' ? extractUrlBase(url) : './';
|
|
343
|
+
|
|
344
|
+
// There's no Image in native, so create a data texture instead
|
|
345
|
+
THREE.TextureLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
|
|
346
|
+
if (this.path) url = this.path + url;
|
|
347
|
+
const texture = new THREE.Texture();
|
|
348
|
+
getAsset(url).then(async asset => {
|
|
349
|
+
const uri = asset.localUri || asset.uri;
|
|
350
|
+
if (!asset.width || !asset.height) {
|
|
351
|
+
const {
|
|
352
|
+
width,
|
|
353
|
+
height
|
|
354
|
+
} = await new Promise((res, rej) => Image.getSize(uri, (width, height) => res({
|
|
355
|
+
width,
|
|
356
|
+
height
|
|
357
|
+
}), rej));
|
|
358
|
+
asset.width = width;
|
|
359
|
+
asset.height = height;
|
|
360
|
+
}
|
|
361
|
+
texture.image = {
|
|
362
|
+
data: {
|
|
363
|
+
localUri: uri
|
|
364
|
+
},
|
|
365
|
+
width: asset.width,
|
|
366
|
+
height: asset.height
|
|
367
|
+
};
|
|
368
|
+
texture.flipY = true;
|
|
369
|
+
texture.unpackAlignment = 1;
|
|
370
|
+
texture.needsUpdate = true;
|
|
371
|
+
|
|
372
|
+
// Force non-DOM upload for EXGL fast paths
|
|
373
|
+
// @ts-ignore
|
|
374
|
+
texture.isDataTexture = true;
|
|
375
|
+
onLoad == null ? void 0 : onLoad(texture);
|
|
376
|
+
}).catch(onError);
|
|
377
|
+
return texture;
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
// Fetches assets via XMLHttpRequest
|
|
381
|
+
THREE.FileLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
|
|
382
|
+
if (this.path) url = this.path + url;
|
|
383
|
+
const request = new XMLHttpRequest();
|
|
384
|
+
getAsset(url).then(async asset => {
|
|
385
|
+
let uri = asset.localUri || asset.uri;
|
|
386
|
+
|
|
387
|
+
// Make FS paths web-safe
|
|
388
|
+
if (asset.uri.startsWith('file://')) {
|
|
389
|
+
const data = await fs.readAsStringAsync(asset.uri, {
|
|
390
|
+
encoding: fs.EncodingType.Base64
|
|
391
|
+
});
|
|
392
|
+
uri = `data:application/octet-stream;base64,${data}`;
|
|
393
|
+
}
|
|
394
|
+
request.open('GET', uri, true);
|
|
395
|
+
request.addEventListener('load', event => {
|
|
396
|
+
if (request.status === 200) {
|
|
397
|
+
onLoad == null ? void 0 : onLoad(request.response);
|
|
398
|
+
this.manager.itemEnd(url);
|
|
399
|
+
} else {
|
|
400
|
+
onError == null ? void 0 : onError(event);
|
|
401
|
+
this.manager.itemError(url);
|
|
402
|
+
this.manager.itemEnd(url);
|
|
403
|
+
}
|
|
404
|
+
}, false);
|
|
405
|
+
request.addEventListener('progress', event => {
|
|
406
|
+
onProgress == null ? void 0 : onProgress(event);
|
|
407
|
+
}, false);
|
|
408
|
+
request.addEventListener('error', event => {
|
|
409
|
+
onError == null ? void 0 : onError(event);
|
|
410
|
+
this.manager.itemError(url);
|
|
411
|
+
this.manager.itemEnd(url);
|
|
412
|
+
}, false);
|
|
413
|
+
request.addEventListener('abort', event => {
|
|
414
|
+
onError == null ? void 0 : onError(event);
|
|
415
|
+
this.manager.itemError(url);
|
|
416
|
+
this.manager.itemEnd(url);
|
|
417
|
+
}, false);
|
|
418
|
+
if (this.responseType) request.responseType = this.responseType;
|
|
419
|
+
if (this.withCredentials) request.withCredentials = this.withCredentials;
|
|
420
|
+
for (const header in this.requestHeader) {
|
|
421
|
+
request.setRequestHeader(header, this.requestHeader[header]);
|
|
422
|
+
}
|
|
423
|
+
request.send(null);
|
|
424
|
+
this.manager.itemStart(url);
|
|
425
|
+
}).catch(onError);
|
|
426
|
+
return request;
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
polyfills();
|
|
431
|
+
|
|
372
432
|
export { Canvas, createTouchEvents as events };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-three/fiber",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.14.0",
|
|
4
4
|
"description": "A React renderer for Threejs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@babel/runtime": "^7.17.8",
|
|
46
46
|
"@types/react-reconciler": "^0.26.7",
|
|
47
|
+
"base64-js": "^1.5.1",
|
|
47
48
|
"its-fine": "^1.0.6",
|
|
48
49
|
"react-reconciler": "^0.27.0",
|
|
49
50
|
"react-use-measure": "^2.1.1",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"expo": ">=43.0",
|
|
56
57
|
"expo-asset": ">=8.4",
|
|
57
58
|
"expo-gl": ">=11.0",
|
|
59
|
+
"expo-file-system": ">=11.0",
|
|
58
60
|
"react": ">=18.0",
|
|
59
61
|
"react-dom": ">=18.0",
|
|
60
62
|
"react-native": ">=0.64",
|
|
@@ -73,6 +75,9 @@
|
|
|
73
75
|
"expo-asset": {
|
|
74
76
|
"optional": true
|
|
75
77
|
},
|
|
78
|
+
"expo-file-system": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
76
81
|
"expo-gl": {
|
|
77
82
|
"optional": true
|
|
78
83
|
}
|