@penabt/pixi-expo 0.2.0 → 0.3.1
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/README.md +99 -11
- package/dist/index.d.mts +75 -3
- package/dist/index.d.ts +75 -3
- package/dist/index.js +142 -21
- package/dist/index.mjs +142 -24
- package/package.json +2 -2
- package/src/adapter/expoManifest.ts +143 -0
- package/src/adapter/loadExpoAsset.ts +106 -23
- package/src/adapter/polyfills.ts +1 -1
- package/src/index.ts +46 -3
package/README.md
CHANGED
|
@@ -143,21 +143,109 @@ import {
|
|
|
143
143
|
|
|
144
144
|
## Loading Assets
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
All asset loading goes through the Expo-compatible loader. PixiJS's browser-dependent `loadTextures` parser is replaced automatically — you don't need to do anything special.
|
|
147
|
+
|
|
148
|
+
Supported formats: `.png`, `.jpg`, `.jpeg`, `.webp`, `.avif`, `.gif`, `data:image/*`
|
|
149
|
+
|
|
150
|
+
### Direct Loading — `Assets.load()`
|
|
151
|
+
|
|
152
|
+
Works with both `require()` (local bundled) and string URLs (remote):
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { Assets, Sprite } from '@penabt/pixi-expo';
|
|
156
|
+
|
|
157
|
+
// Local asset via require()
|
|
158
|
+
const bunny = await Assets.load(require('./assets/bunny.png'));
|
|
159
|
+
const sprite = new Sprite(bunny);
|
|
160
|
+
|
|
161
|
+
// Remote asset via URL
|
|
162
|
+
const remote = await Assets.load('https://example.com/sprite.png');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Array Loading — `Assets.load([])`
|
|
166
|
+
|
|
167
|
+
Load multiple assets at once with array destructuring:
|
|
147
168
|
|
|
148
169
|
```tsx
|
|
149
|
-
import {
|
|
170
|
+
import { Assets, Sprite } from '@penabt/pixi-expo';
|
|
150
171
|
|
|
151
|
-
//
|
|
152
|
-
const
|
|
153
|
-
|
|
172
|
+
// Mix require() and remote URLs in a single call
|
|
173
|
+
const [frame1, frame2, enemy] = await Assets.load([
|
|
174
|
+
require('./assets/frame-1.png'),
|
|
175
|
+
require('./assets/frame-2.png'),
|
|
176
|
+
'https://example.com/enemy.png',
|
|
177
|
+
]);
|
|
178
|
+
|
|
179
|
+
const sprite1 = new Sprite(frame1);
|
|
180
|
+
const sprite2 = new Sprite(frame2);
|
|
154
181
|
```
|
|
155
182
|
|
|
156
|
-
|
|
183
|
+
> **Note:** Unlike standard PixiJS (which returns a `Record<string, Texture>` for arrays), `@penabt/pixi-expo` returns an array in the same order as the input — enabling convenient destructuring.
|
|
184
|
+
|
|
185
|
+
### Manifest & Bundles — `createExpoManifest()`
|
|
186
|
+
|
|
187
|
+
For larger projects, group assets into bundles and load them on demand:
|
|
157
188
|
|
|
158
189
|
```tsx
|
|
159
|
-
|
|
160
|
-
|
|
190
|
+
import { Assets, createExpoManifest } from '@penabt/pixi-expo';
|
|
191
|
+
|
|
192
|
+
const manifest = createExpoManifest({
|
|
193
|
+
bundles: [
|
|
194
|
+
{
|
|
195
|
+
name: 'load-screen',
|
|
196
|
+
assets: [{ alias: 'logo', src: require('./assets/logo.png') }],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: 'game',
|
|
200
|
+
assets: [
|
|
201
|
+
{ alias: 'hero', src: require('./assets/hero.png') },
|
|
202
|
+
{ alias: 'enemy', src: 'https://cdn.example.com/enemy.png' },
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Initialize once
|
|
209
|
+
await Assets.init({ manifest });
|
|
210
|
+
|
|
211
|
+
// Load bundles on demand
|
|
212
|
+
const loadAssets = await Assets.loadBundle('load-screen');
|
|
213
|
+
const gameAssets = await Assets.loadBundle('game');
|
|
214
|
+
const heroSprite = new Sprite(gameAssets.hero);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Dynamic Bundles — `createExpoBundle()`
|
|
218
|
+
|
|
219
|
+
Register bundles at runtime:
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
import { Assets, createExpoBundle } from '@penabt/pixi-expo';
|
|
223
|
+
|
|
224
|
+
Assets.addBundle(
|
|
225
|
+
'powerups',
|
|
226
|
+
createExpoBundle([
|
|
227
|
+
{ alias: 'shield', src: require('./assets/shield.png') },
|
|
228
|
+
{ alias: 'speed', src: 'https://cdn.example.com/speed.png' },
|
|
229
|
+
]),
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const powerups = await Assets.loadBundle('powerups');
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### BitmapFont
|
|
236
|
+
|
|
237
|
+
Load `.fnt` or `.xml` bitmap fonts — the atlas texture is loaded automatically:
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { Assets, BitmapText } from '@penabt/pixi-expo';
|
|
241
|
+
|
|
242
|
+
await Assets.load('https://example.com/fonts/myfont.xml');
|
|
243
|
+
|
|
244
|
+
const score = new BitmapText({
|
|
245
|
+
text: 'Score: 0',
|
|
246
|
+
style: { fontFamily: 'MyFont', fontSize: 32 },
|
|
247
|
+
});
|
|
248
|
+
app.stage.addChild(score);
|
|
161
249
|
```
|
|
162
250
|
|
|
163
251
|
## Performance Tips
|
|
@@ -174,9 +262,9 @@ const texture = await Assets.load('https://example.com/sprite.png');
|
|
|
174
262
|
|
|
175
263
|
## Limitations
|
|
176
264
|
|
|
177
|
-
- **No Canvas 2D**
|
|
178
|
-
- **No
|
|
179
|
-
- **Font Loading**
|
|
265
|
+
- **No Canvas 2D** — expo-gl only supports WebGL, not Canvas 2D context
|
|
266
|
+
- **No Text** — `Text` (canvas-based) and `HTMLText` are not available. Use `BitmapText` instead
|
|
267
|
+
- **Font Loading** — Use `BitmapFont` (`.fnt`/`.xml` + atlas) for in-game text, or `expo-font` for system fonts
|
|
180
268
|
|
|
181
269
|
## Compatibility
|
|
182
270
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ExpoWebGLRenderingContext } from 'expo-gl';
|
|
2
|
-
import { LoaderParser, Texture, Application, Container } from 'pixi.js';
|
|
3
|
-
export { AnimatedSprite, Application, ApplicationOptions, Assets, Batcher, BitmapText, BlurFilter, Circle, Color, ColorMatrixFilter, Container, DOMAdapter, DisplacementFilter, Ellipse, EventBoundary, ExtensionType, FederatedPointerEvent, Filter, FilterOptions, Graphics, Matrix, Mesh, NineSliceSprite, NoiseFilter, ObservablePoint, Point, Polygon, Rectangle, RenderTexture, Renderer, RoundedRectangle, Sprite, Spritesheet, SpritesheetData, Text, TextStyle, Texture, TextureSource, TextureSourceOptions, Ticker, TilingSprite, extensions } from 'pixi.js';
|
|
2
|
+
import { LoaderParser, Texture, ArrayOr, AssetsBundle, AssetsManifest, UnresolvedAsset, Application, Container } from 'pixi.js';
|
|
3
|
+
export { AnimatedSprite, Application, ApplicationOptions, Assets, AssetsBundle, AssetsManifest, Batcher, BitmapText, BlurFilter, Circle, Color, ColorMatrixFilter, Container, DOMAdapter, DisplacementFilter, Ellipse, EventBoundary, ExtensionType, FederatedPointerEvent, Filter, FilterOptions, Graphics, Matrix, Mesh, NineSliceSprite, NoiseFilter, ObservablePoint, Point, Polygon, Rectangle, RenderTexture, Renderer, RoundedRectangle, Sprite, Spritesheet, SpritesheetData, Text, TextStyle, Texture, TextureSource, TextureSourceOptions, Ticker, TilingSprite, UnresolvedAsset, extensions } from 'pixi.js';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
import { ViewStyle, NativeTouchEvent, GestureResponderEvent } from 'react-native';
|
|
6
6
|
|
|
@@ -499,6 +499,78 @@ declare const loadExpoAsset: LoaderParser<Texture>;
|
|
|
499
499
|
*/
|
|
500
500
|
declare const loadExpoFont: LoaderParser<string>;
|
|
501
501
|
|
|
502
|
+
/**
|
|
503
|
+
* PixiJS Manifest & Bundle utilities for Expo.
|
|
504
|
+
*
|
|
505
|
+
* Transforms Expo-flavored manifests (where `src` can be a require() numeric ID)
|
|
506
|
+
* into standard PixiJS manifests (where `src` is always a string).
|
|
507
|
+
*/
|
|
508
|
+
|
|
509
|
+
/** An asset source that accepts Expo require() numeric IDs alongside strings. */
|
|
510
|
+
type ExpoAssetSrc = number | string;
|
|
511
|
+
/**
|
|
512
|
+
* An unresolved asset that accepts require() IDs in src.
|
|
513
|
+
* Mirrors PixiJS UnresolvedAsset but src can include numeric module IDs.
|
|
514
|
+
*/
|
|
515
|
+
interface ExpoUnresolvedAsset<T = any> {
|
|
516
|
+
alias?: ArrayOr<string>;
|
|
517
|
+
src?: ExpoAssetSrc | ExpoAssetSrc[];
|
|
518
|
+
data?: T;
|
|
519
|
+
format?: string;
|
|
520
|
+
parser?: string;
|
|
521
|
+
[key: string]: any;
|
|
522
|
+
}
|
|
523
|
+
/** A bundle that accepts Expo require() IDs. */
|
|
524
|
+
interface ExpoAssetsBundle {
|
|
525
|
+
name: string;
|
|
526
|
+
assets: ExpoUnresolvedAsset[] | Record<string, ExpoAssetSrc | ExpoAssetSrc[] | ExpoUnresolvedAsset>;
|
|
527
|
+
}
|
|
528
|
+
/** A manifest that accepts Expo require() IDs. */
|
|
529
|
+
interface ExpoAssetsManifest {
|
|
530
|
+
bundles: ExpoAssetsBundle[];
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Transform a single Expo asset entry into a standard PixiJS UnresolvedAsset.
|
|
534
|
+
* Numeric require() IDs are registered and converted to string keys.
|
|
535
|
+
*/
|
|
536
|
+
declare function resolveExpoAsset<T = any>(asset: ExpoUnresolvedAsset<T>): UnresolvedAsset<T>;
|
|
537
|
+
/**
|
|
538
|
+
* Transform bundle assets for use with `Assets.addBundle()`.
|
|
539
|
+
* Handles both array and Record formats.
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* ```ts
|
|
543
|
+
* Assets.addBundle('animals', createExpoBundle([
|
|
544
|
+
* { alias: 'bunny', src: require('./assets/bunny.png') },
|
|
545
|
+
* { alias: 'chicken', src: 'https://example.com/chicken.png' },
|
|
546
|
+
* ]));
|
|
547
|
+
* ```
|
|
548
|
+
*/
|
|
549
|
+
declare function createExpoBundle(assets: ExpoAssetsBundle['assets']): AssetsBundle['assets'];
|
|
550
|
+
/**
|
|
551
|
+
* Transform an Expo-flavored manifest into a standard PixiJS AssetsManifest.
|
|
552
|
+
* Numeric require() IDs in `src` fields are registered and converted to string keys.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```ts
|
|
556
|
+
* const manifest = createExpoManifest({
|
|
557
|
+
* bundles: [
|
|
558
|
+
* {
|
|
559
|
+
* name: 'game-screen',
|
|
560
|
+
* assets: [
|
|
561
|
+
* { alias: 'character', src: require('./assets/robot.png') },
|
|
562
|
+
* { alias: 'enemy', src: 'https://example.com/bad-guy.png' },
|
|
563
|
+
* ],
|
|
564
|
+
* },
|
|
565
|
+
* ],
|
|
566
|
+
* });
|
|
567
|
+
*
|
|
568
|
+
* await Assets.init({ manifest });
|
|
569
|
+
* const assets = await Assets.loadBundle('game-screen');
|
|
570
|
+
* ```
|
|
571
|
+
*/
|
|
572
|
+
declare function createExpoManifest(manifest: ExpoAssetsManifest): AssetsManifest;
|
|
573
|
+
|
|
502
574
|
/**
|
|
503
575
|
* Props for the PixiView component.
|
|
504
576
|
*/
|
|
@@ -744,4 +816,4 @@ declare function clearTouchTracking(): void;
|
|
|
744
816
|
*/
|
|
745
817
|
declare function getActiveTouchCount(): number;
|
|
746
818
|
|
|
747
|
-
export { ExpoAdapter, ExpoCanvasElement, type NativePointerEvent, PixiView, type PixiViewHandle, type PixiViewProps, type TouchEventBridgeOptions, clearActiveContext, clearTouchTracking, convertTouchToPointerEvents, getActiveCanvas, getActiveGL, getActiveTouchCount, loadExpoAsset, loadExpoFont, loadTexture, setActiveGLContext };
|
|
819
|
+
export { ExpoAdapter, type ExpoAssetSrc, type ExpoAssetsBundle, type ExpoAssetsManifest, ExpoCanvasElement, type ExpoUnresolvedAsset, type NativePointerEvent, PixiView, type PixiViewHandle, type PixiViewProps, type TouchEventBridgeOptions, clearActiveContext, clearTouchTracking, convertTouchToPointerEvents, createExpoBundle, createExpoManifest, getActiveCanvas, getActiveGL, getActiveTouchCount, loadExpoAsset, loadExpoFont, loadTexture, resolveExpoAsset, setActiveGLContext };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ExpoWebGLRenderingContext } from 'expo-gl';
|
|
2
|
-
import { LoaderParser, Texture, Application, Container } from 'pixi.js';
|
|
3
|
-
export { AnimatedSprite, Application, ApplicationOptions, Assets, Batcher, BitmapText, BlurFilter, Circle, Color, ColorMatrixFilter, Container, DOMAdapter, DisplacementFilter, Ellipse, EventBoundary, ExtensionType, FederatedPointerEvent, Filter, FilterOptions, Graphics, Matrix, Mesh, NineSliceSprite, NoiseFilter, ObservablePoint, Point, Polygon, Rectangle, RenderTexture, Renderer, RoundedRectangle, Sprite, Spritesheet, SpritesheetData, Text, TextStyle, Texture, TextureSource, TextureSourceOptions, Ticker, TilingSprite, extensions } from 'pixi.js';
|
|
2
|
+
import { LoaderParser, Texture, ArrayOr, AssetsBundle, AssetsManifest, UnresolvedAsset, Application, Container } from 'pixi.js';
|
|
3
|
+
export { AnimatedSprite, Application, ApplicationOptions, Assets, AssetsBundle, AssetsManifest, Batcher, BitmapText, BlurFilter, Circle, Color, ColorMatrixFilter, Container, DOMAdapter, DisplacementFilter, Ellipse, EventBoundary, ExtensionType, FederatedPointerEvent, Filter, FilterOptions, Graphics, Matrix, Mesh, NineSliceSprite, NoiseFilter, ObservablePoint, Point, Polygon, Rectangle, RenderTexture, Renderer, RoundedRectangle, Sprite, Spritesheet, SpritesheetData, Text, TextStyle, Texture, TextureSource, TextureSourceOptions, Ticker, TilingSprite, UnresolvedAsset, extensions } from 'pixi.js';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
import { ViewStyle, NativeTouchEvent, GestureResponderEvent } from 'react-native';
|
|
6
6
|
|
|
@@ -499,6 +499,78 @@ declare const loadExpoAsset: LoaderParser<Texture>;
|
|
|
499
499
|
*/
|
|
500
500
|
declare const loadExpoFont: LoaderParser<string>;
|
|
501
501
|
|
|
502
|
+
/**
|
|
503
|
+
* PixiJS Manifest & Bundle utilities for Expo.
|
|
504
|
+
*
|
|
505
|
+
* Transforms Expo-flavored manifests (where `src` can be a require() numeric ID)
|
|
506
|
+
* into standard PixiJS manifests (where `src` is always a string).
|
|
507
|
+
*/
|
|
508
|
+
|
|
509
|
+
/** An asset source that accepts Expo require() numeric IDs alongside strings. */
|
|
510
|
+
type ExpoAssetSrc = number | string;
|
|
511
|
+
/**
|
|
512
|
+
* An unresolved asset that accepts require() IDs in src.
|
|
513
|
+
* Mirrors PixiJS UnresolvedAsset but src can include numeric module IDs.
|
|
514
|
+
*/
|
|
515
|
+
interface ExpoUnresolvedAsset<T = any> {
|
|
516
|
+
alias?: ArrayOr<string>;
|
|
517
|
+
src?: ExpoAssetSrc | ExpoAssetSrc[];
|
|
518
|
+
data?: T;
|
|
519
|
+
format?: string;
|
|
520
|
+
parser?: string;
|
|
521
|
+
[key: string]: any;
|
|
522
|
+
}
|
|
523
|
+
/** A bundle that accepts Expo require() IDs. */
|
|
524
|
+
interface ExpoAssetsBundle {
|
|
525
|
+
name: string;
|
|
526
|
+
assets: ExpoUnresolvedAsset[] | Record<string, ExpoAssetSrc | ExpoAssetSrc[] | ExpoUnresolvedAsset>;
|
|
527
|
+
}
|
|
528
|
+
/** A manifest that accepts Expo require() IDs. */
|
|
529
|
+
interface ExpoAssetsManifest {
|
|
530
|
+
bundles: ExpoAssetsBundle[];
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Transform a single Expo asset entry into a standard PixiJS UnresolvedAsset.
|
|
534
|
+
* Numeric require() IDs are registered and converted to string keys.
|
|
535
|
+
*/
|
|
536
|
+
declare function resolveExpoAsset<T = any>(asset: ExpoUnresolvedAsset<T>): UnresolvedAsset<T>;
|
|
537
|
+
/**
|
|
538
|
+
* Transform bundle assets for use with `Assets.addBundle()`.
|
|
539
|
+
* Handles both array and Record formats.
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* ```ts
|
|
543
|
+
* Assets.addBundle('animals', createExpoBundle([
|
|
544
|
+
* { alias: 'bunny', src: require('./assets/bunny.png') },
|
|
545
|
+
* { alias: 'chicken', src: 'https://example.com/chicken.png' },
|
|
546
|
+
* ]));
|
|
547
|
+
* ```
|
|
548
|
+
*/
|
|
549
|
+
declare function createExpoBundle(assets: ExpoAssetsBundle['assets']): AssetsBundle['assets'];
|
|
550
|
+
/**
|
|
551
|
+
* Transform an Expo-flavored manifest into a standard PixiJS AssetsManifest.
|
|
552
|
+
* Numeric require() IDs in `src` fields are registered and converted to string keys.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```ts
|
|
556
|
+
* const manifest = createExpoManifest({
|
|
557
|
+
* bundles: [
|
|
558
|
+
* {
|
|
559
|
+
* name: 'game-screen',
|
|
560
|
+
* assets: [
|
|
561
|
+
* { alias: 'character', src: require('./assets/robot.png') },
|
|
562
|
+
* { alias: 'enemy', src: 'https://example.com/bad-guy.png' },
|
|
563
|
+
* ],
|
|
564
|
+
* },
|
|
565
|
+
* ],
|
|
566
|
+
* });
|
|
567
|
+
*
|
|
568
|
+
* await Assets.init({ manifest });
|
|
569
|
+
* const assets = await Assets.loadBundle('game-screen');
|
|
570
|
+
* ```
|
|
571
|
+
*/
|
|
572
|
+
declare function createExpoManifest(manifest: ExpoAssetsManifest): AssetsManifest;
|
|
573
|
+
|
|
502
574
|
/**
|
|
503
575
|
* Props for the PixiView component.
|
|
504
576
|
*/
|
|
@@ -744,4 +816,4 @@ declare function clearTouchTracking(): void;
|
|
|
744
816
|
*/
|
|
745
817
|
declare function getActiveTouchCount(): number;
|
|
746
818
|
|
|
747
|
-
export { ExpoAdapter, ExpoCanvasElement, type NativePointerEvent, PixiView, type PixiViewHandle, type PixiViewProps, type TouchEventBridgeOptions, clearActiveContext, clearTouchTracking, convertTouchToPointerEvents, getActiveCanvas, getActiveGL, getActiveTouchCount, loadExpoAsset, loadExpoFont, loadTexture, setActiveGLContext };
|
|
819
|
+
export { ExpoAdapter, type ExpoAssetSrc, type ExpoAssetsBundle, type ExpoAssetsManifest, ExpoCanvasElement, type ExpoUnresolvedAsset, type NativePointerEvent, PixiView, type PixiViewHandle, type PixiViewProps, type TouchEventBridgeOptions, clearActiveContext, clearTouchTracking, convertTouchToPointerEvents, createExpoBundle, createExpoManifest, getActiveCanvas, getActiveGL, getActiveTouchCount, loadExpoAsset, loadExpoFont, loadTexture, resolveExpoAsset, setActiveGLContext };
|
package/dist/index.js
CHANGED
|
@@ -72,6 +72,8 @@ __export(index_exports, {
|
|
|
72
72
|
clearActiveContext: () => clearActiveContext,
|
|
73
73
|
clearTouchTracking: () => clearTouchTracking,
|
|
74
74
|
convertTouchToPointerEvents: () => convertTouchToPointerEvents,
|
|
75
|
+
createExpoBundle: () => createExpoBundle,
|
|
76
|
+
createExpoManifest: () => createExpoManifest,
|
|
75
77
|
extensions: () => import_pixi5.extensions,
|
|
76
78
|
getActiveCanvas: () => getActiveCanvas,
|
|
77
79
|
getActiveGL: () => getActiveGL,
|
|
@@ -79,6 +81,7 @@ __export(index_exports, {
|
|
|
79
81
|
loadExpoAsset: () => loadExpoAsset,
|
|
80
82
|
loadExpoFont: () => loadExpoFont,
|
|
81
83
|
loadTexture: () => loadTexture,
|
|
84
|
+
resolveExpoAsset: () => resolveExpoAsset,
|
|
82
85
|
setActiveGLContext: () => setActiveGLContext
|
|
83
86
|
});
|
|
84
87
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -300,7 +303,6 @@ function windowRemoveEventListener(type, listener, _options) {
|
|
|
300
303
|
function dispatchWindowEvent(event) {
|
|
301
304
|
const listeners = windowListeners.get(event.type);
|
|
302
305
|
if (__DEV__) {
|
|
303
|
-
console.log(`[Window] dispatchEvent: ${event.type}, listeners: ${listeners?.size ?? 0}`);
|
|
304
306
|
}
|
|
305
307
|
if (!listeners || listeners.size === 0) {
|
|
306
308
|
return false;
|
|
@@ -1064,9 +1066,30 @@ var import_pixi4 = require("pixi.js");
|
|
|
1064
1066
|
var import_expo_asset = require("expo-asset");
|
|
1065
1067
|
var import_react_native = require("react-native");
|
|
1066
1068
|
var import_pixi = require("pixi.js");
|
|
1067
|
-
var validImageExtensions = [".png", ".jpg", ".jpeg", ".webp", ".gif"];
|
|
1069
|
+
var validImageExtensions = [".png", ".jpg", ".jpeg", ".webp", ".avif", ".gif"];
|
|
1070
|
+
var validImageMIMEs = [
|
|
1071
|
+
"image/png",
|
|
1072
|
+
"image/jpg",
|
|
1073
|
+
"image/jpeg",
|
|
1074
|
+
"image/webp",
|
|
1075
|
+
"image/avif",
|
|
1076
|
+
"image/gif"
|
|
1077
|
+
];
|
|
1068
1078
|
var MODULE_PREFIX = "__expo_module_";
|
|
1079
|
+
var URL_PREFIX = "__expo_url_";
|
|
1069
1080
|
var moduleIdRegistry = /* @__PURE__ */ new Map();
|
|
1081
|
+
var urlRegistry = /* @__PURE__ */ new Map();
|
|
1082
|
+
var urlCounter = 0;
|
|
1083
|
+
function registerModuleId(moduleId) {
|
|
1084
|
+
const key = `${MODULE_PREFIX}${moduleId}`;
|
|
1085
|
+
moduleIdRegistry.set(key, moduleId);
|
|
1086
|
+
return key;
|
|
1087
|
+
}
|
|
1088
|
+
function registerAssetUrl(url) {
|
|
1089
|
+
const key = `${URL_PREFIX}${urlCounter++}`;
|
|
1090
|
+
urlRegistry.set(key, url);
|
|
1091
|
+
return key;
|
|
1092
|
+
}
|
|
1070
1093
|
function getExtension(url) {
|
|
1071
1094
|
const cleanUrl = url.split("?")[0].split("#")[0];
|
|
1072
1095
|
const lastDot = cleanUrl.lastIndexOf(".");
|
|
@@ -1082,13 +1105,12 @@ function getImageSize(uri) {
|
|
|
1082
1105
|
});
|
|
1083
1106
|
}
|
|
1084
1107
|
async function loadTexture(source) {
|
|
1085
|
-
const { Assets:
|
|
1108
|
+
const { Assets: Assets3 } = await import("pixi.js");
|
|
1086
1109
|
if (typeof source === "number") {
|
|
1087
|
-
const key =
|
|
1088
|
-
|
|
1089
|
-
return Assets2.load(key);
|
|
1110
|
+
const key = registerModuleId(source);
|
|
1111
|
+
return Assets3.load(key);
|
|
1090
1112
|
}
|
|
1091
|
-
return
|
|
1113
|
+
return Assets3.load(source);
|
|
1092
1114
|
}
|
|
1093
1115
|
var loadExpoAsset = {
|
|
1094
1116
|
extension: {
|
|
@@ -1102,43 +1124,77 @@ var loadExpoAsset = {
|
|
|
1102
1124
|
*/
|
|
1103
1125
|
test(url) {
|
|
1104
1126
|
if (url.startsWith(MODULE_PREFIX)) {
|
|
1127
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") \u2192 true (module)`);
|
|
1128
|
+
return true;
|
|
1129
|
+
}
|
|
1130
|
+
if (url.startsWith(URL_PREFIX)) {
|
|
1131
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") \u2192 true (registered url)`);
|
|
1105
1132
|
return true;
|
|
1106
1133
|
}
|
|
1107
1134
|
if (url.startsWith("file://")) {
|
|
1135
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") \u2192 true (file)`);
|
|
1108
1136
|
return true;
|
|
1109
1137
|
}
|
|
1138
|
+
if (url.startsWith("data:")) {
|
|
1139
|
+
const result2 = validImageMIMEs.some((mime) => url.startsWith(`data:${mime}`));
|
|
1140
|
+
if (__DEV__)
|
|
1141
|
+
console.log(`[loadExpoAsset] test("${url.slice(0, 40)}...") \u2192 ${result2} (data url)`);
|
|
1142
|
+
return result2;
|
|
1143
|
+
}
|
|
1110
1144
|
const ext = getExtension(url);
|
|
1111
|
-
|
|
1145
|
+
const result = validImageExtensions.includes(ext);
|
|
1146
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") ext="${ext}" \u2192 ${result}`);
|
|
1147
|
+
return result;
|
|
1112
1148
|
},
|
|
1113
1149
|
/**
|
|
1114
1150
|
* Load an asset and create a PixiJS Texture
|
|
1115
1151
|
*/
|
|
1116
1152
|
async load(url, _asset) {
|
|
1117
|
-
let expoAsset;
|
|
1118
1153
|
try {
|
|
1119
|
-
if (
|
|
1120
|
-
|
|
1154
|
+
if (__DEV__) {
|
|
1155
|
+
console.log(`[loadExpoAsset] Loading: ${url}`);
|
|
1156
|
+
}
|
|
1157
|
+
let localUri;
|
|
1158
|
+
let width;
|
|
1159
|
+
let height;
|
|
1160
|
+
const resolvedUrl = url.startsWith(URL_PREFIX) ? urlRegistry.get(url) ?? url : url;
|
|
1161
|
+
if (url.startsWith(URL_PREFIX) && !urlRegistry.has(url)) {
|
|
1162
|
+
throw new Error(`URL not found in registry for key: ${url}`);
|
|
1163
|
+
}
|
|
1164
|
+
if (resolvedUrl.startsWith(MODULE_PREFIX)) {
|
|
1165
|
+
const moduleId = moduleIdRegistry.get(resolvedUrl);
|
|
1121
1166
|
if (moduleId === void 0) {
|
|
1122
|
-
throw new Error(`Module ID not found in registry for key: ${
|
|
1167
|
+
throw new Error(`Module ID not found in registry for key: ${resolvedUrl}`);
|
|
1123
1168
|
}
|
|
1124
|
-
expoAsset = import_expo_asset.Asset.fromModule(moduleId);
|
|
1125
|
-
|
|
1126
|
-
|
|
1169
|
+
const expoAsset = import_expo_asset.Asset.fromModule(moduleId);
|
|
1170
|
+
await expoAsset.downloadAsync();
|
|
1171
|
+
localUri = expoAsset.localUri || expoAsset.uri;
|
|
1172
|
+
width = expoAsset.width ?? void 0;
|
|
1173
|
+
height = expoAsset.height ?? void 0;
|
|
1174
|
+
} else if (isRemoteUrl(resolvedUrl)) {
|
|
1175
|
+
const expoAsset = import_expo_asset.Asset.fromURI(resolvedUrl);
|
|
1176
|
+
await expoAsset.downloadAsync();
|
|
1177
|
+
localUri = expoAsset.localUri || expoAsset.uri;
|
|
1178
|
+
width = expoAsset.width ?? void 0;
|
|
1179
|
+
height = expoAsset.height ?? void 0;
|
|
1127
1180
|
} else {
|
|
1128
|
-
expoAsset = import_expo_asset.Asset.fromURI(
|
|
1181
|
+
const expoAsset = import_expo_asset.Asset.fromURI(resolvedUrl);
|
|
1182
|
+
await expoAsset.downloadAsync();
|
|
1183
|
+
localUri = expoAsset.localUri || expoAsset.uri;
|
|
1184
|
+
width = expoAsset.width ?? void 0;
|
|
1185
|
+
height = expoAsset.height ?? void 0;
|
|
1129
1186
|
}
|
|
1130
|
-
await expoAsset.downloadAsync();
|
|
1131
|
-
const localUri = expoAsset.localUri || expoAsset.uri;
|
|
1132
1187
|
if (!localUri) {
|
|
1133
1188
|
throw new Error(`Failed to get local URI for asset: ${url}`);
|
|
1134
1189
|
}
|
|
1135
|
-
let width = expoAsset.width;
|
|
1136
|
-
let height = expoAsset.height;
|
|
1137
1190
|
if (!width || !height) {
|
|
1138
1191
|
const size = await getImageSize(localUri);
|
|
1139
1192
|
width = size.width;
|
|
1140
1193
|
height = size.height;
|
|
1141
1194
|
}
|
|
1195
|
+
if (__DEV__) {
|
|
1196
|
+
console.log(`[loadExpoAsset] Resolved: ${localUri} (${width}x${height})`);
|
|
1197
|
+
}
|
|
1142
1198
|
const MockImage = globalThis.HTMLImageElement;
|
|
1143
1199
|
const img = new MockImage();
|
|
1144
1200
|
img.width = width;
|
|
@@ -1156,7 +1212,7 @@ var loadExpoAsset = {
|
|
|
1156
1212
|
});
|
|
1157
1213
|
return new import_pixi.Texture({ source });
|
|
1158
1214
|
} catch (error) {
|
|
1159
|
-
console.error(`Failed to load
|
|
1215
|
+
console.error(`[loadExpoAsset] Failed to load: ${url}`, error);
|
|
1160
1216
|
throw error;
|
|
1161
1217
|
}
|
|
1162
1218
|
},
|
|
@@ -1242,6 +1298,54 @@ var loadExpoFont = {
|
|
|
1242
1298
|
}
|
|
1243
1299
|
};
|
|
1244
1300
|
|
|
1301
|
+
// src/adapter/expoManifest.ts
|
|
1302
|
+
function resolveExpoSrc(src) {
|
|
1303
|
+
if (typeof src === "number") {
|
|
1304
|
+
return registerModuleId(src);
|
|
1305
|
+
}
|
|
1306
|
+
return registerAssetUrl(src);
|
|
1307
|
+
}
|
|
1308
|
+
function resolveExpoSrcArray(src) {
|
|
1309
|
+
if (Array.isArray(src)) {
|
|
1310
|
+
return src.map(resolveExpoSrc);
|
|
1311
|
+
}
|
|
1312
|
+
return resolveExpoSrc(src);
|
|
1313
|
+
}
|
|
1314
|
+
function resolveExpoAsset(asset) {
|
|
1315
|
+
const { src, ...rest } = asset;
|
|
1316
|
+
if (src === void 0) {
|
|
1317
|
+
return rest;
|
|
1318
|
+
}
|
|
1319
|
+
return {
|
|
1320
|
+
...rest,
|
|
1321
|
+
src: resolveExpoSrcArray(src)
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
function createExpoBundle(assets) {
|
|
1325
|
+
if (Array.isArray(assets)) {
|
|
1326
|
+
return assets.map(resolveExpoAsset);
|
|
1327
|
+
}
|
|
1328
|
+
const result = {};
|
|
1329
|
+
for (const [key, value] of Object.entries(assets)) {
|
|
1330
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
1331
|
+
result[key] = resolveExpoSrc(value);
|
|
1332
|
+
} else if (Array.isArray(value)) {
|
|
1333
|
+
result[key] = value.map(resolveExpoSrc);
|
|
1334
|
+
} else {
|
|
1335
|
+
result[key] = resolveExpoAsset(value);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
return result;
|
|
1339
|
+
}
|
|
1340
|
+
function createExpoManifest(manifest) {
|
|
1341
|
+
return {
|
|
1342
|
+
bundles: manifest.bundles.map((bundle) => ({
|
|
1343
|
+
name: bundle.name,
|
|
1344
|
+
assets: createExpoBundle(bundle.assets)
|
|
1345
|
+
}))
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1245
1349
|
// src/components/PixiView.tsx
|
|
1246
1350
|
var import_react = require("react");
|
|
1247
1351
|
var import_react_native3 = require("react-native");
|
|
@@ -1593,8 +1697,22 @@ var styles = import_react_native3.StyleSheet.create({
|
|
|
1593
1697
|
// src/index.ts
|
|
1594
1698
|
var import_pixi5 = require("pixi.js");
|
|
1595
1699
|
import_pixi4.DOMAdapter.set(ExpoAdapter);
|
|
1700
|
+
import_pixi4.extensions.remove(import_pixi4.loadTextures);
|
|
1596
1701
|
import_pixi4.extensions.add(loadExpoAsset);
|
|
1597
1702
|
import_pixi4.extensions.add(loadExpoFont);
|
|
1703
|
+
var _originalLoad = import_pixi4.Assets.load.bind(import_pixi4.Assets);
|
|
1704
|
+
import_pixi4.Assets.load = function patchedLoad(urls, onProgress) {
|
|
1705
|
+
if (typeof urls === "number") {
|
|
1706
|
+
return _originalLoad(registerModuleId(urls), onProgress);
|
|
1707
|
+
}
|
|
1708
|
+
if (Array.isArray(urls)) {
|
|
1709
|
+
const resolved = urls.map((u) => typeof u === "number" ? registerModuleId(u) : u);
|
|
1710
|
+
return _originalLoad(resolved, onProgress).then((record) => {
|
|
1711
|
+
return resolved.map((key) => record[key]);
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
return _originalLoad(urls, onProgress);
|
|
1715
|
+
};
|
|
1598
1716
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1599
1717
|
0 && (module.exports = {
|
|
1600
1718
|
AnimatedSprite,
|
|
@@ -1639,6 +1757,8 @@ import_pixi4.extensions.add(loadExpoFont);
|
|
|
1639
1757
|
clearActiveContext,
|
|
1640
1758
|
clearTouchTracking,
|
|
1641
1759
|
convertTouchToPointerEvents,
|
|
1760
|
+
createExpoBundle,
|
|
1761
|
+
createExpoManifest,
|
|
1642
1762
|
extensions,
|
|
1643
1763
|
getActiveCanvas,
|
|
1644
1764
|
getActiveGL,
|
|
@@ -1646,6 +1766,7 @@ import_pixi4.extensions.add(loadExpoFont);
|
|
|
1646
1766
|
loadExpoAsset,
|
|
1647
1767
|
loadExpoFont,
|
|
1648
1768
|
loadTexture,
|
|
1769
|
+
resolveExpoAsset,
|
|
1649
1770
|
setActiveGLContext
|
|
1650
1771
|
});
|
|
1651
1772
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -215,7 +215,6 @@ function windowRemoveEventListener(type, listener, _options) {
|
|
|
215
215
|
function dispatchWindowEvent(event) {
|
|
216
216
|
const listeners = windowListeners.get(event.type);
|
|
217
217
|
if (__DEV__) {
|
|
218
|
-
console.log(`[Window] dispatchEvent: ${event.type}, listeners: ${listeners?.size ?? 0}`);
|
|
219
218
|
}
|
|
220
219
|
if (!listeners || listeners.size === 0) {
|
|
221
220
|
return false;
|
|
@@ -973,15 +972,36 @@ var ExpoAdapter = {
|
|
|
973
972
|
};
|
|
974
973
|
|
|
975
974
|
// src/index.ts
|
|
976
|
-
import { DOMAdapter, extensions } from "pixi.js";
|
|
975
|
+
import { DOMAdapter, extensions, loadTextures, Assets } from "pixi.js";
|
|
977
976
|
|
|
978
977
|
// src/adapter/loadExpoAsset.ts
|
|
979
978
|
import { Asset } from "expo-asset";
|
|
980
979
|
import { Image } from "react-native";
|
|
981
980
|
import { ExtensionType, Texture, ImageSource, LoaderParserPriority } from "pixi.js";
|
|
982
|
-
var validImageExtensions = [".png", ".jpg", ".jpeg", ".webp", ".gif"];
|
|
981
|
+
var validImageExtensions = [".png", ".jpg", ".jpeg", ".webp", ".avif", ".gif"];
|
|
982
|
+
var validImageMIMEs = [
|
|
983
|
+
"image/png",
|
|
984
|
+
"image/jpg",
|
|
985
|
+
"image/jpeg",
|
|
986
|
+
"image/webp",
|
|
987
|
+
"image/avif",
|
|
988
|
+
"image/gif"
|
|
989
|
+
];
|
|
983
990
|
var MODULE_PREFIX = "__expo_module_";
|
|
991
|
+
var URL_PREFIX = "__expo_url_";
|
|
984
992
|
var moduleIdRegistry = /* @__PURE__ */ new Map();
|
|
993
|
+
var urlRegistry = /* @__PURE__ */ new Map();
|
|
994
|
+
var urlCounter = 0;
|
|
995
|
+
function registerModuleId(moduleId) {
|
|
996
|
+
const key = `${MODULE_PREFIX}${moduleId}`;
|
|
997
|
+
moduleIdRegistry.set(key, moduleId);
|
|
998
|
+
return key;
|
|
999
|
+
}
|
|
1000
|
+
function registerAssetUrl(url) {
|
|
1001
|
+
const key = `${URL_PREFIX}${urlCounter++}`;
|
|
1002
|
+
urlRegistry.set(key, url);
|
|
1003
|
+
return key;
|
|
1004
|
+
}
|
|
985
1005
|
function getExtension(url) {
|
|
986
1006
|
const cleanUrl = url.split("?")[0].split("#")[0];
|
|
987
1007
|
const lastDot = cleanUrl.lastIndexOf(".");
|
|
@@ -997,13 +1017,12 @@ function getImageSize(uri) {
|
|
|
997
1017
|
});
|
|
998
1018
|
}
|
|
999
1019
|
async function loadTexture(source) {
|
|
1000
|
-
const { Assets:
|
|
1020
|
+
const { Assets: Assets3 } = await import("pixi.js");
|
|
1001
1021
|
if (typeof source === "number") {
|
|
1002
|
-
const key =
|
|
1003
|
-
|
|
1004
|
-
return Assets2.load(key);
|
|
1022
|
+
const key = registerModuleId(source);
|
|
1023
|
+
return Assets3.load(key);
|
|
1005
1024
|
}
|
|
1006
|
-
return
|
|
1025
|
+
return Assets3.load(source);
|
|
1007
1026
|
}
|
|
1008
1027
|
var loadExpoAsset = {
|
|
1009
1028
|
extension: {
|
|
@@ -1017,43 +1036,77 @@ var loadExpoAsset = {
|
|
|
1017
1036
|
*/
|
|
1018
1037
|
test(url) {
|
|
1019
1038
|
if (url.startsWith(MODULE_PREFIX)) {
|
|
1039
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") \u2192 true (module)`);
|
|
1040
|
+
return true;
|
|
1041
|
+
}
|
|
1042
|
+
if (url.startsWith(URL_PREFIX)) {
|
|
1043
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") \u2192 true (registered url)`);
|
|
1020
1044
|
return true;
|
|
1021
1045
|
}
|
|
1022
1046
|
if (url.startsWith("file://")) {
|
|
1047
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") \u2192 true (file)`);
|
|
1023
1048
|
return true;
|
|
1024
1049
|
}
|
|
1050
|
+
if (url.startsWith("data:")) {
|
|
1051
|
+
const result2 = validImageMIMEs.some((mime) => url.startsWith(`data:${mime}`));
|
|
1052
|
+
if (__DEV__)
|
|
1053
|
+
console.log(`[loadExpoAsset] test("${url.slice(0, 40)}...") \u2192 ${result2} (data url)`);
|
|
1054
|
+
return result2;
|
|
1055
|
+
}
|
|
1025
1056
|
const ext = getExtension(url);
|
|
1026
|
-
|
|
1057
|
+
const result = validImageExtensions.includes(ext);
|
|
1058
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") ext="${ext}" \u2192 ${result}`);
|
|
1059
|
+
return result;
|
|
1027
1060
|
},
|
|
1028
1061
|
/**
|
|
1029
1062
|
* Load an asset and create a PixiJS Texture
|
|
1030
1063
|
*/
|
|
1031
1064
|
async load(url, _asset) {
|
|
1032
|
-
let expoAsset;
|
|
1033
1065
|
try {
|
|
1034
|
-
if (
|
|
1035
|
-
|
|
1066
|
+
if (__DEV__) {
|
|
1067
|
+
console.log(`[loadExpoAsset] Loading: ${url}`);
|
|
1068
|
+
}
|
|
1069
|
+
let localUri;
|
|
1070
|
+
let width;
|
|
1071
|
+
let height;
|
|
1072
|
+
const resolvedUrl = url.startsWith(URL_PREFIX) ? urlRegistry.get(url) ?? url : url;
|
|
1073
|
+
if (url.startsWith(URL_PREFIX) && !urlRegistry.has(url)) {
|
|
1074
|
+
throw new Error(`URL not found in registry for key: ${url}`);
|
|
1075
|
+
}
|
|
1076
|
+
if (resolvedUrl.startsWith(MODULE_PREFIX)) {
|
|
1077
|
+
const moduleId = moduleIdRegistry.get(resolvedUrl);
|
|
1036
1078
|
if (moduleId === void 0) {
|
|
1037
|
-
throw new Error(`Module ID not found in registry for key: ${
|
|
1079
|
+
throw new Error(`Module ID not found in registry for key: ${resolvedUrl}`);
|
|
1038
1080
|
}
|
|
1039
|
-
expoAsset = Asset.fromModule(moduleId);
|
|
1040
|
-
|
|
1041
|
-
|
|
1081
|
+
const expoAsset = Asset.fromModule(moduleId);
|
|
1082
|
+
await expoAsset.downloadAsync();
|
|
1083
|
+
localUri = expoAsset.localUri || expoAsset.uri;
|
|
1084
|
+
width = expoAsset.width ?? void 0;
|
|
1085
|
+
height = expoAsset.height ?? void 0;
|
|
1086
|
+
} else if (isRemoteUrl(resolvedUrl)) {
|
|
1087
|
+
const expoAsset = Asset.fromURI(resolvedUrl);
|
|
1088
|
+
await expoAsset.downloadAsync();
|
|
1089
|
+
localUri = expoAsset.localUri || expoAsset.uri;
|
|
1090
|
+
width = expoAsset.width ?? void 0;
|
|
1091
|
+
height = expoAsset.height ?? void 0;
|
|
1042
1092
|
} else {
|
|
1043
|
-
expoAsset = Asset.fromURI(
|
|
1093
|
+
const expoAsset = Asset.fromURI(resolvedUrl);
|
|
1094
|
+
await expoAsset.downloadAsync();
|
|
1095
|
+
localUri = expoAsset.localUri || expoAsset.uri;
|
|
1096
|
+
width = expoAsset.width ?? void 0;
|
|
1097
|
+
height = expoAsset.height ?? void 0;
|
|
1044
1098
|
}
|
|
1045
|
-
await expoAsset.downloadAsync();
|
|
1046
|
-
const localUri = expoAsset.localUri || expoAsset.uri;
|
|
1047
1099
|
if (!localUri) {
|
|
1048
1100
|
throw new Error(`Failed to get local URI for asset: ${url}`);
|
|
1049
1101
|
}
|
|
1050
|
-
let width = expoAsset.width;
|
|
1051
|
-
let height = expoAsset.height;
|
|
1052
1102
|
if (!width || !height) {
|
|
1053
1103
|
const size = await getImageSize(localUri);
|
|
1054
1104
|
width = size.width;
|
|
1055
1105
|
height = size.height;
|
|
1056
1106
|
}
|
|
1107
|
+
if (__DEV__) {
|
|
1108
|
+
console.log(`[loadExpoAsset] Resolved: ${localUri} (${width}x${height})`);
|
|
1109
|
+
}
|
|
1057
1110
|
const MockImage = globalThis.HTMLImageElement;
|
|
1058
1111
|
const img = new MockImage();
|
|
1059
1112
|
img.width = width;
|
|
@@ -1071,7 +1124,7 @@ var loadExpoAsset = {
|
|
|
1071
1124
|
});
|
|
1072
1125
|
return new Texture({ source });
|
|
1073
1126
|
} catch (error) {
|
|
1074
|
-
console.error(`Failed to load
|
|
1127
|
+
console.error(`[loadExpoAsset] Failed to load: ${url}`, error);
|
|
1075
1128
|
throw error;
|
|
1076
1129
|
}
|
|
1077
1130
|
},
|
|
@@ -1157,6 +1210,54 @@ var loadExpoFont = {
|
|
|
1157
1210
|
}
|
|
1158
1211
|
};
|
|
1159
1212
|
|
|
1213
|
+
// src/adapter/expoManifest.ts
|
|
1214
|
+
function resolveExpoSrc(src) {
|
|
1215
|
+
if (typeof src === "number") {
|
|
1216
|
+
return registerModuleId(src);
|
|
1217
|
+
}
|
|
1218
|
+
return registerAssetUrl(src);
|
|
1219
|
+
}
|
|
1220
|
+
function resolveExpoSrcArray(src) {
|
|
1221
|
+
if (Array.isArray(src)) {
|
|
1222
|
+
return src.map(resolveExpoSrc);
|
|
1223
|
+
}
|
|
1224
|
+
return resolveExpoSrc(src);
|
|
1225
|
+
}
|
|
1226
|
+
function resolveExpoAsset(asset) {
|
|
1227
|
+
const { src, ...rest } = asset;
|
|
1228
|
+
if (src === void 0) {
|
|
1229
|
+
return rest;
|
|
1230
|
+
}
|
|
1231
|
+
return {
|
|
1232
|
+
...rest,
|
|
1233
|
+
src: resolveExpoSrcArray(src)
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
function createExpoBundle(assets) {
|
|
1237
|
+
if (Array.isArray(assets)) {
|
|
1238
|
+
return assets.map(resolveExpoAsset);
|
|
1239
|
+
}
|
|
1240
|
+
const result = {};
|
|
1241
|
+
for (const [key, value] of Object.entries(assets)) {
|
|
1242
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
1243
|
+
result[key] = resolveExpoSrc(value);
|
|
1244
|
+
} else if (Array.isArray(value)) {
|
|
1245
|
+
result[key] = value.map(resolveExpoSrc);
|
|
1246
|
+
} else {
|
|
1247
|
+
result[key] = resolveExpoAsset(value);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
return result;
|
|
1251
|
+
}
|
|
1252
|
+
function createExpoManifest(manifest) {
|
|
1253
|
+
return {
|
|
1254
|
+
bundles: manifest.bundles.map((bundle) => ({
|
|
1255
|
+
name: bundle.name,
|
|
1256
|
+
assets: createExpoBundle(bundle.assets)
|
|
1257
|
+
}))
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1160
1261
|
// src/components/PixiView.tsx
|
|
1161
1262
|
import { useCallback, useRef, useEffect, useImperativeHandle, forwardRef } from "react";
|
|
1162
1263
|
import {
|
|
@@ -1524,7 +1625,7 @@ import {
|
|
|
1524
1625
|
TextureSource,
|
|
1525
1626
|
Spritesheet,
|
|
1526
1627
|
RenderTexture,
|
|
1527
|
-
Assets,
|
|
1628
|
+
Assets as Assets2,
|
|
1528
1629
|
Matrix,
|
|
1529
1630
|
Point,
|
|
1530
1631
|
ObservablePoint,
|
|
@@ -1550,12 +1651,26 @@ import {
|
|
|
1550
1651
|
DOMAdapter as DOMAdapter2
|
|
1551
1652
|
} from "pixi.js";
|
|
1552
1653
|
DOMAdapter.set(ExpoAdapter);
|
|
1654
|
+
extensions.remove(loadTextures);
|
|
1553
1655
|
extensions.add(loadExpoAsset);
|
|
1554
1656
|
extensions.add(loadExpoFont);
|
|
1657
|
+
var _originalLoad = Assets.load.bind(Assets);
|
|
1658
|
+
Assets.load = function patchedLoad(urls, onProgress) {
|
|
1659
|
+
if (typeof urls === "number") {
|
|
1660
|
+
return _originalLoad(registerModuleId(urls), onProgress);
|
|
1661
|
+
}
|
|
1662
|
+
if (Array.isArray(urls)) {
|
|
1663
|
+
const resolved = urls.map((u) => typeof u === "number" ? registerModuleId(u) : u);
|
|
1664
|
+
return _originalLoad(resolved, onProgress).then((record) => {
|
|
1665
|
+
return resolved.map((key) => record[key]);
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
return _originalLoad(urls, onProgress);
|
|
1669
|
+
};
|
|
1555
1670
|
export {
|
|
1556
1671
|
AnimatedSprite,
|
|
1557
1672
|
Application2 as Application,
|
|
1558
|
-
Assets,
|
|
1673
|
+
Assets2 as Assets,
|
|
1559
1674
|
Batcher,
|
|
1560
1675
|
BitmapText,
|
|
1561
1676
|
BlurFilter,
|
|
@@ -1595,6 +1710,8 @@ export {
|
|
|
1595
1710
|
clearActiveContext,
|
|
1596
1711
|
clearTouchTracking,
|
|
1597
1712
|
convertTouchToPointerEvents,
|
|
1713
|
+
createExpoBundle,
|
|
1714
|
+
createExpoManifest,
|
|
1598
1715
|
extensions2 as extensions,
|
|
1599
1716
|
getActiveCanvas,
|
|
1600
1717
|
getActiveGL,
|
|
@@ -1602,6 +1719,7 @@ export {
|
|
|
1602
1719
|
loadExpoAsset,
|
|
1603
1720
|
loadExpoFont,
|
|
1604
1721
|
loadTexture,
|
|
1722
|
+
resolveExpoAsset,
|
|
1605
1723
|
setActiveGLContext
|
|
1606
1724
|
};
|
|
1607
1725
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@penabt/pixi-expo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "PixiJS v8 adapter for React Native Expo. Enables hardware-accelerated 2D graphics using expo-gl WebGL context.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
32
32
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
33
33
|
"prepublishOnly": "npm run build",
|
|
34
|
-
"clean": "rm -rf
|
|
34
|
+
"clean": "rm -rf dist",
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
|
36
36
|
"lint": "eslint src --ext .ts,.tsx",
|
|
37
37
|
"format": "prettier --write ."
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PixiJS Manifest & Bundle utilities for Expo.
|
|
3
|
+
*
|
|
4
|
+
* Transforms Expo-flavored manifests (where `src` can be a require() numeric ID)
|
|
5
|
+
* into standard PixiJS manifests (where `src` is always a string).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { UnresolvedAsset, AssetsBundle, AssetsManifest, ArrayOr } from 'pixi.js';
|
|
9
|
+
import { registerModuleId, registerAssetUrl } from './loadExpoAsset';
|
|
10
|
+
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// TYPES
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
/** An asset source that accepts Expo require() numeric IDs alongside strings. */
|
|
16
|
+
export type ExpoAssetSrc = number | string;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* An unresolved asset that accepts require() IDs in src.
|
|
20
|
+
* Mirrors PixiJS UnresolvedAsset but src can include numeric module IDs.
|
|
21
|
+
*/
|
|
22
|
+
export interface ExpoUnresolvedAsset<T = any> {
|
|
23
|
+
alias?: ArrayOr<string>;
|
|
24
|
+
src?: ExpoAssetSrc | ExpoAssetSrc[];
|
|
25
|
+
data?: T;
|
|
26
|
+
format?: string;
|
|
27
|
+
parser?: string;
|
|
28
|
+
[key: string]: any;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** A bundle that accepts Expo require() IDs. */
|
|
32
|
+
export interface ExpoAssetsBundle {
|
|
33
|
+
name: string;
|
|
34
|
+
assets:
|
|
35
|
+
| ExpoUnresolvedAsset[]
|
|
36
|
+
| Record<string, ExpoAssetSrc | ExpoAssetSrc[] | ExpoUnresolvedAsset>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A manifest that accepts Expo require() IDs. */
|
|
40
|
+
export interface ExpoAssetsManifest {
|
|
41
|
+
bundles: ExpoAssetsBundle[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// =============================================================================
|
|
45
|
+
// INTERNAL HELPERS
|
|
46
|
+
// =============================================================================
|
|
47
|
+
|
|
48
|
+
function resolveExpoSrc(src: ExpoAssetSrc): string {
|
|
49
|
+
if (typeof src === 'number') {
|
|
50
|
+
return registerModuleId(src);
|
|
51
|
+
}
|
|
52
|
+
// Wrap string URLs with a registry key to prevent PixiJS's resolver
|
|
53
|
+
// from routing them to built-in browser-dependent parsers
|
|
54
|
+
return registerAssetUrl(src);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolveExpoSrcArray(src: ExpoAssetSrc | ExpoAssetSrc[]): string | string[] {
|
|
58
|
+
if (Array.isArray(src)) {
|
|
59
|
+
return src.map(resolveExpoSrc);
|
|
60
|
+
}
|
|
61
|
+
return resolveExpoSrc(src);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// =============================================================================
|
|
65
|
+
// PUBLIC API
|
|
66
|
+
// =============================================================================
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Transform a single Expo asset entry into a standard PixiJS UnresolvedAsset.
|
|
70
|
+
* Numeric require() IDs are registered and converted to string keys.
|
|
71
|
+
*/
|
|
72
|
+
export function resolveExpoAsset<T = any>(asset: ExpoUnresolvedAsset<T>): UnresolvedAsset<T> {
|
|
73
|
+
const { src, ...rest } = asset;
|
|
74
|
+
if (src === undefined) {
|
|
75
|
+
return rest as UnresolvedAsset<T>;
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
...rest,
|
|
79
|
+
src: resolveExpoSrcArray(src),
|
|
80
|
+
} as UnresolvedAsset<T>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Transform bundle assets for use with `Assets.addBundle()`.
|
|
85
|
+
* Handles both array and Record formats.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* Assets.addBundle('animals', createExpoBundle([
|
|
90
|
+
* { alias: 'bunny', src: require('./assets/bunny.png') },
|
|
91
|
+
* { alias: 'chicken', src: 'https://example.com/chicken.png' },
|
|
92
|
+
* ]));
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export function createExpoBundle(assets: ExpoAssetsBundle['assets']): AssetsBundle['assets'] {
|
|
96
|
+
if (Array.isArray(assets)) {
|
|
97
|
+
return assets.map(resolveExpoAsset);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Record format: Record<string, ExpoAssetSrc | ExpoAssetSrc[] | ExpoUnresolvedAsset>
|
|
101
|
+
const result: Record<string, ArrayOr<string> | UnresolvedAsset> = {};
|
|
102
|
+
for (const [key, value] of Object.entries(assets)) {
|
|
103
|
+
if (typeof value === 'number' || typeof value === 'string') {
|
|
104
|
+
result[key] = resolveExpoSrc(value);
|
|
105
|
+
} else if (Array.isArray(value)) {
|
|
106
|
+
result[key] = value.map(resolveExpoSrc);
|
|
107
|
+
} else {
|
|
108
|
+
result[key] = resolveExpoAsset(value);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Transform an Expo-flavored manifest into a standard PixiJS AssetsManifest.
|
|
116
|
+
* Numeric require() IDs in `src` fields are registered and converted to string keys.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* const manifest = createExpoManifest({
|
|
121
|
+
* bundles: [
|
|
122
|
+
* {
|
|
123
|
+
* name: 'game-screen',
|
|
124
|
+
* assets: [
|
|
125
|
+
* { alias: 'character', src: require('./assets/robot.png') },
|
|
126
|
+
* { alias: 'enemy', src: 'https://example.com/bad-guy.png' },
|
|
127
|
+
* ],
|
|
128
|
+
* },
|
|
129
|
+
* ],
|
|
130
|
+
* });
|
|
131
|
+
*
|
|
132
|
+
* await Assets.init({ manifest });
|
|
133
|
+
* const assets = await Assets.loadBundle('game-screen');
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export function createExpoManifest(manifest: ExpoAssetsManifest): AssetsManifest {
|
|
137
|
+
return {
|
|
138
|
+
bundles: manifest.bundles.map((bundle) => ({
|
|
139
|
+
name: bundle.name,
|
|
140
|
+
assets: createExpoBundle(bundle.assets),
|
|
141
|
+
})),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -14,9 +14,18 @@ import { ExtensionType, Texture, ImageSource, LoaderParserPriority } from 'pixi.
|
|
|
14
14
|
|
|
15
15
|
import type { LoaderParser, ResolvedAsset } from 'pixi.js';
|
|
16
16
|
|
|
17
|
-
const validImageExtensions = ['.png', '.jpg', '.jpeg', '.webp', '.gif'];
|
|
17
|
+
const validImageExtensions = ['.png', '.jpg', '.jpeg', '.webp', '.avif', '.gif'];
|
|
18
|
+
const validImageMIMEs = [
|
|
19
|
+
'image/png',
|
|
20
|
+
'image/jpg',
|
|
21
|
+
'image/jpeg',
|
|
22
|
+
'image/webp',
|
|
23
|
+
'image/avif',
|
|
24
|
+
'image/gif',
|
|
25
|
+
];
|
|
18
26
|
|
|
19
27
|
const MODULE_PREFIX = '__expo_module_';
|
|
28
|
+
const URL_PREFIX = '__expo_url_';
|
|
20
29
|
|
|
21
30
|
/**
|
|
22
31
|
* Registry mapping stringified keys to original require() module IDs.
|
|
@@ -25,6 +34,37 @@ const MODULE_PREFIX = '__expo_module_';
|
|
|
25
34
|
*/
|
|
26
35
|
const moduleIdRegistry = new Map<string, number>();
|
|
27
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Registry mapping stringified keys to original string URLs.
|
|
39
|
+
* PixiJS's Resolver auto-assigns built-in parsers for known extensions (e.g. .png → loadTextures),
|
|
40
|
+
* bypassing our custom loader's test(). Wrapping URLs with a prefix ensures they always
|
|
41
|
+
* route through loadExpoAsset.
|
|
42
|
+
*/
|
|
43
|
+
const urlRegistry = new Map<string, string>();
|
|
44
|
+
let urlCounter = 0;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Register a require() module ID in the registry and return the string key.
|
|
48
|
+
* This key can be used as a `src` in PixiJS Assets system and will be
|
|
49
|
+
* intercepted by the loadExpoAsset loader.
|
|
50
|
+
*/
|
|
51
|
+
export function registerModuleId(moduleId: number): string {
|
|
52
|
+
const key = `${MODULE_PREFIX}${moduleId}`;
|
|
53
|
+
moduleIdRegistry.set(key, moduleId);
|
|
54
|
+
return key;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Register a string URL in the registry and return a prefixed key.
|
|
59
|
+
* This prevents PixiJS's resolver from routing the URL to built-in
|
|
60
|
+
* browser-dependent parsers that don't work in React Native.
|
|
61
|
+
*/
|
|
62
|
+
export function registerAssetUrl(url: string): string {
|
|
63
|
+
const key = `${URL_PREFIX}${urlCounter++}`;
|
|
64
|
+
urlRegistry.set(key, url);
|
|
65
|
+
return key;
|
|
66
|
+
}
|
|
67
|
+
|
|
28
68
|
/**
|
|
29
69
|
* Get file extension from a URL or path
|
|
30
70
|
*/
|
|
@@ -69,8 +109,7 @@ export async function loadTexture(source: number | string): Promise<Texture> {
|
|
|
69
109
|
const { Assets } = await import('pixi.js');
|
|
70
110
|
|
|
71
111
|
if (typeof source === 'number') {
|
|
72
|
-
const key =
|
|
73
|
-
moduleIdRegistry.set(key, source);
|
|
112
|
+
const key = registerModuleId(source);
|
|
74
113
|
return Assets.load(key);
|
|
75
114
|
}
|
|
76
115
|
|
|
@@ -100,61 +139,105 @@ export const loadExpoAsset = {
|
|
|
100
139
|
test(url: string): boolean {
|
|
101
140
|
// Handle registered module IDs (from loadTexture helper)
|
|
102
141
|
if (url.startsWith(MODULE_PREFIX)) {
|
|
142
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") → true (module)`);
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Handle registered URL keys (from createExpoManifest / registerAssetUrl)
|
|
147
|
+
if (url.startsWith(URL_PREFIX)) {
|
|
148
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") → true (registered url)`);
|
|
103
149
|
return true;
|
|
104
150
|
}
|
|
105
151
|
|
|
106
152
|
// Handle local file URIs
|
|
107
153
|
if (url.startsWith('file://')) {
|
|
154
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") → true (file)`);
|
|
108
155
|
return true;
|
|
109
156
|
}
|
|
110
157
|
|
|
158
|
+
// Handle data URLs with valid image MIME types
|
|
159
|
+
if (url.startsWith('data:')) {
|
|
160
|
+
const result = validImageMIMEs.some((mime) => url.startsWith(`data:${mime}`));
|
|
161
|
+
if (__DEV__)
|
|
162
|
+
console.log(`[loadExpoAsset] test("${url.slice(0, 40)}...") → ${result} (data url)`);
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
|
|
111
166
|
// Handle URLs with valid image extensions
|
|
112
167
|
const ext = getExtension(url);
|
|
168
|
+
const result = validImageExtensions.includes(ext);
|
|
113
169
|
|
|
114
|
-
|
|
170
|
+
if (__DEV__) console.log(`[loadExpoAsset] test("${url}") ext="${ext}" → ${result}`);
|
|
171
|
+
return result;
|
|
115
172
|
},
|
|
116
173
|
|
|
117
174
|
/**
|
|
118
175
|
* Load an asset and create a PixiJS Texture
|
|
119
176
|
*/
|
|
120
177
|
async load(url: string, _asset?: ResolvedAsset): Promise<Texture> {
|
|
121
|
-
let expoAsset: Asset;
|
|
122
|
-
|
|
123
178
|
try {
|
|
124
|
-
if (
|
|
179
|
+
if (__DEV__) {
|
|
180
|
+
console.log(`[loadExpoAsset] Loading: ${url}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let localUri: string;
|
|
184
|
+
let width: number | undefined;
|
|
185
|
+
let height: number | undefined;
|
|
186
|
+
|
|
187
|
+
// Resolve registered URL keys back to original URLs
|
|
188
|
+
const resolvedUrl = url.startsWith(URL_PREFIX) ? (urlRegistry.get(url) ?? url) : url;
|
|
189
|
+
|
|
190
|
+
if (url.startsWith(URL_PREFIX) && !urlRegistry.has(url)) {
|
|
191
|
+
throw new Error(`URL not found in registry for key: ${url}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (resolvedUrl.startsWith(MODULE_PREFIX)) {
|
|
125
195
|
// Recover the original require() module ID from registry
|
|
126
|
-
const moduleId = moduleIdRegistry.get(
|
|
196
|
+
const moduleId = moduleIdRegistry.get(resolvedUrl);
|
|
127
197
|
|
|
128
198
|
if (moduleId === undefined) {
|
|
129
|
-
throw new Error(`Module ID not found in registry for key: ${
|
|
199
|
+
throw new Error(`Module ID not found in registry for key: ${resolvedUrl}`);
|
|
130
200
|
}
|
|
131
201
|
|
|
132
|
-
expoAsset = Asset.fromModule(moduleId);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
202
|
+
const expoAsset = Asset.fromModule(moduleId);
|
|
203
|
+
await expoAsset.downloadAsync();
|
|
204
|
+
|
|
205
|
+
localUri = expoAsset.localUri || expoAsset.uri;
|
|
206
|
+
width = expoAsset.width ?? undefined;
|
|
207
|
+
height = expoAsset.height ?? undefined;
|
|
208
|
+
} else if (isRemoteUrl(resolvedUrl)) {
|
|
209
|
+
// Download remote image to local cache via expo-asset
|
|
210
|
+
const expoAsset = Asset.fromURI(resolvedUrl);
|
|
211
|
+
await expoAsset.downloadAsync();
|
|
138
212
|
|
|
139
|
-
|
|
140
|
-
|
|
213
|
+
localUri = expoAsset.localUri || expoAsset.uri;
|
|
214
|
+
width = expoAsset.width ?? undefined;
|
|
215
|
+
height = expoAsset.height ?? undefined;
|
|
216
|
+
} else {
|
|
217
|
+
// Local file URI
|
|
218
|
+
const expoAsset = Asset.fromURI(resolvedUrl);
|
|
219
|
+
await expoAsset.downloadAsync();
|
|
141
220
|
|
|
142
|
-
|
|
221
|
+
localUri = expoAsset.localUri || expoAsset.uri;
|
|
222
|
+
width = expoAsset.width ?? undefined;
|
|
223
|
+
height = expoAsset.height ?? undefined;
|
|
224
|
+
}
|
|
143
225
|
|
|
144
226
|
if (!localUri) {
|
|
145
227
|
throw new Error(`Failed to get local URI for asset: ${url}`);
|
|
146
228
|
}
|
|
147
229
|
|
|
148
|
-
// Get dimensions
|
|
149
|
-
let width = expoAsset.width;
|
|
150
|
-
let height = expoAsset.height;
|
|
151
|
-
|
|
230
|
+
// Get dimensions via Image.getSize if not already known
|
|
152
231
|
if (!width || !height) {
|
|
153
232
|
const size = await getImageSize(localUri);
|
|
154
233
|
width = size.width;
|
|
155
234
|
height = size.height;
|
|
156
235
|
}
|
|
157
236
|
|
|
237
|
+
if (__DEV__) {
|
|
238
|
+
console.log(`[loadExpoAsset] Resolved: ${localUri} (${width}x${height})`);
|
|
239
|
+
}
|
|
240
|
+
|
|
158
241
|
// Create an HTMLImageElement instance that:
|
|
159
242
|
// 1. Passes ImageSource.test() (instanceof HTMLImageElement)
|
|
160
243
|
// 2. Has `localUri` so expo-gl's patched texImage2D loads the image natively
|
|
@@ -177,7 +260,7 @@ export const loadExpoAsset = {
|
|
|
177
260
|
|
|
178
261
|
return new Texture({ source });
|
|
179
262
|
} catch (error) {
|
|
180
|
-
console.error(`Failed to load
|
|
263
|
+
console.error(`[loadExpoAsset] Failed to load: ${url}`, error);
|
|
181
264
|
throw error;
|
|
182
265
|
}
|
|
183
266
|
},
|
package/src/adapter/polyfills.ts
CHANGED
|
@@ -290,7 +290,7 @@ export function dispatchWindowEvent(event: { type: string; [key: string]: any })
|
|
|
290
290
|
const listeners = windowListeners.get(event.type);
|
|
291
291
|
|
|
292
292
|
if (__DEV__) {
|
|
293
|
-
console.log(`[Window] dispatchEvent: ${event.type}, listeners: ${listeners?.size ?? 0}`);
|
|
293
|
+
//console.log(`[Window] dispatchEvent: ${event.type}, listeners: ${listeners?.size ?? 0}`);
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
if (!listeners || listeners.size === 0) {
|
package/src/index.ts
CHANGED
|
@@ -76,23 +76,47 @@ import { ExpoCanvasElement } from './adapter';
|
|
|
76
76
|
// and configure it to use our custom adapter.
|
|
77
77
|
// =============================================================================
|
|
78
78
|
|
|
79
|
-
import { DOMAdapter, extensions } from 'pixi.js';
|
|
79
|
+
import { DOMAdapter, extensions, loadTextures, Assets } from 'pixi.js';
|
|
80
80
|
|
|
81
81
|
// Set the custom adapter before any PixiJS rendering occurs
|
|
82
82
|
DOMAdapter.set(ExpoAdapter as any);
|
|
83
83
|
|
|
84
84
|
// =============================================================================
|
|
85
85
|
// PHASE 4: ASSET LOADERS
|
|
86
|
-
//
|
|
86
|
+
// Remove PixiJS's built-in texture loader (uses createImageBitmap which doesn't
|
|
87
|
+
// exist in React Native) and register our Expo-compatible loaders.
|
|
87
88
|
// These must be registered after DOMAdapter is configured.
|
|
88
89
|
// =============================================================================
|
|
89
90
|
|
|
90
|
-
import { loadExpoAsset, loadTexture } from './adapter/loadExpoAsset';
|
|
91
|
+
import { loadExpoAsset, loadTexture, registerModuleId } from './adapter/loadExpoAsset';
|
|
91
92
|
import { loadExpoFont } from './adapter/loadExpoFont';
|
|
92
93
|
|
|
94
|
+
extensions.remove(loadTextures);
|
|
93
95
|
extensions.add(loadExpoAsset);
|
|
94
96
|
extensions.add(loadExpoFont);
|
|
95
97
|
|
|
98
|
+
// =============================================================================
|
|
99
|
+
// PHASE 5: PATCH Assets.load TO ACCEPT require() IDs
|
|
100
|
+
// PixiJS's Assets.load() only accepts strings. This patch allows passing
|
|
101
|
+
// numeric require() module IDs directly: Assets.load(require('./img.png'))
|
|
102
|
+
// =============================================================================
|
|
103
|
+
|
|
104
|
+
const _originalLoad = Assets.load.bind(Assets);
|
|
105
|
+
(Assets as any).load = function patchedLoad(urls: any, onProgress?: any): Promise<any> {
|
|
106
|
+
if (typeof urls === 'number') {
|
|
107
|
+
return _originalLoad(registerModuleId(urls), onProgress);
|
|
108
|
+
}
|
|
109
|
+
if (Array.isArray(urls)) {
|
|
110
|
+
const resolved = urls.map((u: any) => (typeof u === 'number' ? registerModuleId(u) : u));
|
|
111
|
+
// Assets.load returns Record<string, T> for arrays. Convert to array so
|
|
112
|
+
// callers can destructure: const [a, b] = await Assets.load([url1, url2])
|
|
113
|
+
return _originalLoad(resolved, onProgress).then((record: any) => {
|
|
114
|
+
return resolved.map((key: string) => record[key]);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return _originalLoad(urls, onProgress);
|
|
118
|
+
};
|
|
119
|
+
|
|
96
120
|
// =============================================================================
|
|
97
121
|
// EXPORTS: ADAPTER UTILITIES
|
|
98
122
|
// Low-level adapter components for advanced use cases.
|
|
@@ -127,6 +151,19 @@ export {
|
|
|
127
151
|
loadExpoFont,
|
|
128
152
|
};
|
|
129
153
|
|
|
154
|
+
// =============================================================================
|
|
155
|
+
// EXPORTS: MANIFEST & BUNDLE UTILITIES
|
|
156
|
+
// Transform Expo-flavored manifests (with require() IDs) for PixiJS Assets.
|
|
157
|
+
// =============================================================================
|
|
158
|
+
|
|
159
|
+
export { createExpoManifest, createExpoBundle, resolveExpoAsset } from './adapter/expoManifest';
|
|
160
|
+
export type {
|
|
161
|
+
ExpoAssetSrc,
|
|
162
|
+
ExpoUnresolvedAsset,
|
|
163
|
+
ExpoAssetsBundle,
|
|
164
|
+
ExpoAssetsManifest,
|
|
165
|
+
} from './adapter/expoManifest';
|
|
166
|
+
|
|
130
167
|
// =============================================================================
|
|
131
168
|
// EXPORTS: REACT COMPONENTS
|
|
132
169
|
// High-level React Native components for easy integration.
|
|
@@ -301,4 +338,10 @@ export type {
|
|
|
301
338
|
FilterOptions,
|
|
302
339
|
/** Renderer type */
|
|
303
340
|
Renderer,
|
|
341
|
+
/** Manifest format for Assets.init() */
|
|
342
|
+
AssetsManifest,
|
|
343
|
+
/** Bundle format within a manifest */
|
|
344
|
+
AssetsBundle,
|
|
345
|
+
/** Unresolved asset entry */
|
|
346
|
+
UnresolvedAsset,
|
|
304
347
|
} from 'pixi.js';
|