react-native-gradient-mask 0.1.0-beta.1

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 (54) hide show
  1. package/.eslintrc.js +5 -0
  2. package/PUBLISHING.md +171 -0
  3. package/README.md +360 -0
  4. package/android/build.gradle +43 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/expo/modules/gradientmask/GradientMaskModule.kt +33 -0
  7. package/android/src/main/java/expo/modules/gradientmask/GradientMaskView.kt +198 -0
  8. package/build/AnimatedGradientMaskView.d.ts +36 -0
  9. package/build/AnimatedGradientMaskView.d.ts.map +1 -0
  10. package/build/AnimatedGradientMaskView.js +37 -0
  11. package/build/AnimatedGradientMaskView.js.map +1 -0
  12. package/build/AnimatedGradientMaskView.web.d.ts +17 -0
  13. package/build/AnimatedGradientMaskView.web.d.ts.map +1 -0
  14. package/build/AnimatedGradientMaskView.web.js +100 -0
  15. package/build/AnimatedGradientMaskView.web.js.map +1 -0
  16. package/build/GradientMask.types.d.ts +38 -0
  17. package/build/GradientMask.types.d.ts.map +1 -0
  18. package/build/GradientMask.types.js +2 -0
  19. package/build/GradientMask.types.js.map +1 -0
  20. package/build/GradientMaskModule.d.ts +3 -0
  21. package/build/GradientMaskModule.d.ts.map +1 -0
  22. package/build/GradientMaskModule.js +4 -0
  23. package/build/GradientMaskModule.js.map +1 -0
  24. package/build/GradientMaskModule.web.d.ts +3 -0
  25. package/build/GradientMaskModule.web.d.ts.map +1 -0
  26. package/build/GradientMaskModule.web.js +3 -0
  27. package/build/GradientMaskModule.web.js.map +1 -0
  28. package/build/GradientMaskView.d.ts +4 -0
  29. package/build/GradientMaskView.d.ts.map +1 -0
  30. package/build/GradientMaskView.js +7 -0
  31. package/build/GradientMaskView.js.map +1 -0
  32. package/build/GradientMaskView.web.d.ts +8 -0
  33. package/build/GradientMaskView.web.d.ts.map +1 -0
  34. package/build/GradientMaskView.web.js +99 -0
  35. package/build/GradientMaskView.web.js.map +1 -0
  36. package/build/index.d.ts +6 -0
  37. package/build/index.d.ts.map +1 -0
  38. package/build/index.js +7 -0
  39. package/build/index.js.map +1 -0
  40. package/expo-module.config.json +9 -0
  41. package/ios/GradientMask.podspec +29 -0
  42. package/ios/GradientMaskModule.swift +29 -0
  43. package/ios/GradientMaskView.swift +133 -0
  44. package/package.json +83 -0
  45. package/rn-gradient-mask-design.md +657 -0
  46. package/src/AnimatedGradientMaskView.tsx +60 -0
  47. package/src/AnimatedGradientMaskView.web.tsx +149 -0
  48. package/src/GradientMask.types.ts +43 -0
  49. package/src/GradientMaskModule.ts +4 -0
  50. package/src/GradientMaskModule.web.ts +2 -0
  51. package/src/GradientMaskView.tsx +11 -0
  52. package/src/GradientMaskView.web.tsx +129 -0
  53. package/src/index.ts +7 -0
  54. package/tsconfig.json +9 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GradientMaskView.web.js","sourceRoot":"","sources":["../src/GradientMaskView.web.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\nimport { View, StyleSheet } from 'react-native';\n\nimport { GradientMaskViewProps } from './GradientMask.types';\n\n/**\n * 將 processColor 處理過的顏色值轉回 rgba 字串\n */\nfunction colorToRgba(color: number | null): string {\n if (color === null || color === undefined) {\n return 'rgba(0, 0, 0, 0)';\n }\n\n // processColor 在 web 上回傳的格式是 AARRGGBB (32-bit integer)\n const intValue = color >>> 0; // 確保是 unsigned\n const a = ((intValue >> 24) & 0xff) / 255;\n const r = (intValue >> 16) & 0xff;\n const g = (intValue >> 8) & 0xff;\n const b = intValue & 0xff;\n\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n}\n\n/**\n * 根據 direction 取得 CSS linear-gradient 的方向\n */\nfunction getGradientDirection(direction: GradientMaskViewProps['direction']): string {\n switch (direction) {\n case 'top':\n return 'to bottom'; // 頂部透明 → 底部不透明\n case 'bottom':\n return 'to top'; // 底部透明 → 頂部不透明\n case 'left':\n return 'to right'; // 左側透明 → 右側不透明\n case 'right':\n return 'to left'; // 右側透明 → 左側不透明\n default:\n return 'to bottom';\n }\n}\n\n/**\n * 建立 CSS linear-gradient 字串\n */\nfunction buildGradientString(\n colors: (number | null)[],\n locations: number[],\n direction: GradientMaskViewProps['direction']\n): string {\n const gradientDirection = getGradientDirection(direction);\n\n const colorStops = colors.map((color, index) => {\n const rgba = colorToRgba(color);\n const location = locations[index] !== undefined ? locations[index] * 100 : (index / (colors.length - 1)) * 100;\n return `${rgba} ${location}%`;\n });\n\n return `linear-gradient(${gradientDirection}, ${colorStops.join(', ')})`;\n}\n\n/**\n * 根據 maskOpacity 調整顏色的 alpha 值\n * maskOpacity = 0 時,所有顏色變為完全不透明(無遮罩效果)\n * maskOpacity = 1 時,保持原本的 alpha 值\n */\nfunction adjustColorsForOpacity(\n colors: (number | null)[],\n maskOpacity: number\n): (number | null)[] {\n if (maskOpacity >= 1) return colors;\n if (maskOpacity <= 0) {\n // 全部變成不透明黑色,表示內容完全可見\n return colors.map(() => 0xff000000);\n }\n\n return colors.map((color) => {\n if (color === null || color === undefined) return color;\n const intValue = color >>> 0;\n const a = ((intValue >> 24) & 0xff) / 255;\n const r = (intValue >> 16) & 0xff;\n const g = (intValue >> 8) & 0xff;\n const b = intValue & 0xff;\n // 根據 maskOpacity 調整 alpha:當 maskOpacity = 0 時 alpha 趨近於 1(完全可見)\n const adjustedAlpha = a + (1 - a) * (1 - maskOpacity);\n return ((Math.round(adjustedAlpha * 255) << 24) | (r << 16) | (g << 8) | b) >>> 0;\n });\n}\n\n/**\n * GradientMaskView - Web 實作\n * 使用 CSS mask-image 搭配 linear-gradient 實現漸層遮罩效果\n */\nexport default function GradientMaskView(props: GradientMaskViewProps) {\n const {\n colors,\n locations,\n direction = 'top',\n maskOpacity = 1,\n style,\n children,\n } = props;\n\n const maskStyle = React.useMemo(() => {\n if (maskOpacity <= 0) {\n // 無遮罩效果\n return {};\n }\n\n const adjustedColors = adjustColorsForOpacity(colors, maskOpacity);\n const gradientString = buildGradientString(adjustedColors, locations, direction);\n\n return {\n WebkitMaskImage: gradientString,\n maskImage: gradientString,\n };\n }, [colors, locations, direction, maskOpacity]);\n\n return (\n <View style={[styles.container, style, maskStyle as any]}>\n {children}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n overflow: 'hidden',\n },\n});\n"]}
@@ -0,0 +1,6 @@
1
+ export { default } from './GradientMaskModule';
2
+ export { default as GradientMaskView } from './GradientMaskView';
3
+ export { default as AnimatedGradientMaskView } from './AnimatedGradientMaskView';
4
+ export type { AnimatedGradientMaskViewProps } from './AnimatedGradientMaskView';
5
+ export * from './GradientMask.types';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACjF,YAAY,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAChF,cAAc,sBAAsB,CAAC"}
package/build/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // Reexport the native module. On web, it will be resolved to GradientMaskModule.web.ts
2
+ // and on native platforms to GradientMaskModule.ts
3
+ export { default } from './GradientMaskModule';
4
+ export { default as GradientMaskView } from './GradientMaskView';
5
+ export { default as AnimatedGradientMaskView } from './AnimatedGradientMaskView';
6
+ export * from './GradientMask.types';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,mDAAmD;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEjF,cAAc,sBAAsB,CAAC","sourcesContent":["// Reexport the native module. On web, it will be resolved to GradientMaskModule.web.ts\n// and on native platforms to GradientMaskModule.ts\nexport { default } from './GradientMaskModule';\nexport { default as GradientMaskView } from './GradientMaskView';\nexport { default as AnimatedGradientMaskView } from './AnimatedGradientMaskView';\nexport type { AnimatedGradientMaskViewProps } from './AnimatedGradientMaskView';\nexport * from './GradientMask.types';\n"]}
@@ -0,0 +1,9 @@
1
+ {
2
+ "platforms": ["apple", "android", "web"],
3
+ "apple": {
4
+ "modules": ["GradientMaskModule"]
5
+ },
6
+ "android": {
7
+ "modules": ["expo.modules.gradientmask.GradientMaskModule"]
8
+ }
9
+ }
@@ -0,0 +1,29 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'GradientMask'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.description = package['description']
10
+ s.license = package['license']
11
+ s.author = package['author']
12
+ s.homepage = package['homepage']
13
+ s.platforms = {
14
+ :ios => '15.1',
15
+ :tvos => '15.1'
16
+ }
17
+ s.swift_version = '5.9'
18
+ s.source = { git: 'https://github.com/CS6/s33' }
19
+ s.static_framework = true
20
+
21
+ s.dependency 'ExpoModulesCore'
22
+
23
+ # Swift/Objective-C compatibility
24
+ s.pod_target_xcconfig = {
25
+ 'DEFINES_MODULE' => 'YES',
26
+ }
27
+
28
+ s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
29
+ end
@@ -0,0 +1,29 @@
1
+ import ExpoModulesCore
2
+
3
+ public class GradientMaskModule: Module {
4
+ public func definition() -> ModuleDefinition {
5
+ Name("GradientMask")
6
+
7
+ View(GradientMaskView.self) {
8
+ // colors: List of processed colors (from processColor in JS)
9
+ Prop("colors") { (view: GradientMaskView, colors: [Int]?) in
10
+ view.setColors(colors)
11
+ }
12
+
13
+ // locations: Array of floats (0-1) for gradient stops
14
+ Prop("locations") { (view: GradientMaskView, locations: [Double]?) in
15
+ view.setLocations(locations)
16
+ }
17
+
18
+ // direction: "top" | "bottom" | "left" | "right"
19
+ Prop("direction") { (view: GradientMaskView, direction: String?) in
20
+ view.setDirection(direction ?? "top")
21
+ }
22
+
23
+ // maskOpacity: 0 = no mask effect, 1 = full gradient mask
24
+ Prop("maskOpacity") { (view: GradientMaskView, opacity: Double?) in
25
+ view.setMaskOpacity(opacity ?? 1.0)
26
+ }
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,133 @@
1
+ import ExpoModulesCore
2
+ import UIKit
3
+
4
+ class GradientMaskView: ExpoView {
5
+
6
+ // MARK: - Properties
7
+
8
+ private let gradientMaskLayer = CAGradientLayer()
9
+ /// Used to overlay gradient when maskOpacity = 0, making content fully visible
10
+ private let solidMaskLayer = CALayer()
11
+
12
+ private var _colors: [CGColor] = [
13
+ UIColor.clear.cgColor,
14
+ UIColor.black.cgColor
15
+ ]
16
+
17
+ private var _locations: [NSNumber] = [0, 1]
18
+ private var _direction: String = "top"
19
+ private var _maskOpacity: CGFloat = 1.0
20
+
21
+ // MARK: - Initialization
22
+
23
+ required init(appContext: AppContext? = nil) {
24
+ super.init(appContext: appContext)
25
+ setupView()
26
+ }
27
+
28
+ private func setupView() {
29
+ backgroundColor = .clear
30
+ clipsToBounds = true
31
+
32
+ // Initialize gradient mask layer
33
+ gradientMaskLayer.colors = _colors
34
+ gradientMaskLayer.locations = _locations
35
+
36
+ // Initialize solid mask layer (opaque black, used to overlay gradient)
37
+ solidMaskLayer.backgroundColor = UIColor.black.cgColor
38
+ // Initially solidMaskLayer is invisible (opacity = 1 - maskOpacity = 0)
39
+ // So mask effect shows full gradient
40
+ solidMaskLayer.opacity = 0.0
41
+
42
+ updateGradientDirection()
43
+ }
44
+
45
+ // MARK: - Props Setters
46
+
47
+ func setColors(_ colors: [Int]?) {
48
+ guard let colors = colors else { return }
49
+ _colors = colors.map { colorValue -> CGColor in
50
+ let intValue = UInt32(bitPattern: Int32(truncatingIfNeeded: colorValue))
51
+ return UIColor(
52
+ red: CGFloat((intValue >> 16) & 0xFF) / 255.0,
53
+ green: CGFloat((intValue >> 8) & 0xFF) / 255.0,
54
+ blue: CGFloat(intValue & 0xFF) / 255.0,
55
+ alpha: CGFloat((intValue >> 24) & 0xFF) / 255.0
56
+ ).cgColor
57
+ }
58
+ updateGradientMask()
59
+ }
60
+
61
+ func setLocations(_ locations: [Double]?) {
62
+ guard let locations = locations else { return }
63
+ _locations = locations.map { NSNumber(value: $0) }
64
+ updateGradientMask()
65
+ }
66
+
67
+ func setDirection(_ direction: String) {
68
+ _direction = direction
69
+ updateGradientDirection()
70
+ updateGradientMask()
71
+ }
72
+
73
+ func setMaskOpacity(_ opacity: Double) {
74
+ _maskOpacity = CGFloat(opacity)
75
+ updateMaskOpacity()
76
+ }
77
+
78
+ // MARK: - Layout
79
+
80
+ override func layoutSubviews() {
81
+ super.layoutSubviews()
82
+ updateGradientMask()
83
+ }
84
+
85
+ // MARK: - Gradient Mask
86
+
87
+ private func updateGradientMask() {
88
+ gradientMaskLayer.frame = bounds
89
+ solidMaskLayer.frame = bounds
90
+
91
+ gradientMaskLayer.colors = _colors
92
+ gradientMaskLayer.locations = _locations
93
+
94
+ updateGradientDirection()
95
+
96
+ // Create composite mask: gradient + solid overlay
97
+ let compositeMask = CALayer()
98
+ compositeMask.frame = bounds
99
+ compositeMask.addSublayer(gradientMaskLayer)
100
+ compositeMask.addSublayer(solidMaskLayer)
101
+
102
+ layer.mask = compositeMask
103
+ }
104
+
105
+ private func updateMaskOpacity() {
106
+ CATransaction.begin()
107
+ CATransaction.setDisableActions(true)
108
+ // maskOpacity = 0 → solidMaskLayer.opacity = 1 → content fully visible
109
+ // maskOpacity = 1 → solidMaskLayer.opacity = 0 → full gradient effect
110
+ solidMaskLayer.opacity = Float(1.0 - _maskOpacity)
111
+ CATransaction.commit()
112
+ }
113
+
114
+ private func updateGradientDirection() {
115
+ switch _direction {
116
+ case "top":
117
+ gradientMaskLayer.startPoint = CGPoint(x: 0.5, y: 0)
118
+ gradientMaskLayer.endPoint = CGPoint(x: 0.5, y: 1)
119
+ case "bottom":
120
+ gradientMaskLayer.startPoint = CGPoint(x: 0.5, y: 1)
121
+ gradientMaskLayer.endPoint = CGPoint(x: 0.5, y: 0)
122
+ case "left":
123
+ gradientMaskLayer.startPoint = CGPoint(x: 0, y: 0.5)
124
+ gradientMaskLayer.endPoint = CGPoint(x: 1, y: 0.5)
125
+ case "right":
126
+ gradientMaskLayer.startPoint = CGPoint(x: 1, y: 0.5)
127
+ gradientMaskLayer.endPoint = CGPoint(x: 0, y: 0.5)
128
+ default:
129
+ gradientMaskLayer.startPoint = CGPoint(x: 0.5, y: 0)
130
+ gradientMaskLayer.endPoint = CGPoint(x: 0.5, y: 1)
131
+ }
132
+ }
133
+ }
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "react-native-gradient-mask",
3
+ "version": "0.1.0-beta.1",
4
+ "description": "A native gradient mask component for React Native with Reanimated animation support. Supports iOS, Android, and Web platforms.",
5
+ "main": "build/index.js",
6
+ "module": "build/index.js",
7
+ "types": "build/index.d.ts",
8
+ "sideEffects": false,
9
+ "scripts": {
10
+ "build": "expo-module build",
11
+ "clean": "expo-module clean",
12
+ "lint": "expo-module lint",
13
+ "test": "expo-module test",
14
+ "prepare": "expo-module prepare",
15
+ "prepublishOnly": "expo-module prepublishOnly",
16
+ "expo-module": "expo-module",
17
+ "open:ios": "xed example/ios",
18
+ "open:android": "open -a \"Android Studio\" example/android"
19
+ },
20
+ "keywords": [
21
+ "react-native",
22
+ "expo",
23
+ "gradient",
24
+ "mask",
25
+ "fade",
26
+ "opacity",
27
+ "animation",
28
+ "reanimated",
29
+ "native",
30
+ "ios",
31
+ "android",
32
+ "web",
33
+ "gradient-mask",
34
+ "fade-effect",
35
+ "scroll-fade",
36
+ "list-mask"
37
+ ],
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/CS6/react-native-gradient-mask.git"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/CS6/react-native-gradient-mask/issues"
44
+ },
45
+ "author": {
46
+ "name": "DaYuan Lin",
47
+ "email": "dayuan.code@gmail.com",
48
+ "url": "https://github.com/CS6"
49
+ },
50
+ "license": "MIT",
51
+ "homepage": "https://github.com/CS6/react-native-gradient-mask#readme",
52
+ "funding": {
53
+ "type": "github",
54
+ "url": "https://github.com/sponsors/CS6"
55
+ },
56
+ "engines": {
57
+ "node": ">=18.0.0"
58
+ },
59
+ "os": [
60
+ "darwin",
61
+ "linux",
62
+ "win32"
63
+ ],
64
+ "dependencies": {},
65
+ "devDependencies": {
66
+ "@types/react": "~19.1.0",
67
+ "expo": "^54.0.27",
68
+ "expo-module-scripts": "^5.0.8",
69
+ "react-native": "0.81.5",
70
+ "react-native-reanimated": "^3.0.0"
71
+ },
72
+ "peerDependencies": {
73
+ "expo": "*",
74
+ "react": "*",
75
+ "react-native": "*",
76
+ "react-native-reanimated": ">=3.0.0"
77
+ },
78
+ "peerDependenciesMeta": {
79
+ "react-native-reanimated": {
80
+ "optional": true
81
+ }
82
+ }
83
+ }