expo-orb 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/.eslintrc.js +5 -0
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/android/build.gradle +60 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/expo/modules/orb/ExpoOrbModule.kt +79 -0
- package/android/src/main/java/expo/modules/orb/ExpoOrbView.kt +105 -0
- package/android/src/main/java/expo/modules/orb/OrbConfiguration.kt +30 -0
- package/android/src/main/java/expo/modules/orb/OrbSharedState.kt +25 -0
- package/android/src/main/java/expo/modules/orb/OrbView.kt +368 -0
- package/android/src/main/java/expo/modules/orb/ParticlesView.kt +77 -0
- package/android/src/main/java/expo/modules/orb/RotatingGlowView.kt +90 -0
- package/android/src/main/java/expo/modules/orb/WavyBlobView.kt +90 -0
- package/build/ExpoOrb.types.d.ts +17 -0
- package/build/ExpoOrb.types.d.ts.map +1 -0
- package/build/ExpoOrb.types.js +2 -0
- package/build/ExpoOrb.types.js.map +1 -0
- package/build/ExpoOrbModule.d.ts +17 -0
- package/build/ExpoOrbModule.d.ts.map +1 -0
- package/build/ExpoOrbModule.js +11 -0
- package/build/ExpoOrbModule.js.map +1 -0
- package/build/ExpoOrbModule.web.d.ts +6 -0
- package/build/ExpoOrbModule.web.d.ts.map +1 -0
- package/build/ExpoOrbModule.web.js +5 -0
- package/build/ExpoOrbModule.web.js.map +1 -0
- package/build/ExpoOrbView.d.ts +4 -0
- package/build/ExpoOrbView.d.ts.map +1 -0
- package/build/ExpoOrbView.js +7 -0
- package/build/ExpoOrbView.js.map +1 -0
- package/build/ExpoOrbView.web.d.ts +4 -0
- package/build/ExpoOrbView.web.d.ts.map +1 -0
- package/build/ExpoOrbView.web.js +17 -0
- package/build/ExpoOrbView.web.js.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +4 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/ExpoOrb.podspec +30 -0
- package/ios/ExpoOrbModule.swift +70 -0
- package/ios/ExpoOrbView.swift +105 -0
- package/ios/Orb/OrbConfiguration.swift +53 -0
- package/ios/Orb/OrbView.swift +367 -0
- package/ios/Orb/ParticlesView.swift +156 -0
- package/ios/Orb/RealisticShadows.swift +42 -0
- package/ios/Orb/RotatingGlowView.swift +62 -0
- package/ios/Orb/WavyBlobView.swift +83 -0
- package/package.json +46 -0
- package/src/ExpoOrb.types.ts +17 -0
- package/src/ExpoOrbModule.ts +22 -0
- package/src/ExpoOrbModule.web.ts +5 -0
- package/src/ExpoOrbView.tsx +11 -0
- package/src/ExpoOrbView.web.tsx +26 -0
- package/src/index.ts +3 -0
- package/tsconfig.json +9 -0
package/.eslintrc.js
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ensar Bavrk
|
|
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,129 @@
|
|
|
1
|
+
# expo-orb
|
|
2
|
+
|
|
3
|
+
An animated orb component for React Native with Expo. Renders a glowing, animated sphere with particles, wavy blobs, and customizable visual effects.
|
|
4
|
+
|
|
5
|
+
Inspired by and iOS implementation from [metasidd/Orb](https://github.com/metasidd/Orb)
|
|
6
|
+
|
|
7
|
+
## Demo
|
|
8
|
+
|
|
9
|
+
| iOS | Android |
|
|
10
|
+
|:---:|:-------:|
|
|
11
|
+
|  |  |
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Smooth 60fps animations
|
|
16
|
+
- Activity-based animation states (idle, speaking, etc.)
|
|
17
|
+
- Breathing animation effect
|
|
18
|
+
- Customizable colors, particles, and glow effects
|
|
19
|
+
- Native performance on iOS (SwiftUI/SpriteKit) and Android (Jetpack Compose)
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install expo-orb
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For bare React Native projects, run `npx pod-install` after installation.
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { ExpoOrbView, setOrbActivity } from 'expo-orb';
|
|
33
|
+
|
|
34
|
+
function MyComponent() {
|
|
35
|
+
return (
|
|
36
|
+
<ExpoOrbView
|
|
37
|
+
style={{ width: 200, height: 200 }}
|
|
38
|
+
backgroundColors={['#7c3aed', '#3b82f6', '#ec4899']}
|
|
39
|
+
glowColor="#ffffff"
|
|
40
|
+
particleColor="#ffffff"
|
|
41
|
+
showBackground={true}
|
|
42
|
+
showWavyBlobs={true}
|
|
43
|
+
showParticles={true}
|
|
44
|
+
showGlowEffects={true}
|
|
45
|
+
showShadow={true}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Control animation intensity (0-1)
|
|
51
|
+
setOrbActivity(0.8); // Active/speaking state
|
|
52
|
+
setOrbActivity(0.1); // Idle state
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Props
|
|
56
|
+
|
|
57
|
+
| Prop | Type | Default | Description |
|
|
58
|
+
|------|------|---------|-------------|
|
|
59
|
+
| `backgroundColors` | `ColorValue[]` | `['green', 'blue', 'pink']` | Gradient colors for the orb background (min 2 colors) |
|
|
60
|
+
| `glowColor` | `ColorValue` | `'white'` | Color of the glow effects |
|
|
61
|
+
| `particleColor` | `ColorValue` | `'white'` | Color of floating particles |
|
|
62
|
+
| `coreGlowIntensity` | `number` | `1.0` | Intensity of the core glow effect (0+) |
|
|
63
|
+
| `breathingIntensity` | `number` | `0` | Intensity of breathing animation (0-1) |
|
|
64
|
+
| `breathingSpeed` | `number` | `0.25` | Speed of breathing animation |
|
|
65
|
+
| `showBackground` | `boolean` | `true` | Show/hide background gradient |
|
|
66
|
+
| `showWavyBlobs` | `boolean` | `true` | Show/hide animated wavy blob overlays |
|
|
67
|
+
| `showParticles` | `boolean` | `true` | Show/hide floating particles |
|
|
68
|
+
| `showGlowEffects` | `boolean` | `true` | Show/hide rotating glow effects |
|
|
69
|
+
| `showShadow` | `boolean` | `true` | Show/hide drop shadow |
|
|
70
|
+
| `speed` | `number` | `60` | Animation speed multiplier |
|
|
71
|
+
| `style` | `StyleProp<ViewStyle>` | - | Container style (set width/height here) |
|
|
72
|
+
|
|
73
|
+
## Methods
|
|
74
|
+
|
|
75
|
+
### `setOrbActivity(activity: number)`
|
|
76
|
+
|
|
77
|
+
Controls the orb's animation intensity. Pass a value between 0 and 1:
|
|
78
|
+
|
|
79
|
+
- `0` - Minimal activity (idle)
|
|
80
|
+
- `0.1-0.3` - Low activity
|
|
81
|
+
- `0.5-0.7` - Medium activity
|
|
82
|
+
- `0.8-1.0` - High activity (speaking/active)
|
|
83
|
+
|
|
84
|
+
This function bypasses React's prop system to prevent animation flickering during rapid updates.
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { setOrbActivity } from 'expo-orb';
|
|
88
|
+
|
|
89
|
+
// Transition to speaking state
|
|
90
|
+
setOrbActivity(0.85);
|
|
91
|
+
|
|
92
|
+
// Return to idle
|
|
93
|
+
setOrbActivity(0.08);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Example
|
|
97
|
+
|
|
98
|
+
See the `/example` directory for a complete working example with activity simulation.
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import * as React from 'react';
|
|
102
|
+
import { View } from 'react-native';
|
|
103
|
+
import { ExpoOrbView, setOrbActivity } from 'expo-orb';
|
|
104
|
+
|
|
105
|
+
export default function App() {
|
|
106
|
+
React.useEffect(() => {
|
|
107
|
+
// Simulate activity changes
|
|
108
|
+
const interval = setInterval(() => {
|
|
109
|
+
const activity = Math.random() > 0.5 ? 0.85 : 0.08;
|
|
110
|
+
setOrbActivity(activity);
|
|
111
|
+
}, 2000);
|
|
112
|
+
|
|
113
|
+
return () => clearInterval(interval);
|
|
114
|
+
}, []);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
|
118
|
+
<ExpoOrbView
|
|
119
|
+
style={{ width: 220, height: 220 }}
|
|
120
|
+
backgroundColors={['#7c3aed', '#3b82f6', '#ec4899']}
|
|
121
|
+
/>
|
|
122
|
+
</View>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
MIT
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.safeExtGet = { prop, fallback ->
|
|
3
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
plugins {
|
|
8
|
+
id 'com.android.library'
|
|
9
|
+
id 'kotlin-android'
|
|
10
|
+
id 'org.jetbrains.kotlin.plugin.compose' version '2.1.20'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
group = 'expo.modules.iosorb'
|
|
14
|
+
version = '0.1.0'
|
|
15
|
+
|
|
16
|
+
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
17
|
+
apply from: expoModulesCorePlugin
|
|
18
|
+
applyKotlinExpoModulesCorePlugin()
|
|
19
|
+
useCoreDependencies()
|
|
20
|
+
useExpoPublishing()
|
|
21
|
+
|
|
22
|
+
android {
|
|
23
|
+
namespace "expo.modules.iosorb"
|
|
24
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 34)
|
|
25
|
+
|
|
26
|
+
defaultConfig {
|
|
27
|
+
minSdkVersion safeExtGet("minSdkVersion", 24)
|
|
28
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
|
29
|
+
versionCode 1
|
|
30
|
+
versionName "0.1.0"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
lintOptions {
|
|
34
|
+
abortOnError false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
buildFeatures {
|
|
38
|
+
compose true
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
kotlinOptions {
|
|
42
|
+
jvmTarget = '17'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
compileOptions {
|
|
46
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
47
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
dependencies {
|
|
52
|
+
def composeBom = platform('androidx.compose:compose-bom:2024.02.00')
|
|
53
|
+
implementation composeBom
|
|
54
|
+
|
|
55
|
+
implementation 'androidx.compose.ui:ui'
|
|
56
|
+
implementation 'androidx.compose.ui:ui-graphics'
|
|
57
|
+
implementation 'androidx.compose.foundation:foundation'
|
|
58
|
+
implementation 'androidx.compose.runtime:runtime'
|
|
59
|
+
implementation 'androidx.activity:activity-compose:1.8.2'
|
|
60
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
package expo.modules.orb
|
|
2
|
+
|
|
3
|
+
import android.graphics.Color as AndroidColor
|
|
4
|
+
import expo.modules.kotlin.modules.Module
|
|
5
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
6
|
+
|
|
7
|
+
class ExpoOrbModule : Module() {
|
|
8
|
+
override fun definition() = ModuleDefinition {
|
|
9
|
+
Name("ExpoOrb")
|
|
10
|
+
|
|
11
|
+
// Activity function - bypasses React's prop reconciliation
|
|
12
|
+
Function("setActivity") { activity: Double ->
|
|
13
|
+
val clampedActivity = activity.coerceIn(0.0, 1.0)
|
|
14
|
+
OrbSharedState.targetActivity = clampedActivity
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
View(ExpoOrbView::class) {
|
|
18
|
+
// Colors come as strings (hex) or integers from React Native
|
|
19
|
+
Prop("backgroundColors") { view: ExpoOrbView, colors: List<Any> ->
|
|
20
|
+
val parsedColors = colors.map { parseColor(it) }
|
|
21
|
+
view.setBackgroundColors(parsedColors)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Prop("glowColor") { view: ExpoOrbView, color: Any ->
|
|
25
|
+
view.setGlowColor(parseColor(color))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
Prop("particleColor") { view: ExpoOrbView, color: Any ->
|
|
29
|
+
view.setParticleColor(parseColor(color))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Prop("coreGlowIntensity") { view: ExpoOrbView, value: Double ->
|
|
33
|
+
view.setCoreGlowIntensity(value)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
Prop("breathingIntensity") { view: ExpoOrbView, value: Double ->
|
|
37
|
+
view.setBreathingIntensity(value)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Prop("breathingSpeed") { view: ExpoOrbView, value: Double ->
|
|
41
|
+
view.setBreathingSpeed(value)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Prop("showBackground") { view: ExpoOrbView, value: Boolean ->
|
|
45
|
+
view.setShowBackground(value)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Prop("showWavyBlobs") { view: ExpoOrbView, value: Boolean ->
|
|
49
|
+
view.setShowWavyBlobs(value)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
Prop("showParticles") { view: ExpoOrbView, value: Boolean ->
|
|
53
|
+
view.setShowParticles(value)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
Prop("showGlowEffects") { view: ExpoOrbView, value: Boolean ->
|
|
57
|
+
view.setShowGlowEffects(value)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Prop("showShadow") { view: ExpoOrbView, value: Boolean ->
|
|
61
|
+
view.setShowShadow(value)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
Prop("speed") { view: ExpoOrbView, value: Double ->
|
|
65
|
+
view.setSpeed(value)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private fun parseColor(value: Any): Int {
|
|
71
|
+
return when (value) {
|
|
72
|
+
is String -> AndroidColor.parseColor(value)
|
|
73
|
+
is Int -> value
|
|
74
|
+
is Double -> value.toInt()
|
|
75
|
+
is Long -> value.toInt()
|
|
76
|
+
else -> AndroidColor.WHITE
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
package expo.modules.orb
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import androidx.compose.foundation.layout.Box
|
|
5
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
6
|
+
import androidx.compose.foundation.layout.padding
|
|
7
|
+
import androidx.compose.runtime.Composable
|
|
8
|
+
import androidx.compose.runtime.getValue
|
|
9
|
+
import androidx.compose.runtime.mutableStateOf
|
|
10
|
+
import androidx.compose.runtime.setValue
|
|
11
|
+
import androidx.compose.ui.Alignment
|
|
12
|
+
import androidx.compose.ui.Modifier
|
|
13
|
+
import androidx.compose.ui.graphics.Color
|
|
14
|
+
import androidx.compose.ui.platform.ComposeView
|
|
15
|
+
import androidx.compose.ui.unit.dp
|
|
16
|
+
import expo.modules.kotlin.AppContext
|
|
17
|
+
import expo.modules.kotlin.views.ExpoView
|
|
18
|
+
|
|
19
|
+
class ExpoOrbView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
|
|
20
|
+
|
|
21
|
+
// Mutable state for configuration
|
|
22
|
+
private var config by mutableStateOf(OrbConfiguration())
|
|
23
|
+
|
|
24
|
+
private val composeView = ComposeView(context).apply {
|
|
25
|
+
setContent {
|
|
26
|
+
OrbViewContent()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
init {
|
|
31
|
+
// Allow glow/shadow effects to render outside view bounds
|
|
32
|
+
clipChildren = false
|
|
33
|
+
clipToPadding = false
|
|
34
|
+
composeView.clipChildren = false
|
|
35
|
+
composeView.clipToPadding = false
|
|
36
|
+
addView(composeView, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Composable
|
|
40
|
+
private fun OrbViewContent() {
|
|
41
|
+
// Add padding so glow/shadow can overflow outside the orb circle
|
|
42
|
+
Box(
|
|
43
|
+
modifier = Modifier
|
|
44
|
+
.fillMaxSize()
|
|
45
|
+
.padding(24.dp), // Space for glow overflow
|
|
46
|
+
contentAlignment = Alignment.Center
|
|
47
|
+
) {
|
|
48
|
+
OrbView(
|
|
49
|
+
config = config,
|
|
50
|
+
useSharedActivityState = true,
|
|
51
|
+
modifier = Modifier.fillMaxSize()
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fun setBackgroundColors(colors: List<Int>) {
|
|
57
|
+
config = config.copy(
|
|
58
|
+
backgroundColors = colors.map { Color(it) }
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fun setGlowColor(color: Int) {
|
|
63
|
+
config = config.copy(glowColor = Color(color))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fun setParticleColor(color: Int) {
|
|
67
|
+
config = config.copy(particleColor = Color(color))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fun setCoreGlowIntensity(value: Double) {
|
|
71
|
+
config = config.copy(coreGlowIntensity = value)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fun setBreathingIntensity(value: Double) {
|
|
75
|
+
config = config.copy(breathingIntensity = value)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fun setBreathingSpeed(value: Double) {
|
|
79
|
+
config = config.copy(breathingSpeed = value)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fun setShowBackground(value: Boolean) {
|
|
83
|
+
config = config.copy(showBackground = value)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
fun setShowWavyBlobs(value: Boolean) {
|
|
87
|
+
config = config.copy(showWavyBlobs = value)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fun setShowParticles(value: Boolean) {
|
|
91
|
+
config = config.copy(showParticles = value)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fun setShowGlowEffects(value: Boolean) {
|
|
95
|
+
config = config.copy(showGlowEffects = value)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
fun setShowShadow(value: Boolean) {
|
|
99
|
+
config = config.copy(showShadow = value)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fun setSpeed(value: Double) {
|
|
103
|
+
config = config.copy(speed = value)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
package expo.modules.orb
|
|
2
|
+
|
|
3
|
+
import androidx.compose.ui.graphics.Color
|
|
4
|
+
|
|
5
|
+
data class OrbConfiguration(
|
|
6
|
+
val backgroundColors: List<Color> = listOf(Color.Green, Color.Blue, Color.Magenta),
|
|
7
|
+
val glowColor: Color = Color.White,
|
|
8
|
+
val particleColor: Color = Color.White,
|
|
9
|
+
val coreGlowIntensity: Double = 1.0,
|
|
10
|
+
val showBackground: Boolean = true,
|
|
11
|
+
val showWavyBlobs: Boolean = true,
|
|
12
|
+
val showParticles: Boolean = true,
|
|
13
|
+
val showGlowEffects: Boolean = true,
|
|
14
|
+
val showShadow: Boolean = true,
|
|
15
|
+
val speed: Double = 60.0,
|
|
16
|
+
val breathingIntensity: Double = 0.0,
|
|
17
|
+
val breathingSpeed: Double = 0.25
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Effective configuration computed from activity level.
|
|
22
|
+
* All speeds must stay CONSTANT to avoid animation jumps.
|
|
23
|
+
*/
|
|
24
|
+
data class EffectiveConfig(
|
|
25
|
+
val speed: Double,
|
|
26
|
+
val breathingIntensity: Double,
|
|
27
|
+
val breathingSpeed: Double,
|
|
28
|
+
val coreGlowIntensity: Double,
|
|
29
|
+
val glowColor: Color
|
|
30
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package expo.modules.orb
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared state for activity animation - mirrors iOS OrbSharedState.
|
|
5
|
+
*
|
|
6
|
+
* This singleton allows smooth animations when bridging React Native ↔ Compose.
|
|
7
|
+
* JS sends target values, native interpolates during animation loop.
|
|
8
|
+
*/
|
|
9
|
+
object OrbSharedState {
|
|
10
|
+
@Volatile
|
|
11
|
+
var targetActivity: Double = 0.0 // Written by native bridge, read by animation loop
|
|
12
|
+
|
|
13
|
+
@Volatile
|
|
14
|
+
var currentActivity: Double = 0.0 // Interpolated value, updated each frame
|
|
15
|
+
|
|
16
|
+
@Volatile
|
|
17
|
+
var lastUpdateTime: Long = System.nanoTime() // For frame-time-based interpolation
|
|
18
|
+
|
|
19
|
+
// Cumulative phase for breathing - allows speed changes without jumps
|
|
20
|
+
@Volatile
|
|
21
|
+
var breathingPhase: Double = 0.0
|
|
22
|
+
|
|
23
|
+
@Volatile
|
|
24
|
+
var lastBreathingUpdate: Long = System.nanoTime()
|
|
25
|
+
}
|