├── .eslintrc.js ├── .github └── images │ ├── basic.gif │ └── fire.gif ├── .gitignore ├── .npmignore ├── .prettierrc.js ├── LICENSE ├── README.md ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── expo │ └── modules │ └── caemitterlayer │ ├── ReactNativeCAEmitterLayerModule.kt │ └── ReactNativeCAEmitterLayerView.kt ├── example ├── .gitignore ├── App.tsx ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── expo │ │ │ │ └── modules │ │ │ │ └── caemitterlayer │ │ │ │ └── example │ │ │ │ └── ReactNativeFlipper.java │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── expo │ │ │ │ │ └── modules │ │ │ │ │ └── caemitterlayer │ │ │ │ │ └── example │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ │ ├── drawable-hdpi │ │ │ │ └── splashscreen_image.png │ │ │ │ ├── drawable-mdpi │ │ │ │ └── splashscreen_image.png │ │ │ │ ├── drawable-xhdpi │ │ │ │ └── splashscreen_image.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ └── splashscreen_image.png │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ └── splashscreen_image.png │ │ │ │ ├── drawable │ │ │ │ ├── rn_edit_text_material.xml │ │ │ │ └── splashscreen.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── values-night │ │ │ │ └── colors.xml │ │ │ │ └── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ └── release │ │ │ └── java │ │ │ └── expo │ │ │ └── modules │ │ │ └── caemitterlayer │ │ │ └── example │ │ │ └── ReactNativeFlipper.java │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── assets │ ├── adaptive-icon.png │ ├── contents │ │ ├── circle.png │ │ ├── flame.png │ │ ├── rectangle.png │ │ └── snowflake.png │ ├── favicon.png │ ├── icon.png │ └── splash.png ├── babel.config.js ├── examples │ ├── Basic.tsx │ ├── BurstEffect.tsx │ ├── Confetti.tsx │ ├── ConfettiCannon.tsx │ ├── Emoji.tsx │ ├── Fire.tsx │ ├── Fireworks.tsx │ ├── List.tsx │ ├── Snow.tsx │ └── pokemons.ts ├── index.js ├── ios │ ├── .gitignore │ ├── .xcode.env │ ├── Podfile │ ├── Podfile.lock │ ├── Podfile.properties.json │ ├── reactnativecaemitterlayerexample.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── reactnativecaemitterlayerexample.xcscheme │ ├── reactnativecaemitterlayerexample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── reactnativecaemitterlayerexample │ │ ├── AppDelegate.h │ │ ├── AppDelegate.mm │ │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── App-Icon-20x20@1x.png │ │ │ ├── App-Icon-20x20@2x.png │ │ │ ├── App-Icon-20x20@3x.png │ │ │ ├── App-Icon-29x29@1x.png │ │ │ ├── App-Icon-29x29@2x.png │ │ │ ├── App-Icon-29x29@3x.png │ │ │ ├── App-Icon-40x40@1x.png │ │ │ ├── App-Icon-40x40@2x.png │ │ │ ├── App-Icon-40x40@3x.png │ │ │ ├── App-Icon-60x60@2x.png │ │ │ ├── App-Icon-60x60@3x.png │ │ │ ├── App-Icon-76x76@1x.png │ │ │ ├── App-Icon-76x76@2x.png │ │ │ ├── App-Icon-83.5x83.5@2x.png │ │ │ ├── Contents.json │ │ │ └── ItunesArtwork@2x.png │ │ ├── Contents.json │ │ ├── SplashScreen.imageset │ │ │ ├── Contents.json │ │ │ └── image.png │ │ └── SplashScreenBackground.imageset │ │ │ ├── Contents.json │ │ │ └── image.png │ │ ├── Info.plist │ │ ├── SplashScreen.storyboard │ │ ├── Supporting │ │ └── Expo.plist │ │ ├── main.m │ │ ├── noop-file.swift │ │ └── reactnativecaemitterlayerexample.entitlements ├── metro.config.js ├── package-lock.json ├── package.json ├── tsconfig.json └── webpack.config.js ├── expo-module.config.json ├── ios ├── CAEmitterLayer+InitialValuesSync.swift ├── EmitterImageLoader.swift ├── Helpers.swift ├── Mapping │ ├── AnyValue.swift │ ├── EmitterConfiguration.swift │ ├── JsonConvertible.swift │ ├── RCTCAImageSource.swift │ └── StringContents.swift ├── ReactNativeCAEmitterLayer.podspec ├── ReactNativeCAEmitterLayerModule.swift └── ReactNativeCAEmitterLayerView.swift ├── package-lock.json ├── package.json ├── src ├── EmitterView.tsx ├── ReactNativeCAEmitterLayerView.ts ├── index.ts └── types.ts └── tsconfig.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['universe/native', 'universe/web'], 4 | ignorePatterns: ['build'], 5 | }; 6 | -------------------------------------------------------------------------------- /.github/images/basic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalZebra/react-native-caemitterlayer/ba343cd28bef0b44c8f2767eecf161b3ff20c492/.github/images/basic.gif -------------------------------------------------------------------------------- /.github/images/fire.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalZebra/react-native-caemitterlayer/ba343cd28bef0b44c8f2767eecf161b3ff20c492/.github/images/fire.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # VSCode 6 | .vscode/ 7 | jsconfig.json 8 | 9 | # Xcode 10 | # 11 | build/ 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata 21 | *.xccheckout 22 | *.moved-aside 23 | DerivedData 24 | *.hmap 25 | *.ipa 26 | *.xcuserstate 27 | project.xcworkspace 28 | 29 | # Android/IJ 30 | # 31 | .classpath 32 | .cxx 33 | .gradle 34 | .idea 35 | .project 36 | .settings 37 | local.properties 38 | android.iml 39 | 40 | # Cocoapods 41 | # 42 | example/ios/Pods 43 | 44 | # Ruby 45 | example/vendor/ 46 | 47 | # node.js 48 | # 49 | node_modules/ 50 | npm-debug.log 51 | yarn-debug.log 52 | yarn-error.log 53 | 54 | # BUCK 55 | buck-out/ 56 | \.buckd/ 57 | android/app/libs 58 | android/keystores/debug.keystore 59 | 60 | # Expo 61 | .expo/* 62 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Exclude all top-level hidden directories by convention 2 | /.*/ 3 | 4 | __mocks__ 5 | __tests__ 6 | 7 | /babel.config.js 8 | /android/src/androidTest/ 9 | /android/src/test/ 10 | /android/build/ 11 | /example/ 12 | 13 | .github 14 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | bracketSameLine: true, 4 | semi: false, 5 | singleQuote: true, 6 | trailingComma: 'all', 7 | }; 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Drew 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

react-native-caemitterlayer

2 | 3 |

4 | A React Native wrapper for iOS's CAEmitterLayer

5 | 6 |

Create powerful and performant particle effects for your React Native apps! 7 |

8 | 9 |

10 | a GIF of a view emitting flames from the top 11 |

12 |

13 | Installation | Basic Usage | Documentation/API | Roadmap | Acknowledgements 14 |

15 | 16 | ## 💿 Installation 17 | 18 | Add this package to your app with the following command: 19 | 20 | ```bash 21 | npx expo install react-native-caemitterlayer 22 | ``` 23 | 24 | > ⚠️ This module is built on [Expo Modules API](https://docs.expo.dev/modules/overview/) and thus requires Expo 47 or above. 25 | > If your project is a "vanilla" React Native application, consider [adding Expo to it](https://docs.expo.dev/bare/installing-expo-modules/) to utilize the Expo ecosystem. 26 | 27 | > ℹ️ If not already, you will have to adopt [Expo prebuild](https://docs.expo.dev/workflow/prebuild/) or [Expo dev builds](https://docs.expo.dev/develop/development-builds/introduction/) to make use of custom native modules. 28 | 29 | ## 🔥 Basic Usage 30 | 31 | `EmitterView` requires an `emitterConfig` prop, which contains the configuration for the underlying `CAEmitterLayer` and its associated `CAEmitterCell`s. 32 | 33 | ```tsx 34 | import { View } from 'react-native' 35 | import { EmitterConfigPropType, EmitterView } from 'react-native-caemitterlayer' 36 | 37 | const circle = require('../assets/contents/circle.png') 38 | 39 | export function BasicExample() { 40 | const emitterConfig: EmitterConfigPropType = { 41 | layer: { 42 | // center the emission point in the middle of top edge of the view 43 | emitterPosition: { 44 | x: 50, 45 | y: 0, 46 | }, 47 | emitterCells: [ 48 | { 49 | imageContents: circle, // image require'd by Metro bundler 50 | color: '#006699', 51 | lifetime: 5, // particles live for 5 seconds 52 | velocity: 20, 53 | birthRate: 1, // One particle per second 54 | emissionLongitude: -Math.PI / 2, // emit particles up 55 | emissionRange: Math.PI / 4, // emit particles in a 45 degree cone 56 | }, 57 | ], 58 | }, 59 | } 60 | 61 | return ( 62 | 69 | 73 | 74 | ) 75 | } 76 | ``` 77 | 78 |

The code above produces this:

79 |

80 | a GIF of a view emitting circles from the top. 81 |

82 | 83 | Check out the [example app](example/) for more in depth and powerful examples. 84 | 85 | ## 📖 Documentation 86 | 87 | ### `EmitterView` 88 | 89 | ```tsx 90 | import { EmitterView } from 'react-native-caemitterlayer'; 91 | 92 | 95 | ``` 96 | 97 | **Props** 98 | 99 | #### `emitterConfig: EmitterConfigPropType` (required) 100 | 101 | The `CAEmitterLayer` configuration for this `EmitterView`. See below for how to configure this prop, or check out the example app's [examples](example/examples/) for working examples. 102 | 103 | #### `emitterConfig.layer: EmitterLayer` (required) 104 | 105 | Configures the single `CAEmitterLayer` which will render particles. 106 | 107 | #### `emitterConfig.layer.enabled?: boolean` 108 | 109 | Whether or not the emitter is enabled. Defaults to `true`. 110 | 111 | #### `emitterConfig.layer.initialValues?: object` 112 | 113 | Values which are applied to the `CAEmitterLayer` on mount and whenever the layer transitions from `enabled: false` -> `enabled: true`. 114 | This is useful for setting certain [`CAMediaTiming` properties](https://developer.apple.com/documentation/quartzcore/camediatiming) which should only be set once (e.g. `beginTime`). 115 | 116 | #### `emitterConfig.layer.emitterCells?: EmitterCellType[]` 117 | 118 | An array of `EmitterCellType` objects which act as templates for the particles emitted by the layer. 119 | Each property on the `EmitterCellType` is optional and has the same defaults as the corresponding property on `CAEmitterCell`. See the [CAEmitterCell docs](https://developer.apple.com/documentation/quartzcore/caemittercell) for details on each property. 120 | 121 | #### `emitterConfig.layer` - `CAEmitterLayer` properties 122 | 123 | The rest of the properties on `emitterConfig.layer` are passed directly to the `CAEmitterLayer` instance (if set) and have the same defaults they would on `CAEmitterLayer`. See the [CAEmitterLayer docs](https://developer.apple.com/documentation/quartzcore/caemitterlayer) for details on each property. 124 | 125 | #### `...ViewProps` 126 | 127 | `EmitterView` also accepts all the same `ViewProps` props (i.e. `style`) as a [React Native `View`](https://reactnative.dev/docs/view). 128 | 129 | ## 🗺️ Roadmap 130 | 131 | Future plans for features/enhancements/fixes (in no particular order/priority): 132 | 133 | - **Better image support** ✅ - **Shipped in `v0.3.0`** 134 | 135 | ~~This library currently requires inlining of the images used for emitter cells (via `EmitterCellType.imageData`). The images must be represented as base64 encoded strings on the JS side. This isn't ideal for performance or developer ergonomics. A better way of handling images is high priority.~~ 136 | 137 | - **Better animation support** 138 | 139 | It's currently not possible to animate `EmitterView` directly via RN Animated or Reanmiated. Will be looking at ways to support this to avoid having to wrap the `` in an `` or similar. 140 | 141 | - **Support placeholders for `emitterPosition` and `emitterSize`.** 142 | 143 | `emitterPosition` and `emitterSize` require specifying exact coordinates/sizes. For some uses, `useWindowDimensions` may be sufficient. However, due to the nature of how React Native renders things, you may have to wait until `onLayout` is called before knowing what to set these values to. Adding placeholder values/strings could reduce the need for `onLayout`/`useWindowDimensions` as we could then set `emitterSize`/`emitterPosition` on the native side. 144 | 145 | - Support emoji as emitter cell contents. 146 | - Support drawing basic images to be used in emitter cells (circle, oval, rect, triangle, etc) 147 | - Possible animation support of layer or cell properties - either via Animated/Reanimated or basic `CAAnimation`s 148 | 149 | ### Not planned/Out of scope 150 | 151 | #### Android support 152 | 153 | Android support is not planned at this time. Android does not contain a built in particle emitter engine. I've explored various options but there's drawbacks to each. I've also investigated building one myself. However, it would take much effort reach parity with `CAEmitterLayer`. 154 | 155 | ## 🙌 Acknowledgements 156 | 157 | A number of blog posts helped immensely with learning `CAEmitterLayer`. These authors also provided great examples which were used to test this library's functionality: 158 | 159 | - https://nshipster.com/caemitterlayer/ by [@Mattt](https://github.com/mattt) 160 | - https://bryce.co/caemitterbehavior/ by [@brycepauken](https://github.com/brycepauken) 161 | - https://bryce.co/recreating-imessage-confetti/ by [@brycepauken](https://github.com/brycepauken) 162 | - https://medium.com/@peteliev/what-do-you-know-about-caemitterlayer-368378d45c2e by [@peteliev](https://github.com/peteliev) 163 | 164 | ## ⚖️ License 165 | 166 | MIT 167 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'maven-publish' 4 | 5 | group = 'expo.modules.caemitterlayer' 6 | version = '0.1.0' 7 | 8 | buildscript { 9 | def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle") 10 | if (expoModulesCorePlugin.exists()) { 11 | apply from: expoModulesCorePlugin 12 | applyKotlinExpoModulesCorePlugin() 13 | } 14 | 15 | // Simple helper that allows the root project to override versions declared by this library. 16 | ext.safeExtGet = { prop, fallback -> 17 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 18 | } 19 | 20 | // Ensures backward compatibility 21 | ext.getKotlinVersion = { 22 | if (ext.has("kotlinVersion")) { 23 | ext.kotlinVersion() 24 | } else { 25 | ext.safeExtGet("kotlinVersion", "1.6.10") 26 | } 27 | } 28 | 29 | repositories { 30 | mavenCentral() 31 | } 32 | 33 | dependencies { 34 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}") 35 | } 36 | } 37 | 38 | // Creating sources with comments 39 | task androidSourcesJar(type: Jar) { 40 | classifier = 'sources' 41 | from android.sourceSets.main.java.srcDirs 42 | } 43 | 44 | afterEvaluate { 45 | publishing { 46 | publications { 47 | release(MavenPublication) { 48 | from components.release 49 | // Add additional sourcesJar to artifacts 50 | artifact(androidSourcesJar) 51 | } 52 | } 53 | repositories { 54 | maven { 55 | url = mavenLocal().url 56 | } 57 | } 58 | } 59 | } 60 | 61 | android { 62 | compileSdkVersion safeExtGet("compileSdkVersion", 31) 63 | 64 | compileOptions { 65 | sourceCompatibility JavaVersion.VERSION_11 66 | targetCompatibility JavaVersion.VERSION_11 67 | } 68 | 69 | kotlinOptions { 70 | jvmTarget = JavaVersion.VERSION_11.majorVersion 71 | } 72 | 73 | defaultConfig { 74 | minSdkVersion safeExtGet("minSdkVersion", 21) 75 | targetSdkVersion safeExtGet("targetSdkVersion", 31) 76 | versionCode 1 77 | versionName "0.1.0" 78 | } 79 | lintOptions { 80 | abortOnError false 81 | } 82 | } 83 | 84 | repositories { 85 | mavenCentral() 86 | } 87 | 88 | dependencies { 89 | implementation project(':expo-modules-core') 90 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" 91 | } 92 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /android/src/main/java/expo/modules/caemitterlayer/ReactNativeCAEmitterLayerModule.kt: -------------------------------------------------------------------------------- 1 | package expo.modules.caemitterlayer 2 | 3 | import expo.modules.kotlin.modules.Module 4 | import expo.modules.kotlin.modules.ModuleDefinition 5 | 6 | class ReactNativeCAEmitterLayerModule : Module() { 7 | // Each module class must implement the definition function. The definition consists of components 8 | // that describes the module's functionality and behavior. 9 | // See https://docs.expo.dev/modules/module-api for more details about available components. 10 | override fun definition() = ModuleDefinition { 11 | // Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument. 12 | // Can be inferred from module's class name, but it's recommended to set it explicitly for clarity. 13 | // The module will be accessible from `requireNativeModule('ReactNativeCAEmitterLayer')` in JavaScript. 14 | Name("ReactNativeCAEmitterLayer") 15 | 16 | // Sets constant properties on the module. Can take a dictionary or a closure that returns a dictionary. 17 | Constants( 18 | "PI" to Math.PI 19 | ) 20 | 21 | // Defines event names that the module can send to JavaScript. 22 | Events("onChange") 23 | 24 | // Defines a JavaScript synchronous function that runs the native code on the JavaScript thread. 25 | Function("hello") { 26 | "Hello world! 👋" 27 | } 28 | 29 | // Defines a JavaScript function that always returns a Promise and whose native code 30 | // is by default dispatched on the different thread than the JavaScript runtime runs on. 31 | AsyncFunction("setValueAsync") { value: String -> 32 | // Send an event to JavaScript. 33 | sendEvent("onChange", mapOf( 34 | "value" to value 35 | )) 36 | } 37 | 38 | // Enables the module to be used as a native view. Definition components that are accepted as part of 39 | // the view definition: Prop, Events. 40 | View(ReactNativeCAEmitterLayerView::class) { 41 | // Defines a setter for the `name` prop. 42 | Prop("name") { view: ReactNativeCAEmitterLayerView, prop: String -> 43 | println(prop) 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /android/src/main/java/expo/modules/caemitterlayer/ReactNativeCAEmitterLayerView.kt: -------------------------------------------------------------------------------- 1 | package expo.modules.caemitterlayer 2 | 3 | import android.content.Context 4 | import expo.modules.kotlin.AppContext 5 | import expo.modules.kotlin.views.ExpoView 6 | 7 | class ReactNativeCAEmitterLayerView(context: Context, appContext: AppContext) : ExpoView(context, appContext) 8 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | 16 | # Temporary files created by Metro to check the health of the file watcher 17 | .metro-health-check* 18 | -------------------------------------------------------------------------------- /example/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import { 3 | Button, 4 | SafeAreaView, 5 | Text, 6 | TextStyle, 7 | TouchableOpacity, 8 | View, 9 | ViewStyle, 10 | } from 'react-native' 11 | 12 | import { Basic } from './examples/Basic' 13 | import { BurstEffect } from './examples/BurstEffect' 14 | import { Confetti } from './examples/Confetti' 15 | import { ConfettiCannon } from './examples/ConfettiCannon' 16 | import { Emoji } from './examples/Emoji' 17 | import { Fire } from './examples/Fire' 18 | import { Fireworks } from './examples/Fireworks' 19 | import { PokemonList } from './examples/List' 20 | import { Snow } from './examples/Snow' 21 | 22 | const Examples: Record = { 23 | Basic: { 24 | Component: Basic, 25 | title: 'Basic', 26 | }, 27 | Emoji: { 28 | Component: Emoji, 29 | title: 'Emoji', 30 | }, 31 | Fire: { 32 | Component: Fire, 33 | title: 'Fire', 34 | }, 35 | Snow: { 36 | Component: Snow, 37 | title: 'Snow', 38 | }, 39 | Fireworks: { 40 | Component: Fireworks, 41 | title: 'Fireworks', 42 | }, 43 | BurstEffect: { 44 | Component: BurstEffect, 45 | title: 'Burst Effect', 46 | }, 47 | Confetti: { 48 | Component: Confetti, 49 | title: 'Confetti', 50 | }, 51 | ConfettiCannon: { 52 | Component: ConfettiCannon, 53 | title: 'Confetti Cannon', 54 | }, 55 | List: { 56 | Component: PokemonList, 57 | title: 'List', 58 | }, 59 | } 60 | 61 | type ExampleKeys = keyof typeof Examples 62 | 63 | export default function App() { 64 | const [selectedExample, setSelecteExample] = useState( 65 | null, 66 | ) 67 | 68 | if (!selectedExample) { 69 | return ( 70 | 71 | Examples 72 | {Object.entries(Examples).map(([exampleName, { title }]) => ( 73 |