Some checks failed
Publish to npm / publish (push) Has been cancelled
- Remove all Chinese characters from src/, example/, ios/, patches/, tests/ - Add esbuild-based build obfuscation (minify + identifier mangle + no sourcemaps) - Drop src/ from npm publish, only ship minified dist/ - Remove source maps and declaration maps from build output - Add README icons and visual polish throughout - Fix broken Table of Contents anchor links in README Co-authored-by: Cursor <cursoragent@cursor.com>
212 lines
6.7 KiB
Markdown
212 lines
6.7 KiB
Markdown
# MaskSegmentCanvas Example
|
|
|
|
A Demo project that **fully simulates real-world business integration**, showing how to integrate `react-native-mask-segment-canvas` into your React Native project.
|
|
|
|
## Difference from the Library's Own Demo
|
|
|
|
| Project | Import Method | Purpose |
|
|
| ---- | -------- | ---- |
|
|
| Root `App.tsx` | `import ... from './src'` (internal source) | Library author self-testing |
|
|
| **This example/** | `import ... from 'react-native-mask-segment-canvas'` (public API) | **Business integration reference** |
|
|
|
|
This example only depends on the library's public API and does not touch any `src/` internals. It serves as a template you can directly copy into your project.
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# 1. Enter the example directory
|
|
cd example
|
|
|
|
# 2. Install dependencies (auto-links the parent library)
|
|
npm install
|
|
|
|
# 3. Apply postinstall patches (patch-package patches react-native-fast-opencv)
|
|
# Automatically runs after npm install. If it didn't, run manually:
|
|
npx patch-package
|
|
|
|
# 4. iOS: Install native dependencies
|
|
cd ios && pod install && cd ..
|
|
|
|
# 5. Start Metro
|
|
npm start
|
|
|
|
# 6. In another terminal, run
|
|
npm run ios
|
|
# or
|
|
npm run android
|
|
```
|
|
|
|
## File Overview
|
|
|
|
```
|
|
example/
|
|
├── App.tsx # ★ Core: Complete integration example page
|
|
├── index.js # RN entry (registers gesture-handler + Buffer polyfill)
|
|
├── app.json # App name config
|
|
├── package.json # Standalone dependency config, "react-native-mask-segment-canvas": "file:.."
|
|
├── metro.config.js # Metro config (watchFolders pointing to parent directory)
|
|
├── babel.config.js # Babel config (includes reanimated plugin)
|
|
├── tsconfig.json # TypeScript config
|
|
└── README.md # This file
|
|
```
|
|
|
|
## Features Covered in App.tsx
|
|
|
|
`App.tsx` is a complete page you can reference directly, covering:
|
|
|
|
| Feature | Relevant Code Location |
|
|
| ---- | ------------ |
|
|
| **PNG pre-warming** | `useEffect` → `prewarmPngBgrCacheAsync` |
|
|
| **State management** | `watchState` / `isInteractive` / `isOutlineReady` derived states |
|
|
| **onWatch callback** | `handleWatch` — tracks initialization stages |
|
|
| **onPaintCallback** | `handlePaintCallback` — handles both successful paint and missing-brush scenarios |
|
|
| **onError callback** | `handleError` — captures segmentation/load failures |
|
|
| **Ref operations** | `save` / `reset` / `swap` / `clearAllPaint` / `session` |
|
|
| **setPaintColor** | Preset brush colors via `ref.setPaintColor` |
|
|
| **Custom semantic color table** | `GYM_CUSTOM_COLORS` example + mode toggle UI |
|
|
| **Pipeline resolution toggle** | `pipelinePreset` low/medium/high resolution switching |
|
|
| **Toast notifications** | `brush_required` callback when no brush is selected + custom Toast |
|
|
| **Loading/error UI** | PNG pre-warm loading, initialization Loading, error display |
|
|
| **Draft recovery** | `sessionDraft` state + `initialSession` prop |
|
|
|
|
## Integrating into Your Own Project
|
|
|
|
### Option 1: npm install (recommended for production)
|
|
|
|
```bash
|
|
npm install react-native-mask-segment-canvas
|
|
```
|
|
|
|
### Option 2: Local development
|
|
|
|
```bash
|
|
# In the library directory
|
|
npm link
|
|
|
|
# In your project
|
|
npm link react-native-mask-segment-canvas
|
|
```
|
|
|
|
Your `metro.config.js` needs to add:
|
|
|
|
```js
|
|
const path = require('path');
|
|
|
|
module.exports = mergeConfig(getDefaultConfig(__dirname), {
|
|
watchFolders: [path.resolve(__dirname, '../MaskSegmentApp')],
|
|
resolver: {
|
|
nodeModulesPaths: [path.resolve(__dirname, 'node_modules')],
|
|
},
|
|
});
|
|
```
|
|
|
|
### Option 3: file: dependency (used by this example)
|
|
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"react-native-mask-segment-canvas": "file:../MaskSegmentApp"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Required peerDependencies
|
|
|
|
```bash
|
|
npm install @shopify/react-native-skia react-native-reanimated react-native-fast-opencv react-native-fs buffer
|
|
# If using photo library picker
|
|
npm install react-native-image-picker
|
|
# Safe area insets
|
|
npm install react-native-safe-area-context
|
|
```
|
|
|
|
### postinstall Configuration
|
|
|
|
Your `package.json` needs:
|
|
|
|
```json
|
|
{
|
|
"scripts": {
|
|
"postinstall": "patch-package"
|
|
},
|
|
"devDependencies": {
|
|
"patch-package": "^8.0.1"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Common Issues
|
|
|
|
**Getting "module not found" after `npm install`?**
|
|
- Make sure `postinstall` ran (`npx patch-package`)
|
|
- Check that Metro config's `watchFolders` includes the library directory
|
|
|
|
**`pod install` fails?**
|
|
```bash
|
|
cd ios
|
|
bundle install
|
|
bundle exec pod install --repo-update
|
|
```
|
|
|
|
**Android build errors?**
|
|
```bash
|
|
cd android && ./gradlew clean && cd ..
|
|
```
|
|
|
|
**Duplicate module errors at runtime (most common)**
|
|
|
|
In monorepo, npm link, or `file:..` setups, you'll frequently encounter these "similar-looking" errors:
|
|
|
|
- `SkiaPictureView must be a function (received 'undefined')`
|
|
- `createAnimatedNode: Animated node[...] already exists` (including UIFrameGuarded variants)
|
|
- Other Fabric ViewManager / native module singleton conflicts
|
|
|
|
**Root cause**: Metro is loading multiple copies of `@shopify/react-native-skia`, `react-native-reanimated`, `react-native-gesture-handler`, `react-native-fast-opencv`, `react-native-safe-area-context`, and other peer dependencies.
|
|
|
|
**Recommended complete solution** (copy directly into your project):
|
|
|
|
1. **At the very top of index.js** (must come first):
|
|
|
|
```js
|
|
import 'react-native-gesture-handler';
|
|
import 'react-native-reanimated';
|
|
import '@shopify/react-native-skia';
|
|
```
|
|
|
|
2. **metro.config.js** (use extraNodeModules + blockList for double safety):
|
|
|
|
```js
|
|
const path = require('path');
|
|
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
|
|
|
const yourNodeModules = path.resolve(__dirname, 'node_modules');
|
|
|
|
const singletons = [
|
|
'react', 'react-native',
|
|
'react-native-reanimated',
|
|
'@shopify/react-native-skia',
|
|
'react-native-gesture-handler',
|
|
'react-native-fast-opencv',
|
|
'react-native-safe-area-context',
|
|
'react-native-fs',
|
|
'react-native-image-picker',
|
|
];
|
|
|
|
module.exports = mergeConfig(getDefaultConfig(__dirname), {
|
|
watchFolders: [path.resolve(__dirname, '../MaskSegmentApp')],
|
|
resolver: {
|
|
nodeModulesPaths: [yourNodeModules],
|
|
extraNodeModules: singletons.reduce((acc, p) => (acc[p] = path.resolve(yourNodeModules, p), acc), {}),
|
|
blockList: singletons.map(p => new RegExp(`/MaskSegmentApp/node_modules/${p.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}/`)),
|
|
},
|
|
});
|
|
```
|
|
|
|
> `example/metro.config.js` is already written following this standard template. You can reference it directly.
|
|
|
|
After completing the two steps above, you **must**:
|
|
- Restart Metro (`npx react-native start --reset-cache`)
|
|
- Reinstall the app (recommend `cd android && ./gradlew clean` first, or re-run after iOS pod install)
|
|
|
|
This will resolve all "similar" duplicate-module runtime errors in one shot.
|