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.
Files changed (55) hide show
  1. package/.eslintrc.js +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +129 -0
  4. package/android/build.gradle +60 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/expo/modules/orb/ExpoOrbModule.kt +79 -0
  7. package/android/src/main/java/expo/modules/orb/ExpoOrbView.kt +105 -0
  8. package/android/src/main/java/expo/modules/orb/OrbConfiguration.kt +30 -0
  9. package/android/src/main/java/expo/modules/orb/OrbSharedState.kt +25 -0
  10. package/android/src/main/java/expo/modules/orb/OrbView.kt +368 -0
  11. package/android/src/main/java/expo/modules/orb/ParticlesView.kt +77 -0
  12. package/android/src/main/java/expo/modules/orb/RotatingGlowView.kt +90 -0
  13. package/android/src/main/java/expo/modules/orb/WavyBlobView.kt +90 -0
  14. package/build/ExpoOrb.types.d.ts +17 -0
  15. package/build/ExpoOrb.types.d.ts.map +1 -0
  16. package/build/ExpoOrb.types.js +2 -0
  17. package/build/ExpoOrb.types.js.map +1 -0
  18. package/build/ExpoOrbModule.d.ts +17 -0
  19. package/build/ExpoOrbModule.d.ts.map +1 -0
  20. package/build/ExpoOrbModule.js +11 -0
  21. package/build/ExpoOrbModule.js.map +1 -0
  22. package/build/ExpoOrbModule.web.d.ts +6 -0
  23. package/build/ExpoOrbModule.web.d.ts.map +1 -0
  24. package/build/ExpoOrbModule.web.js +5 -0
  25. package/build/ExpoOrbModule.web.js.map +1 -0
  26. package/build/ExpoOrbView.d.ts +4 -0
  27. package/build/ExpoOrbView.d.ts.map +1 -0
  28. package/build/ExpoOrbView.js +7 -0
  29. package/build/ExpoOrbView.js.map +1 -0
  30. package/build/ExpoOrbView.web.d.ts +4 -0
  31. package/build/ExpoOrbView.web.d.ts.map +1 -0
  32. package/build/ExpoOrbView.web.js +17 -0
  33. package/build/ExpoOrbView.web.js.map +1 -0
  34. package/build/index.d.ts +4 -0
  35. package/build/index.d.ts.map +1 -0
  36. package/build/index.js +4 -0
  37. package/build/index.js.map +1 -0
  38. package/expo-module.config.json +9 -0
  39. package/ios/ExpoOrb.podspec +30 -0
  40. package/ios/ExpoOrbModule.swift +70 -0
  41. package/ios/ExpoOrbView.swift +105 -0
  42. package/ios/Orb/OrbConfiguration.swift +53 -0
  43. package/ios/Orb/OrbView.swift +367 -0
  44. package/ios/Orb/ParticlesView.swift +156 -0
  45. package/ios/Orb/RealisticShadows.swift +42 -0
  46. package/ios/Orb/RotatingGlowView.swift +62 -0
  47. package/ios/Orb/WavyBlobView.swift +83 -0
  48. package/package.json +46 -0
  49. package/src/ExpoOrb.types.ts +17 -0
  50. package/src/ExpoOrbModule.ts +22 -0
  51. package/src/ExpoOrbModule.web.ts +5 -0
  52. package/src/ExpoOrbView.tsx +11 -0
  53. package/src/ExpoOrbView.web.tsx +26 -0
  54. package/src/index.ts +3 -0
  55. package/tsconfig.json +9 -0
@@ -0,0 +1,83 @@
1
+ import SwiftUI
2
+
3
+ struct WavyBlobView: View {
4
+ @State private var points: [CGPoint] = (0 ..< 6).map { index in
5
+ let angle = (Double(index) / 6) * 2 * .pi
6
+ return CGPoint(
7
+ x: 0.5 + cos(angle) * 0.9,
8
+ y: 0.5 + sin(angle) * 0.9
9
+ )
10
+ }
11
+
12
+ private let color: Color
13
+ private let loopDuration: Double
14
+
15
+ init(color: Color, loopDuration: Double = 1) {
16
+ self.color = color
17
+ self.loopDuration = loopDuration
18
+ }
19
+
20
+ var body: some View {
21
+ TimelineView(.animation) { timeline in
22
+ Canvas { context, size in
23
+ let timeNow = timeline.date.timeIntervalSinceReferenceDate
24
+ let angle = (timeNow.remainder(dividingBy: loopDuration) / loopDuration) * 2 * .pi
25
+
26
+ var path = Path()
27
+ let center = CGPoint(x: size.width / 2, y: size.height / 2)
28
+ let radius = min(size.width, size.height) * 0.45
29
+
30
+ // Move points with larger variations using sine for smooth looping
31
+ let adjustedPoints = points.enumerated().map { index, point in
32
+ let phaseOffset = Double(index) * .pi / 3
33
+ let xOffset = sin(angle + phaseOffset) * 0.15
34
+ let yOffset = cos(angle + phaseOffset) * 0.15
35
+ return CGPoint(
36
+ x: (point.x - 0.5 + xOffset) * radius + center.x,
37
+ y: (point.y - 0.5 + yOffset) * radius + center.y
38
+ )
39
+ }
40
+
41
+ // Start the path
42
+ path.move(to: adjustedPoints[0])
43
+
44
+ // Create smooth curves between points
45
+ for i in 0 ..< adjustedPoints.count {
46
+ let next = (i + 1) % adjustedPoints.count
47
+
48
+ // Calculate the angle between points
49
+ let currentAngle = atan2(
50
+ adjustedPoints[i].y - center.y,
51
+ adjustedPoints[i].x - center.x
52
+ )
53
+ let nextAngle = atan2(
54
+ adjustedPoints[next].y - center.y,
55
+ adjustedPoints[next].x - center.x
56
+ )
57
+
58
+ // Create perpendicular handles
59
+ let handleLength = radius * 0.33
60
+
61
+ let control1 = CGPoint(
62
+ x: adjustedPoints[i].x + cos(currentAngle + .pi / 2) * handleLength,
63
+ y: adjustedPoints[i].y + sin(currentAngle + .pi / 2) * handleLength
64
+ )
65
+
66
+ let control2 = CGPoint(
67
+ x: adjustedPoints[next].x + cos(nextAngle - .pi / 2) * handleLength,
68
+ y: adjustedPoints[next].y + sin(nextAngle - .pi / 2) * handleLength
69
+ )
70
+
71
+ path.addCurve(
72
+ to: adjustedPoints[next],
73
+ control1: control1,
74
+ control2: control2
75
+ )
76
+ }
77
+
78
+ context.fill(path, with: .color(color))
79
+ }
80
+ }
81
+ .animation(.spring(), value: points)
82
+ }
83
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "expo-orb",
3
+ "version": "0.1.0",
4
+ "description": "Animated orb component for React Native (iOS, Android, Web)",
5
+ "main": "build/index.js",
6
+ "types": "build/index.d.ts",
7
+ "scripts": {
8
+ "build": "expo-module build",
9
+ "clean": "expo-module clean",
10
+ "lint": "expo-module lint",
11
+ "test": "expo-module test",
12
+ "prepare": "expo-module prepare",
13
+ "prepublishOnly": "expo-module prepublishOnly",
14
+ "expo-module": "expo-module",
15
+ "open:ios": "xed example/ios",
16
+ "open:android": "open -a \"Android Studio\" example/android"
17
+ },
18
+ "keywords": [
19
+ "react-native",
20
+ "expo",
21
+ "expo-orb",
22
+ "orb",
23
+ "animation",
24
+ "ios",
25
+ "android"
26
+ ],
27
+ "repository": "https://github.com/enso-works/expo-orb",
28
+ "bugs": {
29
+ "url": "https://github.com/enso-works/expo-orb/issues"
30
+ },
31
+ "author": "Ensar Bavrk <ensar.bavrk@gmail.com> (https://github.com/enso-works)",
32
+ "license": "MIT",
33
+ "homepage": "https://github.com/enso-works/expo-orb#readme",
34
+ "dependencies": {},
35
+ "devDependencies": {
36
+ "@types/react": "~19.1.0",
37
+ "expo-module-scripts": "^5.0.8",
38
+ "expo": "^54.0.27",
39
+ "react-native": "0.81.5"
40
+ },
41
+ "peerDependencies": {
42
+ "expo": "*",
43
+ "react": "*",
44
+ "react-native": "*"
45
+ }
46
+ }
@@ -0,0 +1,17 @@
1
+ import type { ColorValue, StyleProp, ViewStyle } from 'react-native';
2
+
3
+ export type ExpoOrbViewProps = {
4
+ backgroundColors?: ColorValue[];
5
+ glowColor?: ColorValue;
6
+ particleColor?: ColorValue;
7
+ coreGlowIntensity?: number;
8
+ breathingIntensity?: number;
9
+ breathingSpeed?: number;
10
+ showBackground?: boolean;
11
+ showWavyBlobs?: boolean;
12
+ showParticles?: boolean;
13
+ showGlowEffects?: boolean;
14
+ showShadow?: boolean;
15
+ speed?: number;
16
+ style?: StyleProp<ViewStyle>;
17
+ };
@@ -0,0 +1,22 @@
1
+ import { NativeModule, requireNativeModule } from 'expo';
2
+
3
+ declare class ExpoOrbModule extends NativeModule {
4
+ /**
5
+ * Set the activity level (0-1) for the orb animation.
6
+ * Uses a native function instead of a prop to bypass React's reconciliation
7
+ * and prevent view re-renders that cause animation flickering.
8
+ */
9
+ setActivity(activity: number): void;
10
+ }
11
+
12
+ const module = requireNativeModule<ExpoOrbModule>('ExpoOrb');
13
+
14
+ export default module;
15
+
16
+ /**
17
+ * Set the orb activity level directly via native function.
18
+ * This bypasses React's prop system to prevent animation interference.
19
+ */
20
+ export function setOrbActivity(activity: number): void {
21
+ module.setActivity(activity);
22
+ }
@@ -0,0 +1,5 @@
1
+ import { registerWebModule, NativeModule } from 'expo';
2
+
3
+ class ExpoOrbModule extends NativeModule {}
4
+
5
+ export default registerWebModule(ExpoOrbModule, 'ExpoOrbModule');
@@ -0,0 +1,11 @@
1
+ import { requireNativeView } from 'expo';
2
+ import * as React from 'react';
3
+
4
+ import { ExpoOrbViewProps } from './ExpoOrb.types';
5
+
6
+ const NativeView: React.ComponentType<ExpoOrbViewProps> =
7
+ requireNativeView('ExpoOrb');
8
+
9
+ export default function ExpoOrbView(props: ExpoOrbViewProps) {
10
+ return <NativeView {...props} />;
11
+ }
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+
3
+ import { ExpoOrbViewProps } from './ExpoOrb.types';
4
+
5
+ const defaultColors = ['#22c55e', '#3b82f6', '#ec4899'];
6
+
7
+ export default function ExpoOrbView(props: ExpoOrbViewProps) {
8
+ const colors = Array.isArray(props.backgroundColors) &&
9
+ props.backgroundColors.every((color) => typeof color === 'string')
10
+ ? (props.backgroundColors as string[])
11
+ : defaultColors;
12
+
13
+ const gradient = `radial-gradient(circle at 30% 30%, ${colors[0]}, ${colors[1] ?? colors[0]}, ${colors[2] ?? colors[1] ?? colors[0]})`;
14
+
15
+ return (
16
+ <div
17
+ style={{
18
+ width: '100%',
19
+ height: '100%',
20
+ borderRadius: '9999px',
21
+ background: gradient,
22
+ boxShadow: '0 0 30px rgba(255,255,255,0.2)',
23
+ }}
24
+ />
25
+ );
26
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { default, setOrbActivity } from './ExpoOrbModule';
2
+ export { default as ExpoOrbView } from './ExpoOrbView';
3
+ export * from './ExpoOrb.types';
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ // @generated by expo-module-scripts
2
+ {
3
+ "extends": "expo-module-scripts/tsconfig.base",
4
+ "compilerOptions": {
5
+ "outDir": "./build"
6
+ },
7
+ "include": ["./src"],
8
+ "exclude": ["**/__mocks__/*", "**/__tests__/*", "**/__rsc_tests__/*"]
9
+ }