@react-three/fiber 8.13.8 → 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 +12 -0
- package/dist/declarations/src/native/polyfills.d.ts +1 -1
- package/native/dist/react-three-fiber-native.cjs.dev.js +210 -151
- package/native/dist/react-three-fiber-native.cjs.prod.js +210 -151
- package/native/dist/react-three-fiber-native.esm.js +210 -149
- package/package.json +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @react-three/fiber
|
|
2
2
|
|
|
3
|
+
## 8.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 89e96bf4: feat: react-native-web, native globals fixes
|
|
8
|
+
|
|
9
|
+
## 8.13.9
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 44d57b3c: fix(native): TextureLoader should remain consistent with FileLoader
|
|
14
|
+
|
|
3
15
|
## 8.13.8
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -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,130 +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
|
-
const texture = new THREE__namespace.Texture();
|
|
170
|
-
|
|
171
|
-
// @ts-ignore
|
|
172
|
-
texture.isDataTexture = true;
|
|
173
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
174
|
-
texture.image = {
|
|
175
|
-
data: asset,
|
|
176
|
-
width: asset.width,
|
|
177
|
-
height: asset.height
|
|
178
|
-
};
|
|
179
|
-
texture.flipY = true;
|
|
180
|
-
texture.unpackAlignment = 1;
|
|
181
|
-
texture.needsUpdate = true;
|
|
182
|
-
onLoad == null ? void 0 : onLoad(texture);
|
|
183
|
-
}).catch(onError);
|
|
184
|
-
return texture;
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// Fetches assets via XMLHttpRequest
|
|
188
|
-
const prevFileLoad = THREE__namespace.FileLoader.prototype.load;
|
|
189
|
-
THREE__namespace.FileLoader.prototype.load = function (url, onLoad, onProgress, onError) {
|
|
190
|
-
if (this.path) url = this.path + url;
|
|
191
|
-
const request = new XMLHttpRequest();
|
|
192
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
193
|
-
request.open('GET', asset.uri, true);
|
|
194
|
-
request.addEventListener('load', event => {
|
|
195
|
-
if (request.status === 200) {
|
|
196
|
-
onLoad == null ? void 0 : onLoad(request.response);
|
|
197
|
-
this.manager.itemEnd(url);
|
|
198
|
-
} else {
|
|
199
|
-
onError == null ? void 0 : onError(event);
|
|
200
|
-
this.manager.itemError(url);
|
|
201
|
-
this.manager.itemEnd(url);
|
|
115
|
+
set(state => ({
|
|
116
|
+
events: {
|
|
117
|
+
...state.events,
|
|
118
|
+
connected: false
|
|
202
119
|
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
onProgress == null ? void 0 : onProgress(event);
|
|
206
|
-
}, false);
|
|
207
|
-
request.addEventListener('error', event => {
|
|
208
|
-
onError == null ? void 0 : onError(event);
|
|
209
|
-
this.manager.itemError(url);
|
|
210
|
-
this.manager.itemEnd(url);
|
|
211
|
-
}, false);
|
|
212
|
-
request.addEventListener('abort', event => {
|
|
213
|
-
onError == null ? void 0 : onError(event);
|
|
214
|
-
this.manager.itemError(url);
|
|
215
|
-
this.manager.itemEnd(url);
|
|
216
|
-
}, false);
|
|
217
|
-
if (this.responseType) request.responseType = this.responseType;
|
|
218
|
-
if (this.withCredentials) request.withCredentials = this.withCredentials;
|
|
219
|
-
for (const header in this.requestHeader) {
|
|
220
|
-
request.setRequestHeader(header, this.requestHeader[header]);
|
|
221
|
-
}
|
|
222
|
-
request.send(null);
|
|
223
|
-
this.manager.itemStart(url);
|
|
224
|
-
});
|
|
225
|
-
return request;
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
// Cleanup function
|
|
229
|
-
return () => {
|
|
230
|
-
THREE__namespace.LoaderUtils.extractUrlBase = extractUrlBase;
|
|
231
|
-
THREE__namespace.TextureLoader.prototype.load = prevTextureLoad;
|
|
232
|
-
THREE__namespace.FileLoader.prototype.load = prevFileLoad;
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
233
122
|
};
|
|
234
123
|
}
|
|
235
124
|
|
|
@@ -285,9 +174,6 @@ const CanvasImpl = /*#__PURE__*/React__namespace.forwardRef(({
|
|
|
285
174
|
if (error) throw error;
|
|
286
175
|
const viewRef = React__namespace.useRef(null);
|
|
287
176
|
const root = React__namespace.useRef(null);
|
|
288
|
-
|
|
289
|
-
// Inject and cleanup RN polyfills if able
|
|
290
|
-
React__namespace.useLayoutEffect(() => polyfills(), []);
|
|
291
177
|
const onLayout = React__namespace.useCallback(e => {
|
|
292
178
|
const {
|
|
293
179
|
width,
|
|
@@ -346,8 +232,7 @@ const CanvasImpl = /*#__PURE__*/React__namespace.forwardRef(({
|
|
|
346
232
|
// Overwrite onCreated to apply RN bindings
|
|
347
233
|
onCreated: state => {
|
|
348
234
|
// Bind events after creation
|
|
349
|
-
|
|
350
|
-
setBind(handlers);
|
|
235
|
+
setBind(state.events.handlers);
|
|
351
236
|
|
|
352
237
|
// Bind render to RN bridge
|
|
353
238
|
const context = state.gl.getContext();
|
|
@@ -395,6 +280,180 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(function CanvasWrapper(p
|
|
|
395
280
|
})));
|
|
396
281
|
});
|
|
397
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
|
+
|
|
398
457
|
exports.ReactThreeFiber = index.threeTypes;
|
|
399
458
|
exports._roots = index.roots;
|
|
400
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,130 +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
|
-
const texture = new THREE__namespace.Texture();
|
|
170
|
-
|
|
171
|
-
// @ts-ignore
|
|
172
|
-
texture.isDataTexture = true;
|
|
173
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
174
|
-
texture.image = {
|
|
175
|
-
data: asset,
|
|
176
|
-
width: asset.width,
|
|
177
|
-
height: asset.height
|
|
178
|
-
};
|
|
179
|
-
texture.flipY = true;
|
|
180
|
-
texture.unpackAlignment = 1;
|
|
181
|
-
texture.needsUpdate = true;
|
|
182
|
-
onLoad == null ? void 0 : onLoad(texture);
|
|
183
|
-
}).catch(onError);
|
|
184
|
-
return texture;
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// Fetches assets via XMLHttpRequest
|
|
188
|
-
const prevFileLoad = THREE__namespace.FileLoader.prototype.load;
|
|
189
|
-
THREE__namespace.FileLoader.prototype.load = function (url, onLoad, onProgress, onError) {
|
|
190
|
-
if (this.path) url = this.path + url;
|
|
191
|
-
const request = new XMLHttpRequest();
|
|
192
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
193
|
-
request.open('GET', asset.uri, true);
|
|
194
|
-
request.addEventListener('load', event => {
|
|
195
|
-
if (request.status === 200) {
|
|
196
|
-
onLoad == null ? void 0 : onLoad(request.response);
|
|
197
|
-
this.manager.itemEnd(url);
|
|
198
|
-
} else {
|
|
199
|
-
onError == null ? void 0 : onError(event);
|
|
200
|
-
this.manager.itemError(url);
|
|
201
|
-
this.manager.itemEnd(url);
|
|
115
|
+
set(state => ({
|
|
116
|
+
events: {
|
|
117
|
+
...state.events,
|
|
118
|
+
connected: false
|
|
202
119
|
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
onProgress == null ? void 0 : onProgress(event);
|
|
206
|
-
}, false);
|
|
207
|
-
request.addEventListener('error', event => {
|
|
208
|
-
onError == null ? void 0 : onError(event);
|
|
209
|
-
this.manager.itemError(url);
|
|
210
|
-
this.manager.itemEnd(url);
|
|
211
|
-
}, false);
|
|
212
|
-
request.addEventListener('abort', event => {
|
|
213
|
-
onError == null ? void 0 : onError(event);
|
|
214
|
-
this.manager.itemError(url);
|
|
215
|
-
this.manager.itemEnd(url);
|
|
216
|
-
}, false);
|
|
217
|
-
if (this.responseType) request.responseType = this.responseType;
|
|
218
|
-
if (this.withCredentials) request.withCredentials = this.withCredentials;
|
|
219
|
-
for (const header in this.requestHeader) {
|
|
220
|
-
request.setRequestHeader(header, this.requestHeader[header]);
|
|
221
|
-
}
|
|
222
|
-
request.send(null);
|
|
223
|
-
this.manager.itemStart(url);
|
|
224
|
-
});
|
|
225
|
-
return request;
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
// Cleanup function
|
|
229
|
-
return () => {
|
|
230
|
-
THREE__namespace.LoaderUtils.extractUrlBase = extractUrlBase;
|
|
231
|
-
THREE__namespace.TextureLoader.prototype.load = prevTextureLoad;
|
|
232
|
-
THREE__namespace.FileLoader.prototype.load = prevFileLoad;
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
233
122
|
};
|
|
234
123
|
}
|
|
235
124
|
|
|
@@ -285,9 +174,6 @@ const CanvasImpl = /*#__PURE__*/React__namespace.forwardRef(({
|
|
|
285
174
|
if (error) throw error;
|
|
286
175
|
const viewRef = React__namespace.useRef(null);
|
|
287
176
|
const root = React__namespace.useRef(null);
|
|
288
|
-
|
|
289
|
-
// Inject and cleanup RN polyfills if able
|
|
290
|
-
React__namespace.useLayoutEffect(() => polyfills(), []);
|
|
291
177
|
const onLayout = React__namespace.useCallback(e => {
|
|
292
178
|
const {
|
|
293
179
|
width,
|
|
@@ -346,8 +232,7 @@ const CanvasImpl = /*#__PURE__*/React__namespace.forwardRef(({
|
|
|
346
232
|
// Overwrite onCreated to apply RN bindings
|
|
347
233
|
onCreated: state => {
|
|
348
234
|
// Bind events after creation
|
|
349
|
-
|
|
350
|
-
setBind(handlers);
|
|
235
|
+
setBind(state.events.handlers);
|
|
351
236
|
|
|
352
237
|
// Bind render to RN bridge
|
|
353
238
|
const context = state.gl.getContext();
|
|
@@ -395,6 +280,180 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(function CanvasWrapper(p
|
|
|
395
280
|
})));
|
|
396
281
|
});
|
|
397
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
|
+
|
|
398
457
|
exports.ReactThreeFiber = index.threeTypes;
|
|
399
458
|
exports._roots = index.roots;
|
|
400
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,130 +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
|
-
const texture = new THREE.Texture();
|
|
143
|
-
|
|
144
|
-
// @ts-ignore
|
|
145
|
-
texture.isDataTexture = true;
|
|
146
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
147
|
-
texture.image = {
|
|
148
|
-
data: asset,
|
|
149
|
-
width: asset.width,
|
|
150
|
-
height: asset.height
|
|
151
|
-
};
|
|
152
|
-
texture.flipY = true;
|
|
153
|
-
texture.unpackAlignment = 1;
|
|
154
|
-
texture.needsUpdate = true;
|
|
155
|
-
onLoad == null ? void 0 : onLoad(texture);
|
|
156
|
-
}).catch(onError);
|
|
157
|
-
return texture;
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
// Fetches assets via XMLHttpRequest
|
|
161
|
-
const prevFileLoad = THREE.FileLoader.prototype.load;
|
|
162
|
-
THREE.FileLoader.prototype.load = function (url, onLoad, onProgress, onError) {
|
|
163
|
-
if (this.path) url = this.path + url;
|
|
164
|
-
const request = new XMLHttpRequest();
|
|
165
|
-
getAsset(url).downloadAsync().then(asset => {
|
|
166
|
-
request.open('GET', asset.uri, true);
|
|
167
|
-
request.addEventListener('load', event => {
|
|
168
|
-
if (request.status === 200) {
|
|
169
|
-
onLoad == null ? void 0 : onLoad(request.response);
|
|
170
|
-
this.manager.itemEnd(url);
|
|
171
|
-
} else {
|
|
172
|
-
onError == null ? void 0 : onError(event);
|
|
173
|
-
this.manager.itemError(url);
|
|
174
|
-
this.manager.itemEnd(url);
|
|
90
|
+
set(state => ({
|
|
91
|
+
events: {
|
|
92
|
+
...state.events,
|
|
93
|
+
connected: false
|
|
175
94
|
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
onProgress == null ? void 0 : onProgress(event);
|
|
179
|
-
}, false);
|
|
180
|
-
request.addEventListener('error', event => {
|
|
181
|
-
onError == null ? void 0 : onError(event);
|
|
182
|
-
this.manager.itemError(url);
|
|
183
|
-
this.manager.itemEnd(url);
|
|
184
|
-
}, false);
|
|
185
|
-
request.addEventListener('abort', event => {
|
|
186
|
-
onError == null ? void 0 : onError(event);
|
|
187
|
-
this.manager.itemError(url);
|
|
188
|
-
this.manager.itemEnd(url);
|
|
189
|
-
}, false);
|
|
190
|
-
if (this.responseType) request.responseType = this.responseType;
|
|
191
|
-
if (this.withCredentials) request.withCredentials = this.withCredentials;
|
|
192
|
-
for (const header in this.requestHeader) {
|
|
193
|
-
request.setRequestHeader(header, this.requestHeader[header]);
|
|
194
|
-
}
|
|
195
|
-
request.send(null);
|
|
196
|
-
this.manager.itemStart(url);
|
|
197
|
-
});
|
|
198
|
-
return request;
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
// Cleanup function
|
|
202
|
-
return () => {
|
|
203
|
-
THREE.LoaderUtils.extractUrlBase = extractUrlBase;
|
|
204
|
-
THREE.TextureLoader.prototype.load = prevTextureLoad;
|
|
205
|
-
THREE.FileLoader.prototype.load = prevFileLoad;
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
206
97
|
};
|
|
207
98
|
}
|
|
208
99
|
|
|
@@ -258,9 +149,6 @@ const CanvasImpl = /*#__PURE__*/React.forwardRef(({
|
|
|
258
149
|
if (error) throw error;
|
|
259
150
|
const viewRef = React.useRef(null);
|
|
260
151
|
const root = React.useRef(null);
|
|
261
|
-
|
|
262
|
-
// Inject and cleanup RN polyfills if able
|
|
263
|
-
React.useLayoutEffect(() => polyfills(), []);
|
|
264
152
|
const onLayout = React.useCallback(e => {
|
|
265
153
|
const {
|
|
266
154
|
width,
|
|
@@ -319,8 +207,7 @@ const CanvasImpl = /*#__PURE__*/React.forwardRef(({
|
|
|
319
207
|
// Overwrite onCreated to apply RN bindings
|
|
320
208
|
onCreated: state => {
|
|
321
209
|
// Bind events after creation
|
|
322
|
-
|
|
323
|
-
setBind(handlers);
|
|
210
|
+
setBind(state.events.handlers);
|
|
324
211
|
|
|
325
212
|
// Bind render to RN bridge
|
|
326
213
|
const context = state.gl.getContext();
|
|
@@ -368,4 +255,178 @@ const Canvas = /*#__PURE__*/React.forwardRef(function CanvasWrapper(props, ref)
|
|
|
368
255
|
})));
|
|
369
256
|
});
|
|
370
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
|
+
|
|
371
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
|
}
|