@penabt/pixi-expo 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Pena Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # @penabt/pixi-expo
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@penabt/pixi-expo.svg)](https://www.npmjs.com/package/@penabt/pixi-expo)
4
+ [![license](https://img.shields.io/npm/l/@penabt/pixi-expo.svg)](https://github.com/penabt/pixi-expo/blob/main/LICENSE)
5
+
6
+ **PixiJS v8 adapter for React Native Expo.** Enables hardware-accelerated 2D graphics in your Expo applications using the expo-gl WebGL context.
7
+
8
+ ## Features
9
+
10
+ - 🚀 **PixiJS v8 Support** - Full compatibility with the latest PixiJS version
11
+ - 📱 **Expo Integration** - Works seamlessly with Expo managed and bare workflows
12
+ - ⚡ **60 FPS Performance** - Hardware-accelerated WebGL rendering via expo-gl
13
+ - 🎮 **Game Ready** - Perfect for 2D games, animations, and interactive graphics
14
+ - 📦 **Easy Setup** - Drop-in PixiView component with simple API
15
+ - 🔧 **Customizable** - Access to full PixiJS API and expo-gl context
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ # Install the package
21
+ npm install @penabt/pixi-expo
22
+
23
+ # Install peer dependencies
24
+ npx expo install expo-gl expo-asset expo-font pixi.js
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```tsx
30
+ import React from 'react';
31
+ import { View, StyleSheet } from 'react-native';
32
+ import { PixiView, Graphics, Application } from '@penabt/pixi-expo';
33
+
34
+ export default function GameScreen() {
35
+ const handleAppCreate = (app: Application) => {
36
+ // Create a red circle
37
+ const circle = new Graphics()
38
+ .circle(0, 0, 50)
39
+ .fill({ color: 0xff0000 });
40
+
41
+ circle.position.set(200, 300);
42
+ app.stage.addChild(circle);
43
+
44
+ // Animate with the ticker
45
+ app.ticker.add(() => {
46
+ circle.rotation += 0.01;
47
+ });
48
+ };
49
+
50
+ return (
51
+ <View style={styles.container}>
52
+ <PixiView
53
+ style={styles.game}
54
+ backgroundColor={0x1099bb}
55
+ onApplicationCreate={handleAppCreate}
56
+ />
57
+ </View>
58
+ );
59
+ }
60
+
61
+ const styles = StyleSheet.create({
62
+ container: { flex: 1 },
63
+ game: { flex: 1 },
64
+ });
65
+ ```
66
+
67
+ ## API Reference
68
+
69
+ ### PixiView Component
70
+
71
+ The main component for rendering PixiJS content.
72
+
73
+ ```tsx
74
+ <PixiView
75
+ style={ViewStyle} // Container styles
76
+ backgroundColor={0x000000} // Background color (hex)
77
+ resolution={1} // Device pixel ratio
78
+ antialias={true} // Enable antialiasing
79
+ onApplicationCreate={(app) => {}} // Called when app is ready
80
+ onContextCreate={(gl) => {}} // Called when GL context created
81
+ onError={(error) => {}} // Called on initialization error
82
+ />
83
+ ```
84
+
85
+ ### PixiView Ref Handle
86
+
87
+ Access the PixiJS Application imperatively:
88
+
89
+ ```tsx
90
+ const pixiRef = useRef<PixiViewHandle>(null);
91
+
92
+ // Get the application
93
+ const app = pixiRef.current?.getApplication();
94
+
95
+ // Get the stage
96
+ const stage = pixiRef.current?.getStage();
97
+
98
+ // Force render
99
+ pixiRef.current?.render();
100
+
101
+ // Take screenshot
102
+ const base64 = await pixiRef.current?.takeSnapshot();
103
+ ```
104
+
105
+ ### Re-exported from PixiJS
106
+
107
+ For convenience, common PixiJS exports are available directly:
108
+
109
+ ```tsx
110
+ import {
111
+ // Display Objects
112
+ Application,
113
+ Container,
114
+ Sprite,
115
+ Graphics,
116
+ Text,
117
+ TilingSprite,
118
+ AnimatedSprite,
119
+ Mesh,
120
+ NineSliceSprite,
121
+
122
+ // Textures
123
+ Texture,
124
+ RenderTexture,
125
+ Assets,
126
+
127
+ // Geometry
128
+ Matrix,
129
+ Point,
130
+ Rectangle,
131
+ Circle,
132
+ Polygon,
133
+
134
+ // Filters
135
+ Filter,
136
+ BlurFilter,
137
+ ColorMatrixFilter,
138
+
139
+ // Animation
140
+ Ticker,
141
+
142
+ // And more...
143
+ } from '@penabt/pixi-expo';
144
+ ```
145
+
146
+ ## Loading Assets
147
+
148
+ ### Bundled Assets (require)
149
+
150
+ ```tsx
151
+ import { loadTexture, Sprite } from '@penabt/pixi-expo';
152
+
153
+ // Load a bundled image
154
+ const texture = await loadTexture(require('./assets/bunny.png'));
155
+ const sprite = new Sprite(texture);
156
+ ```
157
+
158
+ ### Remote Assets (URL)
159
+
160
+ ```tsx
161
+ // Load from URL
162
+ const texture = await Assets.load('https://example.com/sprite.png');
163
+ ```
164
+
165
+ ## Performance Tips
166
+
167
+ 1. **Use Shared Ticker** - PixiView enables `sharedTicker` by default for optimal performance
168
+
169
+ 2. **Batch Rendering** - Group similar sprites using `ParticleContainer` for many objects
170
+
171
+ 3. **Texture Atlases** - Use spritesheets instead of individual images
172
+
173
+ 4. **Avoid Text Updates** - Cache text objects, don't create new ones every frame
174
+
175
+ 5. **Production Builds** - Run `npx expo run:ios --configuration Release` for best performance
176
+
177
+ ## Limitations
178
+
179
+ - **No Canvas 2D** - expo-gl only supports WebGL, not Canvas 2D context
180
+ - **No HTMLText** - HTML-based text rendering is not available
181
+ - **Font Loading** - Use expo-font for loading custom fonts
182
+
183
+ ## Compatibility
184
+
185
+ | Package | Version |
186
+ |---------|---------|
187
+ | pixi.js | ≥ 8.0.0 |
188
+ | expo | ≥ 50.0.0 |
189
+ | expo-gl | ≥ 14.0.0 |
190
+ | react-native | ≥ 0.73.0 |
191
+
192
+ ## Contributing
193
+
194
+ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
195
+
196
+ ## License
197
+
198
+ MIT © [Pena Team](https://github.com/penabt)
199
+
200
+ ---
201
+
202
+ Made with ❤️ by [Pena Team](https://github.com/penabt)
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@penabt/pixi-expo",
3
+ "version": "0.1.0",
4
+ "description": "PixiJS v8 adapter for React Native Expo. Enables hardware-accelerated 2D graphics using expo-gl WebGL context.",
5
+ "main": "src/index.ts",
6
+ "module": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "source": "src/index.ts",
9
+ "sideEffects": [
10
+ "./src/adapter/polyfills.ts",
11
+ "./lib/adapter/polyfills.js",
12
+ "./lib/adapter/polyfills.mjs"
13
+ ],
14
+ "publishConfig": {
15
+ "main": "lib/index.js",
16
+ "module": "lib/index.mjs",
17
+ "types": "lib/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "import": "./lib/index.mjs",
21
+ "require": "./lib/index.js",
22
+ "types": "./lib/index.d.ts"
23
+ }
24
+ }
25
+ },
26
+ "files": [
27
+ "lib",
28
+ "src",
29
+ "README.md",
30
+ "LICENSE"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
34
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
35
+ "prepublishOnly": "npm run build",
36
+ "clean": "rm -rf lib",
37
+ "typecheck": "tsc --noEmit",
38
+ "lint": "eslint src --ext .ts,.tsx"
39
+ },
40
+ "keywords": [
41
+ "pena",
42
+ "pixi",
43
+ "pixijs",
44
+ "pixi.js",
45
+ "expo",
46
+ "expo-gl",
47
+ "react-native",
48
+ "webgl",
49
+ "2d",
50
+ "graphics",
51
+ "game",
52
+ "animation",
53
+ "canvas",
54
+ "renderer"
55
+ ],
56
+ "author": {
57
+ "name": "Pena Team",
58
+ "url": "https://github.com/penabt"
59
+ },
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "https://github.com/penabt/pixi-expo.git"
63
+ },
64
+ "bugs": {
65
+ "url": "https://github.com/penabt/pixi-expo/issues"
66
+ },
67
+ "homepage": "https://github.com/penabt/pixi-expo#readme",
68
+ "license": "MIT",
69
+ "engines": {
70
+ "node": ">=18.0.0"
71
+ },
72
+ "peerDependencies": {
73
+ "expo": ">=50.0.0",
74
+ "expo-asset": ">=10.0.0",
75
+ "expo-font": ">=12.0.0",
76
+ "expo-gl": ">=14.0.0",
77
+ "pixi.js": ">=8.0.0",
78
+ "react": ">=18.0.0",
79
+ "react-native": ">=0.73.0"
80
+ },
81
+ "peerDependenciesMeta": {
82
+ "expo-asset": {
83
+ "optional": true
84
+ },
85
+ "expo-font": {
86
+ "optional": true
87
+ }
88
+ },
89
+ "dependencies": {
90
+ "@xmldom/xmldom": "^0.8.10"
91
+ },
92
+ "devDependencies": {
93
+ "@types/react": "^19.2.13",
94
+ "@types/react-native": "^0.72.8",
95
+ "tsup": "^8.5.1",
96
+ "typescript": "^5.9.3"
97
+ }
98
+ }
@@ -0,0 +1,343 @@
1
+ /**
2
+ * @fileoverview PixiJS DOMAdapter implementation for expo-gl.
3
+ *
4
+ * This module provides the core adapter that bridges PixiJS's browser-based
5
+ * architecture to React Native's expo-gl WebGL implementation.
6
+ *
7
+ * @module @penabt/pixi-expo/ExpoAdapter
8
+ * @author Pena Team
9
+ * @license MIT
10
+ *
11
+ * @description
12
+ * PixiJS uses a DOMAdapter pattern to abstract browser APIs. This adapter
13
+ * implements that interface for React Native:
14
+ *
15
+ * - createCanvas: Returns ExpoCanvasElement wrapping expo-gl context
16
+ * - createImage: Returns image-like object for texture loading
17
+ * - getWebGLRenderingContext: Returns WebGL constructor
18
+ * - fetch: Handles remote and local asset URLs
19
+ * - parseXML: Uses @xmldom/xmldom for SVG and other XML parsing
20
+ *
21
+ * @example Setting up the adapter
22
+ * ```ts
23
+ * import { ExpoAdapter, setActiveGLContext } from '@penabt/pixi-expo';
24
+ * import { DOMAdapter } from 'pixi.js';
25
+ *
26
+ * // Set adapter before creating Application
27
+ * DOMAdapter.set(ExpoAdapter);
28
+ *
29
+ * // In GLView.onContextCreate:
30
+ * const canvas = setActiveGLContext(gl, width, height);
31
+ * ```
32
+ */
33
+
34
+ import type { ExpoWebGLRenderingContext } from 'expo-gl';
35
+ import { ExpoCanvasElement } from './ExpoCanvasElement';
36
+ import { DOMParser } from '@xmldom/xmldom';
37
+
38
+ // =============================================================================
39
+ // MODULE STATE
40
+ // Tracks the currently active GL context for PixiJS rendering.
41
+ // =============================================================================
42
+
43
+ /** Currently active canvas element wrapper */
44
+ let activeCanvas: ExpoCanvasElement | null = null;
45
+
46
+ /** Currently active expo-gl WebGL context */
47
+ let activeGL: ExpoWebGLRenderingContext | null = null;
48
+
49
+ // =============================================================================
50
+ // CONTEXT MANAGEMENT FUNCTIONS
51
+ // Public API for managing the active GL context.
52
+ // =============================================================================
53
+
54
+ /**
55
+ * Set the active GL context from expo-gl's GLView.
56
+ *
57
+ * This function must be called in GLView's onContextCreate callback
58
+ * before creating any PixiJS Application or renderer.
59
+ *
60
+ * @param gl - The WebGL context from expo-gl
61
+ * @param width - Canvas width in pixels
62
+ * @param height - Canvas height in pixels
63
+ * @returns ExpoCanvasElement configured with the GL context
64
+ *
65
+ * @example
66
+ * ```tsx
67
+ * <GLView
68
+ * onContextCreate={(gl) => {
69
+ * const canvas = setActiveGLContext(
70
+ * gl,
71
+ * gl.drawingBufferWidth,
72
+ * gl.drawingBufferHeight
73
+ * );
74
+ * // canvas is now ready for PixiJS
75
+ * }}
76
+ * />
77
+ * ```
78
+ */
79
+ export function setActiveGLContext(
80
+ gl: ExpoWebGLRenderingContext,
81
+ width: number,
82
+ height: number
83
+ ): ExpoCanvasElement {
84
+ activeCanvas = new ExpoCanvasElement(width, height);
85
+ activeCanvas.setGLContext(gl);
86
+ activeGL = gl;
87
+ return activeCanvas;
88
+ }
89
+
90
+ /**
91
+ * Get the currently active canvas element.
92
+ *
93
+ * @returns The active ExpoCanvasElement, or null if none is set
94
+ */
95
+ export function getActiveCanvas(): ExpoCanvasElement | null {
96
+ return activeCanvas;
97
+ }
98
+
99
+ /**
100
+ * Get the currently active expo-gl WebGL context.
101
+ *
102
+ * @returns The active GL context, or null if none is set
103
+ */
104
+ export function getActiveGL(): ExpoWebGLRenderingContext | null {
105
+ return activeGL;
106
+ }
107
+
108
+ /**
109
+ * Clear the active context.
110
+ *
111
+ * Should be called when the GLView unmounts to prevent memory leaks
112
+ * and stale references.
113
+ */
114
+ export function clearActiveContext(): void {
115
+ activeCanvas = null;
116
+ activeGL = null;
117
+ }
118
+
119
+ // =============================================================================
120
+ // EXPO ADAPTER
121
+ // Main adapter object implementing PixiJS's Adapter interface.
122
+ // =============================================================================
123
+
124
+ /**
125
+ * PixiJS DOMAdapter implementation for React Native Expo.
126
+ *
127
+ * This object provides the bridge between PixiJS's browser expectations
128
+ * and React Native's expo-gl environment. It's set as the active adapter
129
+ * via `DOMAdapter.set(ExpoAdapter)`.
130
+ *
131
+ * @remarks
132
+ * Key differences from browser adapter:
133
+ * - Canvas 2D context is not supported (WebGL only)
134
+ * - FontFaceSet is not available (use expo-font)
135
+ * - Base URL is empty (use expo-asset for bundled resources)
136
+ * - XML parsing uses @xmldom/xmldom instead of DOMParser
137
+ */
138
+ export const ExpoAdapter = {
139
+ // ===========================================================================
140
+ // CANVAS CREATION
141
+ // ===========================================================================
142
+
143
+ /**
144
+ * Create a canvas element for rendering.
145
+ *
146
+ * If an active canvas exists (from setActiveGLContext), returns it.
147
+ * Otherwise creates a new ExpoCanvasElement that will need a GL context.
148
+ *
149
+ * @param width - Canvas width in pixels
150
+ * @param height - Canvas height in pixels
151
+ * @returns ExpoCanvasElement instance
152
+ */
153
+ createCanvas: (width?: number, height?: number): ExpoCanvasElement => {
154
+ if (activeCanvas) {
155
+ // Update dimensions if provided
156
+ if (width !== undefined) activeCanvas.width = width;
157
+ if (height !== undefined) activeCanvas.height = height;
158
+ return activeCanvas;
159
+ }
160
+
161
+ // Create placeholder canvas (will need GL context later)
162
+ return new ExpoCanvasElement(width ?? 1, height ?? 1);
163
+ },
164
+
165
+ // ===========================================================================
166
+ // IMAGE CREATION
167
+ // ===========================================================================
168
+
169
+ /**
170
+ * Create an image element for texture loading.
171
+ *
172
+ * Returns a minimal HTMLImageElement-like object. For actual texture
173
+ * loading, use the loadExpoAsset loader extension which integrates
174
+ * with expo-asset.
175
+ *
176
+ * @returns Object mimicking HTMLImageElement interface
177
+ */
178
+ createImage: (): any => {
179
+ const image = {
180
+ src: '',
181
+ width: 0,
182
+ height: 0,
183
+ naturalWidth: 0,
184
+ naturalHeight: 0,
185
+ complete: false,
186
+ crossOrigin: '' as string | null,
187
+ onload: null as ((this: any, ev: Event) => any) | null,
188
+ onerror: null as OnErrorEventHandler | null,
189
+
190
+ /**
191
+ * Setting source would trigger image loading.
192
+ * Full implementation uses expo-asset or react-native Image.
193
+ */
194
+ set source(value: string) {
195
+ this.src = value;
196
+ },
197
+ };
198
+
199
+ return image;
200
+ },
201
+
202
+ // ===========================================================================
203
+ // RENDERING CONTEXT ACCESS
204
+ // ===========================================================================
205
+
206
+ /**
207
+ * Get the Canvas 2D rendering context constructor.
208
+ *
209
+ * @returns null - Canvas 2D is not supported in expo-gl
210
+ *
211
+ * @remarks
212
+ * expo-gl only provides WebGL contexts. For 2D canvas operations,
213
+ * consider using @shopify/react-native-skia as an alternative.
214
+ */
215
+ getCanvasRenderingContext2D: (): any => {
216
+ console.warn('ExpoAdapter: 2D context is not supported in expo-gl');
217
+ return null;
218
+ },
219
+
220
+ /**
221
+ * Get the WebGL rendering context constructor.
222
+ *
223
+ * @returns WebGLRenderingContext constructor
224
+ */
225
+ getWebGLRenderingContext: (): typeof WebGLRenderingContext => {
226
+ return WebGLRenderingContext;
227
+ },
228
+
229
+ // ===========================================================================
230
+ // BROWSER API SHIMS
231
+ // ===========================================================================
232
+
233
+ /**
234
+ * Get navigator information.
235
+ *
236
+ * Returns minimal navigator object for feature detection.
237
+ * GPU access is not available in React Native.
238
+ *
239
+ * @returns Navigator-like object
240
+ */
241
+ getNavigator: (): { userAgent: string; gpu: GPU | null } => {
242
+ return {
243
+ userAgent: 'expo-gl/react-native',
244
+ gpu: null,
245
+ };
246
+ },
247
+
248
+ /**
249
+ * Get the base URL for relative resource loading.
250
+ *
251
+ * @returns Empty string - React Native has no traditional base URL
252
+ *
253
+ * @remarks
254
+ * For bundled assets, use expo-asset's Asset.fromModule(require('./file'))
255
+ * instead of relative URLs.
256
+ */
257
+ getBaseUrl: (): string => {
258
+ return '';
259
+ },
260
+
261
+ /**
262
+ * Get the FontFaceSet for CSS font loading.
263
+ *
264
+ * @returns null - FontFaceSet is not available in React Native
265
+ *
266
+ * @remarks
267
+ * Use expo-font's Font.loadAsync() for font loading in Expo apps.
268
+ * The loadExpoFont loader extension provides PixiJS integration.
269
+ */
270
+ getFontFaceSet: (): FontFaceSet | null => {
271
+ return null;
272
+ },
273
+
274
+ // ===========================================================================
275
+ // NETWORK REQUESTS
276
+ // ===========================================================================
277
+
278
+ /**
279
+ * Fetch a resource from network or local storage.
280
+ *
281
+ * Handles different URL schemes:
282
+ * - http:// / https:// - Standard network fetch
283
+ * - file:// - Local file (requires expo-file-system)
284
+ * - asset:// - Bundled asset (requires expo-asset)
285
+ *
286
+ * @param url - Resource URL or Request object
287
+ * @param options - Fetch options
288
+ * @returns Promise resolving to Response
289
+ *
290
+ * @example
291
+ * ```ts
292
+ * const response = await ExpoAdapter.fetch('https://example.com/data.json');
293
+ * const json = await response.json();
294
+ * ```
295
+ */
296
+ fetch: async (url: RequestInfo, options?: RequestInit): Promise<Response> => {
297
+ const requestUrl = typeof url === 'string' ? url : (url as Request).url;
298
+
299
+ // Remote URL - use standard fetch
300
+ if (requestUrl.startsWith('http://') || requestUrl.startsWith('https://')) {
301
+ return fetch(requestUrl, options);
302
+ }
303
+
304
+ // Local file URL
305
+ if (requestUrl.startsWith('file://')) {
306
+ console.warn('ExpoAdapter: Local file loading requires expo-file-system');
307
+ return fetch(requestUrl, options);
308
+ }
309
+
310
+ // Asset URL or require() number
311
+ if (requestUrl.startsWith('asset://') || typeof url === 'number') {
312
+ console.warn('ExpoAdapter: Asset loading requires expo-asset');
313
+ throw new Error('Asset loading not implemented');
314
+ }
315
+
316
+ // Default - try standard fetch
317
+ return fetch(requestUrl, options);
318
+ },
319
+
320
+ // ===========================================================================
321
+ // XML PARSING
322
+ // ===========================================================================
323
+
324
+ /**
325
+ * Parse an XML string into a Document.
326
+ *
327
+ * Uses @xmldom/xmldom since native DOMParser is not available
328
+ * in React Native. Required for SVG and other XML asset parsing.
329
+ *
330
+ * @param xml - XML string to parse
331
+ * @returns Parsed Document object
332
+ *
333
+ * @example
334
+ * ```ts
335
+ * const svg = '<svg>...</svg>';
336
+ * const doc = ExpoAdapter.parseXML(svg);
337
+ * ```
338
+ */
339
+ parseXML: (xml: string): Document => {
340
+ const parser = new DOMParser();
341
+ return parser.parseFromString(xml, 'text/xml') as unknown as Document;
342
+ },
343
+ };