Skip to content

Commit c25c0c0

Browse files
committed
Init package
1 parent 92ba3bf commit c25c0c0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+295
-28
lines changed

.editorconfig

100644100755
File mode changed.

.gitattributes

100644100755
File mode changed.

.github/actions/setup/action.yml

100644100755
File mode changed.

.github/workflows/ci.yml

100644100755
File mode changed.

.gitignore

100644100755
File mode changed.

.nvmrc

100644100755
File mode changed.

.watchmanconfig

100644100755
File mode changed.

.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs

100644100755
File mode changed.

.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs

100644100755
File mode changed.

.yarnrc.yml

100644100755
File mode changed.

CODE_OF_CONDUCT.md

100644100755
File mode changed.

CONTRIBUTING.md

100644100755
File mode changed.

LICENSE

100644100755
File mode changed.

README.md

100644100755
File mode changed.

android/build.gradle

100644100755
File mode changed.

android/gradle.properties

100644100755
File mode changed.

android/src/main/AndroidManifest.xml

100644100755
File mode changed.

android/src/main/AndroidManifestNew.xml

100644100755
File mode changed.

android/src/main/java/com/meshgradient/MeshGradientPackage.kt

100644100755
File mode changed.

android/src/main/java/com/meshgradient/MeshGradientViewManager.kt

100644100755
File mode changed.

babel.config.js

100644100755
File mode changed.

example/.watchmanconfig

100644100755
File mode changed.

example/android/build.gradle

100644100755
File mode changed.

example/android/gradle.properties

100644100755
File mode changed.

example/android/gradle/wrapper/gradle-wrapper.jar

100644100755
File mode changed.

example/android/gradle/wrapper/gradle-wrapper.properties

100644100755
File mode changed.

example/android/gradlew.bat

100644100755
File mode changed.

example/android/settings.gradle

100644100755
File mode changed.

example/app.json

100644100755
File mode changed.

example/babel.config.js

100644100755
File mode changed.

example/index.js

100644100755
File mode changed.

example/ios/File.swift

100644100755
File mode changed.

example/ios/MeshGradientExample-Bridging-Header.h

100644100755
File mode changed.

example/ios/Podfile

100644100755
File mode changed.

example/metro.config.js

100644100755
File mode changed.

example/package.json

100644100755
File mode changed.

example/react-native.config.js

100644100755
File mode changed.

example/src/App.tsx

100644100755
File mode changed.

ios/MeshGradient-Bridging-Header.h

100644100755
File mode changed.

ios/MeshGradientView.swift

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//
2+
// MeshGradientView.swift
3+
// reactnativemeshgradient
4+
//
5+
// Created by wilmxre on 27.07.2024.
6+
//
7+
8+
import ExpoModulesCore
9+
import SwiftUI
10+
11+
@available(iOS 16.0, *)
12+
struct MeshGradientView: View {
13+
var meshWidth: Int
14+
var meshHeight: Int
15+
var points: [SIMD2<Float>]
16+
var primaryColors: [Color]
17+
var secondaryColors: [Color]
18+
var background: Color = .clear
19+
var smoothsColors: Bool
20+
var colorSpace: Gradient.ColorSpace
21+
var borderRadius: CGFloat
22+
var isAnimated: Bool
23+
var animationDuration: TimeInterval
24+
var animationType: String
25+
26+
@State private var isAnimating = false
27+
28+
@State var t: Float = 0.0
29+
@State var timer: Timer?
30+
31+
var body: some View {
32+
ZStack {
33+
if #available(iOS 18.0, *) {
34+
MeshGradient(
35+
width: meshWidth,
36+
height: meshHeight,
37+
points: animationType == "sine"
38+
? [
39+
.init(0, 0), .init(0.5, 0), .init(1, 0),
40+
[
41+
sinInRange(
42+
-0.8...(-0.2), offset: 0.439, timeScale: 0.342, t: t),
43+
sinInRange(0.3...0.7, offset: 3.42, timeScale: 0.984, t: t),
44+
],
45+
[
46+
sinInRange(0.1...0.8, offset: 0.239, timeScale: 0.084, t: t),
47+
sinInRange(0.2...0.8, offset: 5.21, timeScale: 0.242, t: t),
48+
],
49+
[
50+
sinInRange(1.0...1.5, offset: 0.939, timeScale: 0.084, t: t),
51+
sinInRange(0.4...0.8, offset: 0.25, timeScale: 0.642, t: t),
52+
],
53+
[
54+
sinInRange(-0.8...0.0, offset: 1.439, timeScale: 0.442, t: t),
55+
sinInRange(1.4...1.9, offset: 3.42, timeScale: 0.984, t: t),
56+
],
57+
[
58+
sinInRange(0.3...0.6, offset: 0.339, timeScale: 0.784, t: t),
59+
sinInRange(1.0...1.2, offset: 1.22, timeScale: 0.772, t: t),
60+
],
61+
[
62+
sinInRange(1.0...1.5, offset: 0.939, timeScale: 0.056, t: t),
63+
sinInRange(1.3...1.7, offset: 0.47, timeScale: 0.342, t: t),
64+
],
65+
] : points,
66+
colors: isAnimating ? secondaryColors : primaryColors,
67+
background: background,
68+
smoothsColors: smoothsColors,
69+
colorSpace: colorSpace
70+
)
71+
.cornerRadius(borderRadius)
72+
.onAppear {
73+
if isAnimated {
74+
animationType == "sine"
75+
? timer = Timer.scheduledTimer(
76+
withTimeInterval: 0.01, repeats: true
77+
) { _ in
78+
t += 0.02
79+
}
80+
: withAnimation(
81+
.easeInOut(duration: animationDuration).repeatForever(
82+
autoreverses: true)
83+
) {
84+
isAnimating.toggle()
85+
}
86+
}
87+
}
88+
} else {
89+
Text("MeshGradient is not available on this version of iOS.")
90+
}
91+
}
92+
93+
}
94+
}
95+
96+
func sinInRange(
97+
_ range: ClosedRange<Float>, offset: Float, timeScale: Float, t: Float
98+
) -> Float {
99+
let amplitude = (range.upperBound - range.lowerBound) / 2
100+
let midPoint = (range.upperBound + range.lowerBound) / 2
101+
return midPoint + amplitude * sin(timeScale * t + offset)
102+
}

ios/MeshGradientViewManager.m

100644100755
+20-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
1+
//
2+
// MeshGradient.m
3+
// reactnativemeshgradient
4+
//
5+
// Created by wilmxore on 27.07.2024.
6+
//
7+
18
#import <React/RCTViewManager.h>
29

310
@interface RCT_EXTERN_MODULE(MeshGradientViewManager, RCTViewManager)
411

5-
RCT_EXPORT_VIEW_PROPERTY(color, NSString)
12+
RCT_EXPORT_VIEW_PROPERTY(isAnimated, BOOL)
13+
RCT_EXPORT_VIEW_PROPERTY(meshWidth, NSInteger)
14+
RCT_EXPORT_VIEW_PROPERTY(meshHeight, NSInteger)
15+
RCT_EXPORT_VIEW_PROPERTY(points, NSArray)
16+
RCT_EXPORT_VIEW_PROPERTY(primaryColors, NSArray)
17+
RCT_EXPORT_VIEW_PROPERTY(secondaryColors, NSArray)
18+
RCT_EXPORT_VIEW_PROPERTY(background, NSString)
19+
RCT_EXPORT_VIEW_PROPERTY(smoothsColors, BOOL)
20+
RCT_EXPORT_VIEW_PROPERTY(colorSpace, NSString)
21+
RCT_EXPORT_VIEW_PROPERTY(borderRadius, CGFloat)
22+
RCT_EXPORT_VIEW_PROPERTY(animationDuration, NSInteger)
23+
RCT_EXPORT_VIEW_PROPERTY(animationType, NSString)
624

7-
@end
25+
@end

ios/MeshGradientViewManager.swift

100644100755
+156-19
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,173 @@
1+
//
2+
// MeshGradientViewManager.swift
3+
// reactnativemeshgradient
4+
//
5+
// Created by wilmxre on 27.07.2024.
6+
//
7+
8+
import ExpoModulesCore
9+
import React
10+
import SwiftUI
11+
112
@objc(MeshGradientViewManager)
213
class MeshGradientViewManager: RCTViewManager {
3-
4-
override func view() -> (MeshGradientView) {
5-
return MeshGradientView()
14+
override func view() -> UIView! {
15+
if #available(iOS 16.0, *) {
16+
return MeshGradientUIView()
17+
} else {
18+
return UIView()
19+
}
620
}
721

8-
@objc override static func requiresMainQueueSetup() -> Bool {
9-
return false
22+
override static func requiresMainQueueSetup() -> Bool {
23+
return true
1024
}
1125
}
1226

13-
class MeshGradientView : UIView {
27+
@available(iOS 16.0, *)
28+
class MeshGradientUIView: UIView {
29+
private var hostingController: UIHostingController<MeshGradientView>?
1430

15-
@objc var color: String = "" {
31+
@objc var meshWidth: Int = 3 {
1632
didSet {
17-
self.backgroundColor = hexStringToUIColor(hexColor: color)
33+
updateView()
1834
}
1935
}
2036

21-
func hexStringToUIColor(hexColor: String) -> UIColor {
22-
let stringScanner = Scanner(string: hexColor)
37+
@objc var meshHeight: Int = 3 {
38+
didSet {
39+
updateView()
40+
}
41+
}
2342

24-
if(hexColor.hasPrefix("#")) {
25-
stringScanner.scanLocation = 1
43+
@objc var points: [[NSNumber]] = [] {
44+
didSet {
45+
updateView()
2646
}
27-
var color: UInt32 = 0
28-
stringScanner.scanHexInt32(&color)
47+
}
2948

30-
let r = CGFloat(Int(color >> 16) & 0x000000FF)
31-
let g = CGFloat(Int(color >> 8) & 0x000000FF)
32-
let b = CGFloat(Int(color) & 0x000000FF)
49+
@objc var primaryColors: [NSString] = [] {
50+
didSet {
51+
updateView()
52+
}
53+
}
3354

34-
return UIColor(red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: 1)
55+
@objc var secondaryColors: [NSString] = [] {
56+
didSet {
57+
updateView()
58+
}
3559
}
36-
}
60+
61+
@objc var background: NSString = "" {
62+
didSet {
63+
updateView()
64+
}
65+
}
66+
67+
@objc var smoothsColors: Bool = true {
68+
didSet {
69+
updateView()
70+
}
71+
}
72+
73+
@objc var colorSpace: NSString = "device" {
74+
didSet {
75+
updateView()
76+
}
77+
}
78+
79+
@objc var isAnimated: Bool = false {
80+
didSet {
81+
updateView()
82+
}
83+
}
84+
85+
@objc var animationDuration: TimeInterval = 5 {
86+
didSet {
87+
updateView()
88+
}
89+
}
90+
91+
@objc var animationType: String = "sine" {
92+
didSet {
93+
updateView()
94+
}
95+
}
96+
97+
@objc var borderRadius: CGFloat = 0 {
98+
didSet {
99+
updateView()
100+
}
101+
}
102+
103+
private func colorFromHexString(hexString: NSString) -> UIColor {
104+
var cString: String = hexString.trimmingCharacters(
105+
in: .whitespacesAndNewlines
106+
).uppercased()
107+
108+
if cString.hasPrefix("#") {
109+
cString.remove(at: cString.startIndex)
110+
}
111+
112+
if cString.count != 6 {
113+
return UIColor.white
114+
}
115+
116+
var rgbValue: UInt64 = 0
117+
Scanner(string: cString).scanHexInt64(&rgbValue)
118+
119+
return UIColor(
120+
red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
121+
green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
122+
blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
123+
alpha: CGFloat(1.0)
124+
)
125+
}
126+
127+
private func updateView() {
128+
129+
let pointsSIMD = points.map {
130+
SIMD2<Float>(Float(truncating: $0[0]), Float(truncating: $0[1]))
131+
}
132+
let primaryColorsSwiftUI = primaryColors.map {
133+
Color(colorFromHexString(hexString: $0))
134+
}
135+
let secondaryColorsSwiftUI = secondaryColors.map {
136+
Color(colorFromHexString(hexString: $0))
137+
}
138+
let backgroundColor = Color(colorFromHexString(hexString: background))
139+
let colorSpaceEnum: Gradient.ColorSpace =
140+
colorSpace == "perceptual" ? .perceptual : .device
141+
142+
let meshGradientView = MeshGradientView(
143+
meshWidth: meshWidth,
144+
meshHeight: meshHeight,
145+
points: pointsSIMD,
146+
primaryColors: primaryColorsSwiftUI,
147+
secondaryColors: secondaryColorsSwiftUI,
148+
background: backgroundColor,
149+
smoothsColors: smoothsColors,
150+
colorSpace: colorSpaceEnum,
151+
borderRadius: borderRadius,
152+
isAnimated: isAnimated,
153+
animationDuration: animationDuration,
154+
animationType: animationType
155+
)
156+
157+
if hostingController == nil {
158+
hostingController = UIHostingController(rootView: meshGradientView)
159+
if let hostingView = hostingController?.view {
160+
addSubview(hostingView)
161+
hostingView.translatesAutoresizingMaskIntoConstraints = false
162+
NSLayoutConstraint.activate([
163+
hostingView.topAnchor.constraint(equalTo: topAnchor),
164+
hostingView.bottomAnchor.constraint(equalTo: bottomAnchor),
165+
hostingView.leadingAnchor.constraint(equalTo: leadingAnchor),
166+
hostingView.trailingAnchor.constraint(equalTo: trailingAnchor),
167+
])
168+
}
169+
} else {
170+
hostingController?.rootView = meshGradientView
171+
}
172+
}
173+
}

lefthook.yml

100644100755
File mode changed.

package.json

100644100755
File mode changed.

react-native-mesh-gradient.podspec

100644100755
File mode changed.

src/__tests__/index.test.tsx

100644100755
File mode changed.

src/index.tsx

100644100755
+17-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
requireNativeComponent,
33
UIManager,
44
Platform,
5-
type ViewStyle,
5+
type ViewProps,
66
} from 'react-native';
77

88
const LINKING_ERROR =
@@ -11,16 +11,26 @@ const LINKING_ERROR =
1111
'- You rebuilt the app after installing the package\n' +
1212
'- You are not using Expo Go\n';
1313

14-
type MeshGradientProps = {
15-
color: string;
16-
style: ViewStyle;
17-
};
18-
14+
export type MeshGradientProps = ViewProps & {
15+
meshWidth: number;
16+
meshHeight: number;
17+
points: number[][];
18+
primaryColors: string[];
19+
secondaryColors: string[];
20+
background?: string;
21+
smoothsColors?: boolean;
22+
colorSpace?: "device" | "perceptual";
23+
isAnimated?: boolean;
24+
borderRadius?: number;
25+
animationDuration?: number;
26+
animationType?: "sine" | "easeInOut";
27+
};
28+
1929
const ComponentName = 'MeshGradientView';
2030

2131
export const MeshGradientView =
2232
UIManager.getViewManagerConfig(ComponentName) != null
2333
? requireNativeComponent<MeshGradientProps>(ComponentName)
2434
: () => {
2535
throw new Error(LINKING_ERROR);
26-
};
36+
};

tsconfig.build.json

100644100755
File mode changed.

tsconfig.json

100644100755
File mode changed.

turbo.json

100644100755
File mode changed.

0 commit comments

Comments
 (0)