rn-marquee-text 2.0.3 → 2.0.5

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/README.md CHANGED
@@ -1,233 +1,146 @@
1
- # React Native Marquee Text
1
+ # React Native Marquee Text 🏃‍♂️
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/rn-marquee-text?style=flat-square)](https://www.npmjs.com/package/rn-marquee-text)
4
+ [![license](https://img.shields.io/npm/l/rn-marquee-text?style=flat-square)](https://github.com/yourusername/rn-marquee-text/blob/main/LICENSE)
5
+ [![downloads](https://img.shields.io/npm/dt/rn-marquee-text?style=flat-square)](https://www.npmjs.com/package/rn-marquee-text)
6
+ [![CI Status](https://img.shields.io/github/actions/workflow/status/yourusername/rn-marquee-text/ci.yml?style=flat-square)](https://github.com/yourusername/rn-marquee-text/actions)
3
7
 
4
- A highly customizable package for smooth, auto-scrolling marquee text components in React Native apps. Works seamlessly in both React Native CLI and Expo environments.
8
+ A high-performance, customizable marquee component for React Native with smooth animations and gesture support.
5
9
 
6
- ![npm version](https://img.shields.io/npm/v/rn-marquee-text.svg)
7
- ![license](https://img.shields.io/npm/l/rn-marquee-text.svg)
8
- ![downloads](https://img.shields.io/npm/dm/rn-marquee-text.svg)
10
+ ![Demo](https://github.com/yourusername/rn-marquee-text/blob/main/assets/demo.gif?raw=true)
9
11
 
10
- ## 🚀 Features
12
+ ## Features
11
13
 
12
- - Smooth scrolling powered by React Native Reanimated
13
- - 🧩 Fully compatible with React Native & Expo
14
- - 🔁 Supports loop and bounce animation modes
15
- - 🎛️ Customizable speed, delay, and styles
16
- - 📱 No dependencies except Reanimated
17
- - 📏 Auto-detects overflow for auto-scrolling
18
- - ↔️ Supports horizontal and vertical scrolling
19
- - 📰 Built-in MarqueeText for ticker-style effects
14
+ - 🎭 Multiple animation modes (loop, bounce)
15
+ - ↔️ Bidirectional scrolling (horizontal/vertical)
16
+ - Touch gesture interaction
17
+ - Optimized with Reanimated 2
18
+ - 🎨 Customizable speed, spacing, and delays
19
+ - 📱 Supports both iOS and Android
20
+ - 🛠 TypeScript support included
20
21
 
21
- ## 📦 Installation
22
+ ## Installation 💻
22
23
 
23
24
  ```bash
24
- # React Native CLI
25
- npm install rn-marquee-text react-native-reanimated
25
+ # Using npm
26
+ npm install rn-marquee-text react-native-reanimated react-native-gesture-handler
26
27
 
27
- # OR using yarn
28
- yarn add rn-marquee-text react-native-reanimated
29
- ```
28
+ # Using yarn
29
+ yarn add rn-marquee-text react-native-reanimated react-native-gesture-handler
30
+ Peer Dependencies Setup
31
+ Follow Reanimated installation guide
30
32
 
31
- ### Expo Users
33
+ Configure Gesture Handler
32
34
 
33
- ```bash
34
- expo install react-native-reanimated
35
- ```
36
-
37
- Then update your `babel.config.js`:
38
-
39
- ```js
40
- module.exports = {
41
- presets: ['babel-preset-expo'],
42
- plugins: ['react-native-reanimated/plugin'],
43
- };
44
- ```
45
-
46
- ## 🧱 Components
47
-
48
- ### 1️⃣ AutoScroll — Scroll any overflowed content
49
-
50
- #### Basic Usage
51
-
52
- ```tsx
53
- import AutoScroll, { AnimationMode } from 'rn-marquee-text';
54
-
55
- const Example = () => (
56
- <AutoScroll
57
- style={{ height: 100, width: '100%' }}
58
- mode={AnimationMode.LOOP}
59
- speed={40}
60
- >
61
- This is a long text that will scroll if it overflows its container.
62
- </AutoScroll>
63
- );
64
- ```
65
-
66
- #### Props
67
-
68
- | Prop | Type | Default | Description |
69
- |------|------|---------|-------------|
70
- | children | ReactNode | (required) | Content to scroll |
71
- | mode | 'loop' \| 'bounce' | 'loop' | Animation type |
72
- | speed | number | 30 | Speed in px/sec |
73
- | delay | number | 1500 | Delay before animation starts |
74
- | endPauseDuration | number | 1000 | Pause time at edges (for bounce mode) |
75
- | style | ViewStyle | {} | Container styling |
76
- | textStyle | TextStyle | {} | Text styling (when children is string) |
77
- | enabled | boolean | true | Enable or disable animation |
78
- | direction | 'horizontal' \| 'vertical' | 'vertical' | Scroll direction |
79
-
80
- ### 2️⃣ MarqueeText — For ticker-style text
81
-
82
- #### Basic Usage
83
-
84
- ```tsx
85
- import { MarqueeText } from 'rn-marquee-text';
86
- import { View, Text } from 'react-native';
87
-
88
- const Example = () => (
89
- <View style={{ width: '100%', borderRadius: 8, overflow: 'hidden' }}>
35
+ Basic Usage 🚀
36
+ jsx
37
+ import MarqueeText from 'rn-marquee-text';
38
+
39
+ function App() {
40
+ return (
90
41
  <MarqueeText
91
- speed={100}
92
- backgroundColor="#1a365d"
93
- textColor="#f0f4f8"
94
- fontSize={14}
95
- direction="horizontal"
42
+ speed={50}
43
+ style={{ height: 40 }}
44
+ textStyle={{ fontSize: 16 }}
96
45
  >
97
- <Text>
98
- Breaking News: This is a marquee text scrolling horizontally!
99
- </Text>
100
- </MarqueeText>
101
- </View>
102
- );
103
- ```
104
-
105
- #### Props
106
-
107
- | Prop | Type | Default | Description |
108
- |------|------|---------|-------------|
109
- | text | string | (required) | Text to display (alternative to children) |
110
- | speed | number | 80 | Speed in px/sec |
111
- | backgroundColor | string | #000 | Marquee background color |
112
- | textColor | string | #fff | Text color |
113
- | fontSize | number | 16 | Font size |
114
- | paddingVertical | number | 8 | Vertical padding |
115
- | paddingHorizontal | number | 12 | Horizontal padding |
116
- | delay | number | 1000 | Delay before animation starts |
117
- | bounceMode | boolean | false | Whether to bounce at edges |
118
- | endPauseDuration | number | 2000 | Pause at each end (only in bounce mode) |
119
-
120
- ## 💡 Advanced Examples
121
-
122
- ### 🔁 AutoScroll with Custom Content
123
-
124
- ```tsx
125
- import React from 'react';
126
- import { View, Text, StyleSheet } from 'react-native';
127
- import AutoScroll from 'rn-marquee-text';
128
-
129
- const CardScroller = () => (
130
- <AutoScroll style={styles.scrollContainer} direction="horizontal">
131
- <View style={styles.contentContainer}>
132
- {[1, 2, 3, 4, 5].map((item) => (
133
- <View key={item} style={styles.card}>
134
- <Text style={styles.cardText}>Card {item}</Text>
135
- </View>
136
- ))}
137
- </View>
138
- </AutoScroll>
139
- );
140
-
141
- const styles = StyleSheet.create({
142
- scrollContainer: {
143
- height: 120,
144
- width: '100%',
145
- },
146
- contentContainer: {
147
- flexDirection: 'row',
148
- padding: 10,
149
- },
150
- card: {
151
- width: 100,
152
- height: 100,
153
- backgroundColor: '#f0f0f0',
154
- borderRadius: 8,
155
- marginRight: 10,
156
- justifyContent: 'center',
157
- alignItems: 'center',
158
- },
159
- cardText: {
160
- fontSize: 16,
161
- fontWeight: 'bold',
162
- },
163
- });
164
- ```
165
-
166
- ### 📰 News Ticker with MarqueeText
167
-
168
- ```tsx
169
- import React from 'react';
170
- import { View, Text, StyleSheet } from 'react-native';
171
- import { MarqueeText } from 'rn-marquee-text';
172
-
173
- const NewsTicker = () => (
174
- <View style={styles.tickerContainer}>
175
- <MarqueeText speed={70} bounceMode={false}>
176
- <Text style={styles.cardText}>
177
- BREAKING NEWS: Latest updates on global events. Markets reach all-time high.
178
- </Text>
46
+ This text will scroll automatically!
179
47
  </MarqueeText>
180
- </View>
181
- );
182
-
183
- const styles = StyleSheet.create({
184
- tickerContainer: {
185
- width: '100%',
186
- borderRadius: 4,
187
- overflow: 'hidden',
188
- marginVertical: 10,
189
- },
190
- cardText: {
191
- fontSize: 14,
192
- paddingHorizontal: 10,
193
- },
194
- });
195
- ```
196
-
197
- ## 🛠️ Tips for Optimal Performance
198
-
199
- - Match speed with content length for readability
200
- - Use delays to give users time to read before scrolling
201
- - Bounce mode is more attention-grabbing (e.g., for alerts)
202
- - Limit concurrent scroll components to avoid frame drops
203
- - Test on low-end devices for smooth performance
204
-
205
- ## 🧩 Troubleshooting
206
-
207
- ### ❌ Text Not Scrolling?
208
-
209
- - Ensure content overflows the container
210
- - Confirm `enabled` is set to `true`
211
- - Use `overflow: 'hidden'` on container
212
-
213
- ### 🐢 Choppy Animations?
214
-
215
- - Leverage `useNativeDriver` if supported
216
- - Minimize simultaneous animations
217
- - Simplify nested component structure
218
-
219
- ## 📄 License
220
-
221
- MIT
222
-
223
- ## 🤝 Contributing
224
-
225
- Pull requests welcome!
226
-
227
- Steps:
228
-
229
- 1. Fork the repo
230
- 2. Create a branch: `git checkout -b feature/amazing-feature`
231
- 3. Commit your changes: `git commit -m 'Add some amazing feature'`
232
- 4. Push the branch: `git push origin feature/amazing-feature`
233
- 5. Open a Pull Request
48
+ );
49
+ }
50
+ Advanced Usage 🧠
51
+ Custom Animation Configuration
52
+ jsx
53
+ <MarqueeText
54
+ mode="bounce"
55
+ direction="vertical"
56
+ speed={30}
57
+ delay={2000}
58
+ endPauseDuration={1500}
59
+ spacing={30}
60
+ >
61
+ {yourCustomComponent}
62
+ </MarqueeText>
63
+ Imperative Control
64
+ jsx
65
+ const marqueeRef = useRef(null);
66
+
67
+ // Start/pause programmatically
68
+ <>
69
+ <MarqueeText ref={marqueeRef} />
70
+ <Button
71
+ title="Toggle"
72
+ onPress={() => marqueeRef.current?.isActive
73
+ ? marqueeRef.current.stop()
74
+ : marqueeRef.current.start()
75
+ }
76
+ />
77
+ </>```
78
+
79
+ API Reference 📚
80
+ Props
81
+ Prop Type Default Description
82
+ mode 'loop' | 'bounce' 'loop' Animation behavior
83
+ speed number 30 Scroll speed (px/sec)
84
+ direction 'horizontal' | 'vertical' 'horizontal' Scroll axis
85
+ delay number 1500 Initial delay (ms)
86
+ spacing number 20 Space between clones
87
+ enabled boolean true Animation state
88
+ Methods (via ref)
89
+ typescript
90
+ interface MarqueeRef {
91
+ start: () => void;
92
+ stop: () => void;
93
+ isActive: boolean;
94
+ }
95
+ Examples 🎨
96
+ News Ticker
97
+ jsx
98
+ <MarqueeText
99
+ style={styles.ticker}
100
+ textStyle={styles.tickerText}
101
+ speed={40}
102
+ >
103
+ BREAKING: React Native Marquee Text released! • New version available • Check out the docs
104
+ </MarqueeText>
105
+ Vertical Product Carousel
106
+ jsx
107
+ <MarqueeText
108
+ direction="vertical"
109
+ mode="bounce"
110
+ speed={20}
111
+ style={styles.carousel}
112
+ >
113
+ <ProductCard {...item1} />
114
+ <ProductCard {...item2} />
115
+ <ProductCard {...item3} />
116
+ </MarqueeText>
117
+ Troubleshooting ⚠️
118
+ Animation not smooth?
119
+
120
+ Ensure you're using Reanimated 2+
121
+
122
+ Check for heavy renders in parent components
123
+
124
+ Text not visible?
125
+
126
+ Verify container has explicit dimensions
127
+
128
+ Check text color contrast
129
+
130
+ Gesture not working?
131
+
132
+ Confirm Gesture Handler installation
133
+
134
+ Wrap root component with GestureHandlerRootView
135
+
136
+ Contributing 🤝
137
+ We welcome contributions! Please:
138
+
139
+ Fork the repository
140
+
141
+ Create a feature branch
142
+
143
+ Submit a pull request
144
+
145
+ License 📄
146
+ MIT © PARESH CHAVDA
@@ -15,7 +15,7 @@ import { TextStyle, ViewStyle } from 'react-native';
15
15
  * @prop {boolean} enabled - Whether the scrolling animation is enabled
16
16
  * @prop {'horizontal'|'vertical'} direction - The direction of the scrolling animation
17
17
  */
18
- export declare const AutoScroll: ({ children, mode, speed, delay, endPauseDuration, style, textStyle, enabled, direction, }: {
18
+ declare const AutoScroll: ({ children, mode, speed, delay, endPauseDuration, style, textStyle, enabled, direction, }: {
19
19
  children: React.ReactNode;
20
20
  mode?: "loop" | "bounce";
21
21
  speed?: number;
@@ -26,3 +26,4 @@ export declare const AutoScroll: ({ children, mode, speed, delay, endPauseDurati
26
26
  enabled?: boolean;
27
27
  direction?: "horizontal" | "vertical";
28
28
  }) => React.JSX.Element;
29
+ export default AutoScroll;
@@ -17,7 +17,7 @@ import { AnimationMode } from './constants';
17
17
  * @prop {boolean} enabled - Whether the scrolling animation is enabled
18
18
  * @prop {'horizontal'|'vertical'} direction - The direction of the scrolling animation
19
19
  */
20
- export var AutoScroll = function (_a) {
20
+ var AutoScroll = function (_a) {
21
21
  var children = _a.children, _b = _a.mode, mode = _b === void 0 ? 'loop' : _b, _c = _a.speed, speed = _c === void 0 ? 30 : _c, _d = _a.delay, delay = _d === void 0 ? 1500 : _d, _e = _a.endPauseDuration, endPauseDuration = _e === void 0 ? 1000 : _e, _f = _a.style, style = _f === void 0 ? {} : _f, _g = _a.textStyle, textStyle = _g === void 0 ? {} : _g, _h = _a.enabled, enabled = _h === void 0 ? true : _h, _j = _a.direction, direction = _j === void 0 ? 'vertical' : _j;
22
22
  // References for measurement
23
23
  var containerRef = useRef(null);
@@ -143,3 +143,4 @@ var styles = StyleSheet.create({
143
143
  flexShrink: 0,
144
144
  },
145
145
  });
146
+ export default AutoScroll;
@@ -1,46 +1,120 @@
1
1
  import * as React from 'react';
2
- import type { ViewStyle } from 'react-native';
3
- import type { SharedValue } from 'react-native-reanimated';
2
+ import { ViewStyle, TextStyle } from 'react-native';
3
+ /**
4
+ * Animation modes for the marquee text
5
+ */
6
+ declare enum AnimationMode {
7
+ LOOP = "loop",
8
+ BOUNCE = "bounce"
9
+ }
10
+ /**
11
+ * Direction of the marquee animation
12
+ */
4
13
  type MarqueeDirection = 'horizontal' | 'vertical';
5
- export type MarqueeProps = React.PropsWithChildren<{
14
+ /**
15
+ * Props for the MarqueeText component
16
+ */
17
+ interface MarqueeTextProps {
18
+ /**
19
+ * Content to be scrolled (string or React nodes)
20
+ */
21
+ children: React.ReactNode;
22
+ /**
23
+ * The animation mode: 'loop' for continuous scrolling or 'bounce' for back-and-forth
24
+ * @default AnimationMode.LOOP
25
+ */
26
+ mode?: AnimationMode;
27
+ /**
28
+ * Speed of the scrolling animation in pixels per second
29
+ * @default 30
30
+ */
6
31
  speed?: number;
7
- spacing?: number;
32
+ /**
33
+ * Delay in milliseconds before starting the animation
34
+ * @default 1500
35
+ */
36
+ delay?: number;
37
+ /**
38
+ * Duration in milliseconds to pause at the end of the content (only for bounce mode)
39
+ * @default 1000
40
+ */
41
+ endPauseDuration?: number;
42
+ /**
43
+ * Container style
44
+ */
8
45
  style?: ViewStyle;
9
- reverse?: boolean;
10
- frameRate?: number;
46
+ /**
47
+ * Text style (when children is a string)
48
+ */
49
+ textStyle?: TextStyle;
50
+ /**
51
+ * Whether to enable the animation
52
+ * @default true
53
+ */
54
+ enabled?: boolean;
55
+ /**
56
+ * Direction of scrolling ('horizontal' or 'vertical')
57
+ * @default 'horizontal'
58
+ */
11
59
  direction?: MarqueeDirection;
12
- position?: SharedValue<number>;
60
+ /**
61
+ * Spacing between cloned elements
62
+ * @default 20
63
+ */
64
+ spacing?: number;
65
+ /**
66
+ * Allow user interaction with gesture handling
67
+ * @default true
68
+ */
13
69
  withGesture?: boolean;
14
- }>;
15
- export type MarqueeRef = {
70
+ /**
71
+ * Custom frame rate for animation
72
+ */
73
+ frameRate?: number;
74
+ /**
75
+ * Reverse the animation direction
76
+ * @default false
77
+ */
78
+ reverse?: boolean;
79
+ /**
80
+ * Background color for the container
81
+ * @default 'transparent'
82
+ */
83
+ backgroundColor?: string;
84
+ /**
85
+ * Callback when animation starts
86
+ */
87
+ onAnimationStart?: () => void;
88
+ /**
89
+ * Callback when animation stops
90
+ */
91
+ onAnimationStop?: () => void;
92
+ }
93
+ /**
94
+ * Reference handle for the MarqueeText component
95
+ */
96
+ export interface MarqueeTextRef {
97
+ /**
98
+ * Start the marquee animation
99
+ */
16
100
  start: () => void;
101
+ /**
102
+ * Stop the marquee animation
103
+ */
17
104
  stop: () => void;
105
+ /**
106
+ * Whether the animation is currently active
107
+ */
18
108
  isActive: boolean;
19
- };
109
+ }
20
110
  /**
21
- * Marquee component that animates its children in a continuous loop.
111
+ * MarqueeText component for scrolling text or content
22
112
  *
23
- * @param speed - Animation speed (pixels per frame)
24
- * @param spacing - Spacing between cloned elements
25
- * @param style - Container style
26
- * @param reverse - Reverse animation direction
27
- * @param frameRate - Custom frame rate (optional)
28
- * @param direction - Animation direction ('horizontal' or 'vertical')
29
- * @param position - Optional shared value to track position
30
- * @param withGesture - Enable/disable gesture interactions
31
- * @param ref - Ref to control animation
32
- * @param children - Child elements to animate
113
+ * This component creates a scrolling effect for text or other content.
114
+ * It supports both horizontal and vertical scrolling, different animation
115
+ * modes, and user interaction via gestures.
33
116
  */
34
- export declare const Marquee: React.NamedExoticComponent<{
35
- speed?: number;
36
- spacing?: number;
37
- style?: ViewStyle;
38
- reverse?: boolean;
39
- frameRate?: number;
40
- direction?: MarqueeDirection;
41
- position?: SharedValue<number>;
42
- withGesture?: boolean;
43
- } & {
44
- children?: React.ReactNode | undefined;
45
- } & React.RefAttributes<MarqueeRef>>;
46
- export {};
117
+ declare const MarqueeText: React.ForwardRefExoticComponent<MarqueeTextProps & React.RefAttributes<MarqueeTextRef>>;
118
+ export default MarqueeText;
119
+ export { AnimationMode };
120
+ export type { MarqueeDirection, MarqueeTextProps };
@@ -1,131 +1,25 @@
1
- // // MarqueeText.js - Complete rewrite with guaranteed scrolling
2
- // import React, { useEffect } from 'react';
3
- // import { Text, View } from 'react-native';
4
- // import Animated, {
5
- // useSharedValue,
6
- // useAnimatedStyle,
7
- // withRepeat,
8
- // withTiming,
9
- // withDelay,
10
- // Easing,
11
- // withSequence,
12
- // } from 'react-native-reanimated';
13
- // const MarqueeText = ({
14
- // text,
15
- // speed = 60,
16
- // backgroundColor = '#000',
17
- // textColor = '#fff',
18
- // fontSize = 16,
19
- // paddingVertical = 8,
20
- // paddingHorizontal = 12,
21
- // delay = 1000,
22
- // bounceMode = false,
23
- // endPauseDuration = 2000,
24
- // }:{
25
- // text: string;
26
- // speed?: number;
27
- // backgroundColor?: string;
28
- // textColor?: string;
29
- // fontSize?: number;
30
- // paddingVertical?: number;
31
- // paddingHorizontal?: number;
32
- // delay?: number;
33
- // bounceMode?: boolean;
34
- // endPauseDuration?: number;
35
- // }) => {
36
- // // Force a larger content width to ensure scrolling always happens
37
- // // In real usage, we would measure dynamically, but this ensures it works
38
- // const contentWidth = text.length * (fontSize * 0.6);
39
- // const scrollX = useSharedValue(0);
40
- // useEffect(() => {
41
- // // Give layout time to complete
42
- // const timeoutId = setTimeout(() => {
43
- // if (bounceMode) {
44
- // // Bounce animation (back and forth with pauses)
45
- // scrollX.value = withDelay(
46
- // delay,
47
- // withRepeat(
48
- // withSequence(
49
- // withTiming(-contentWidth, {
50
- // duration: contentWidth * (1000 / speed),
51
- // easing: Easing.linear
52
- // }),
53
- // withTiming(-contentWidth, {
54
- // duration: endPauseDuration,
55
- // easing: Easing.linear
56
- // }),
57
- // withTiming(0, {
58
- // duration: contentWidth * (1000 / speed),
59
- // easing: Easing.linear
60
- // }),
61
- // withTiming(0, {
62
- // duration: endPauseDuration,
63
- // easing: Easing.linear
64
- // })
65
- // ),
66
- // -1, // Infinite repeats
67
- // false
68
- // )
69
- // );
70
- // } else {
71
- // // Standard loop animation
72
- // scrollX.value = withDelay(
73
- // delay,
74
- // withRepeat(
75
- // withTiming(-contentWidth, {
76
- // duration: contentWidth * (1000 / speed),
77
- // easing: Easing.linear
78
- // }),
79
- // -1, // Infinite repeats
80
- // false
81
- // )
82
- // );
83
- // }
84
- // }, 500);
85
- // return () => clearTimeout(timeoutId);
86
- // }, [speed, delay, contentWidth, bounceMode, endPauseDuration]);
87
- // const animatedStyle = useAnimatedStyle(() => {
88
- // return {
89
- // transform: [{ translateX: scrollX.value }],
90
- // };
91
- // });
92
- // return (
93
- // <View
94
- // style={{
95
- // backgroundColor,
96
- // paddingVertical,
97
- // paddingHorizontal,
98
- // overflow: 'hidden',
99
- // }}
100
- // >
101
- // <Animated.Text
102
- // style={[
103
- // {
104
- // color: textColor,
105
- // fontSize,
106
- // },
107
- // animatedStyle,
108
- // ]}
109
- // numberOfLines={1}
110
- // // ellipsizeMode="tail"
111
- // >
112
- // {text}
113
- // </Animated.Text>
114
- // </View>
115
- // );
116
- // };
117
- // export default MarqueeText;
118
1
  import * as React from 'react';
119
- import { StyleSheet, View } from 'react-native';
2
+ import { StyleSheet, View, Text } from 'react-native';
120
3
  import { Gesture, GestureDetector } from 'react-native-gesture-handler';
121
- import Animated, { runOnJS, useAnimatedReaction, useAnimatedStyle, useDerivedValue, useFrameCallback, useSharedValue, withDecay, } from 'react-native-reanimated';
4
+ import Animated, { runOnJS, useAnimatedReaction, useAnimatedStyle, useSharedValue, useFrameCallback, withDecay, } from 'react-native-reanimated';
122
5
  import { useFocusEffect } from '@react-navigation/native';
6
+ /**
7
+ * Animation modes for the marquee text
8
+ */
9
+ var AnimationMode;
10
+ (function (AnimationMode) {
11
+ AnimationMode["LOOP"] = "loop";
12
+ AnimationMode["BOUNCE"] = "bounce";
13
+ })(AnimationMode || (AnimationMode = {}));
14
+ /**
15
+ * Component to render a child item in the marquee
16
+ */
123
17
  var AnimatedChild = React.memo(function (_a) {
124
- var index = _a.index, children = _a.children, anim = _a.anim, textMeasurement = _a.textMeasurement, spacing = _a.spacing, direction = _a.direction;
125
- var stylez = useAnimatedStyle(function () {
18
+ var index = _a.index, children = _a.children, anim = _a.anim, contentMeasurement = _a.contentMeasurement, spacing = _a.spacing, direction = _a.direction;
19
+ var style = useAnimatedStyle(function () {
126
20
  var _a;
127
21
  var isVertical = direction === 'vertical';
128
- var dimension = isVertical ? textMeasurement.value.height : textMeasurement.value.width;
22
+ var dimension = isVertical ? contentMeasurement.value.height : contentMeasurement.value.width;
129
23
  if (dimension <= 0)
130
24
  return {};
131
25
  var position = (index - 1) * (dimension + spacing);
@@ -134,67 +28,59 @@ var AnimatedChild = React.memo(function (_a) {
134
28
  position: 'absolute'
135
29
  },
136
30
  _a[isVertical ? 'top' : 'left'] = position,
137
- _a.transform = [
138
- {
139
- translateX: translation,
140
- },
141
- ],
31
+ _a.transform = isVertical
32
+ ? [{ translateY: translation }]
33
+ : [{ translateX: translation }],
142
34
  _a;
143
- }, [index, spacing, textMeasurement, direction]);
144
- return <Animated.View style={stylez}>{children}</Animated.View>;
35
+ }, [index, spacing, contentMeasurement, direction]);
36
+ return <Animated.View style={style}>{children}</Animated.View>;
145
37
  });
146
38
  /**
147
- * Marquee component that animates its children in a continuous loop.
39
+ * MarqueeText component for scrolling text or content
148
40
  *
149
- * @param speed - Animation speed (pixels per frame)
150
- * @param spacing - Spacing between cloned elements
151
- * @param style - Container style
152
- * @param reverse - Reverse animation direction
153
- * @param frameRate - Custom frame rate (optional)
154
- * @param direction - Animation direction ('horizontal' or 'vertical')
155
- * @param position - Optional shared value to track position
156
- * @param withGesture - Enable/disable gesture interactions
157
- * @param ref - Ref to control animation
158
- * @param children - Child elements to animate
41
+ * This component creates a scrolling effect for text or other content.
42
+ * It supports both horizontal and vertical scrolling, different animation
43
+ * modes, and user interaction via gestures.
159
44
  */
160
- export var Marquee = React.memo(React.forwardRef(function (_a, ref) {
161
- var _b = _a.speed, speed = _b === void 0 ? 1 : _b, children = _a.children, _c = _a.spacing, spacing = _c === void 0 ? 0 : _c, style = _a.style, _d = _a.reverse, reverse = _d === void 0 ? false : _d, frameRate = _a.frameRate, _e = _a.direction, direction = _e === void 0 ? 'horizontal' : _e, position = _a.position, _f = _a.withGesture, withGesture = _f === void 0 ? true : _f;
45
+ var MarqueeText = React.forwardRef(function (_a, ref) {
46
+ var children = _a.children, _b = _a.mode, mode = _b === void 0 ? AnimationMode.LOOP : _b, _c = _a.speed, speed = _c === void 0 ? 30 : _c, _d = _a.delay, delay = _d === void 0 ? 1500 : _d, _e = _a.endPauseDuration, endPauseDuration = _e === void 0 ? 1000 : _e, style = _a.style, textStyle = _a.textStyle, _f = _a.enabled, enabled = _f === void 0 ? true : _f, _g = _a.direction, direction = _g === void 0 ? 'horizontal' : _g, _h = _a.spacing, spacing = _h === void 0 ? 20 : _h, _j = _a.withGesture, withGesture = _j === void 0 ? true : _j, frameRate = _a.frameRate, _k = _a.reverse, reverse = _k === void 0 ? false : _k, _l = _a.backgroundColor, backgroundColor = _l === void 0 ? 'transparent' : _l, onAnimationStart = _a.onAnimationStart, onAnimationStop = _a.onAnimationStop;
162
47
  var isVertical = direction === 'vertical';
163
- var parentMeasurement = useSharedValue({
48
+ var isBounceMode = mode === AnimationMode.BOUNCE;
49
+ var containerMeasurement = useSharedValue({
164
50
  width: 0,
165
51
  height: 0,
166
52
  x: 0,
167
53
  y: 0,
168
54
  });
169
- var textMeasurement = useSharedValue({
55
+ var contentMeasurement = useSharedValue({
170
56
  width: 0,
171
57
  height: 0,
172
58
  x: 0,
173
59
  y: 0,
174
60
  });
175
- var _g = React.useState(0), cloneTimes = _g[0], setCloneTimes = _g[1];
61
+ var _m = React.useState(0), cloneTimes = _m[0], setCloneTimes = _m[1];
176
62
  var anim = useSharedValue(0);
177
63
  var isMounted = React.useRef(true);
178
- var frameRateMs = frameRate ? 1000 / frameRate : null;
64
+ var isActive = useSharedValue(false);
65
+ var frameRateMs = frameRate ? 1000 / frameRate : 1000 / 60; // Default to 60fps
66
+ var pixelsPerMs = speed / 1000;
67
+ // Animation frame callback
179
68
  var frameCallback = useFrameCallback(function (frameInfo) {
180
- if (frameInfo.timeSincePreviousFrame === null)
69
+ if (!enabled || frameInfo.timeSincePreviousFrame === null)
181
70
  return;
182
- var frameDelta = frameRateMs
71
+ var deltaTime = frameRateMs
183
72
  ? frameInfo.timeSincePreviousFrame / frameRateMs
184
- : 1;
185
- anim.value += (reverse ? -1 : 1) * speed * frameDelta;
73
+ : frameInfo.timeSincePreviousFrame;
74
+ anim.value += (reverse ? -1 : 1) * pixelsPerMs * deltaTime;
186
75
  }, false);
187
- useDerivedValue(function () {
188
- if (position) {
189
- position.value = anim.value;
190
- }
191
- });
76
+ // Calculate how many clones we need to fill the screen
192
77
  useAnimatedReaction(function () {
193
- var textDim = isVertical ? textMeasurement.value.height : textMeasurement.value.width;
194
- var parentDim = isVertical ? parentMeasurement.value.height : parentMeasurement.value.width;
195
- if (textDim <= 0 || parentDim <= 0)
78
+ var contentDim = isVertical ? contentMeasurement.value.height : contentMeasurement.value.width;
79
+ var containerDim = isVertical ? containerMeasurement.value.height : containerMeasurement.value.width;
80
+ if (contentDim <= 0 || containerDim <= 0)
196
81
  return 0;
197
- return Math.ceil(parentDim / textDim) + 2;
82
+ // Need enough clones to fill the container plus buffer for continuous scrolling
83
+ return Math.ceil(containerDim / contentDim) + 2;
198
84
  }, function (times) {
199
85
  if (times > 0 && isMounted.current) {
200
86
  runOnJS(setCloneTimes)(times);
@@ -202,75 +88,122 @@ export var Marquee = React.memo(React.forwardRef(function (_a, ref) {
202
88
  }, [direction]);
203
89
  // Control functions
204
90
  var start = React.useCallback(function () {
91
+ if (!enabled)
92
+ return;
205
93
  frameCallback.setActive(true);
206
- }, [frameCallback]);
94
+ isActive.value = true;
95
+ onAnimationStart === null || onAnimationStart === void 0 ? void 0 : onAnimationStart();
96
+ }, [frameCallback, enabled, onAnimationStart]);
207
97
  var stop = React.useCallback(function () {
208
98
  frameCallback.setActive(false);
209
- }, [frameCallback]);
99
+ isActive.value = false;
100
+ onAnimationStop === null || onAnimationStop === void 0 ? void 0 : onAnimationStop();
101
+ }, [frameCallback, onAnimationStop]);
102
+ // Expose controls via ref
210
103
  React.useImperativeHandle(ref, function () { return ({
211
104
  start: start,
212
105
  stop: stop,
213
- isActive: frameCallback.isActive,
106
+ get isActive() { return isActive.value; }
214
107
  }); });
108
+ // Setup gesture handling
215
109
  var pan = React.useMemo(function () {
216
110
  return Gesture.Pan()
217
- .enabled(withGesture)
218
- .onBegin(function () { return runOnJS(stop)(); })
111
+ .enabled(withGesture && enabled)
112
+ .onBegin(function () {
113
+ // Stop auto-animation when user interacts
114
+ runOnJS(stop)();
115
+ })
219
116
  .onChange(function (e) {
117
+ // Move according to user's gesture
220
118
  anim.value += -(isVertical ? e.changeY : e.changeX);
221
119
  })
222
120
  .onFinalize(function (e) {
121
+ // Apply momentum scrolling when user releases
223
122
  anim.value = withDecay({
224
123
  velocity: -(isVertical ? e.velocityY : e.velocityX),
225
- }, function (finished) { return finished && runOnJS(start)(); });
124
+ }, function (finished) {
125
+ // Restart auto-animation when decay finishes
126
+ if (finished)
127
+ runOnJS(start)();
128
+ });
226
129
  });
227
- }, [withGesture, isVertical, anim, start, stop]);
130
+ }, [withGesture, isVertical, anim, start, stop, enabled]);
228
131
  // Handle focus/unmount
229
132
  useFocusEffect(React.useCallback(function () {
230
- start();
133
+ // Start animation when screen comes into focus
134
+ if (enabled) {
135
+ start();
136
+ }
231
137
  return function () {
138
+ // Stop animation when screen loses focus
232
139
  stop();
233
140
  anim.value = 0;
234
141
  };
235
- }, [start, stop, anim]));
142
+ }, [start, stop, anim, enabled]));
143
+ // Cleanup on unmount
236
144
  React.useEffect(function () {
237
145
  return function () {
238
146
  isMounted.current = false;
239
147
  };
240
148
  }, []);
241
- return (<Animated.View style={style} onLayout={function (ev) {
242
- parentMeasurement.value = ev.nativeEvent.layout;
149
+ // Start/stop based on enabled prop changes
150
+ React.useEffect(function () {
151
+ if (enabled) {
152
+ start();
153
+ }
154
+ else {
155
+ stop();
156
+ }
157
+ }, [enabled, start, stop]);
158
+ // Render the content
159
+ var renderContent = function () {
160
+ if (typeof children === 'string') {
161
+ return <Text style={textStyle}>{children}</Text>;
162
+ }
163
+ return children;
164
+ };
165
+ return (<Animated.View style={[
166
+ styles.container,
167
+ { backgroundColor: backgroundColor },
168
+ style
169
+ ]} onLayout={function (ev) {
170
+ containerMeasurement.value = ev.nativeEvent.layout;
243
171
  }} pointerEvents="box-none">
244
- <GestureDetector gesture={pan}>
245
- <Animated.View style={isVertical ? styles.column : styles.row} pointerEvents="box-none">
246
- <Animated.ScrollView horizontal={!isVertical} style={styles.hidden} pointerEvents="none">
247
- <View onLayout={function (ev) {
248
- textMeasurement.value = ev.nativeEvent.layout;
172
+ <GestureDetector gesture={pan}>
173
+ <Animated.View style={isVertical ? styles.column : styles.row} pointerEvents="box-none">
174
+ {/* Hidden element for measuring original content size */}
175
+ <View style={styles.hidden} onLayout={function (ev) {
176
+ contentMeasurement.value = ev.nativeEvent.layout;
249
177
  }}>
250
- {children}
251
- </View>
252
- </Animated.ScrollView>
253
-
254
- {cloneTimes > 0 &&
255
- Array.from({ length: cloneTimes }).map(function (_, index) { return (<AnimatedChild key={"clone-".concat(index)} index={index} anim={anim} textMeasurement={textMeasurement} spacing={spacing} direction={direction}>
256
- {children}
257
- </AnimatedChild>); })}
258
- </Animated.View>
259
- </GestureDetector>
260
- </Animated.View>);
261
- }));
178
+ {renderContent()}
179
+ </View>
180
+
181
+ {/* Visible cloned elements */}
182
+ {cloneTimes > 0 &&
183
+ Array.from({ length: cloneTimes }).map(function (_, index) { return (<AnimatedChild key={"clone-".concat(index)} index={index} anim={anim} contentMeasurement={contentMeasurement} spacing={spacing} direction={direction}>
184
+ {renderContent()}
185
+ </AnimatedChild>); })}
186
+ </Animated.View>
187
+ </GestureDetector>
188
+ </Animated.View>);
189
+ });
262
190
  var styles = StyleSheet.create({
191
+ container: {
192
+ overflow: 'hidden',
193
+ },
263
194
  hidden: {
264
195
  opacity: 0,
265
- zIndex: -9999,
266
- flex: 0, // Prevent ScrollView from expanding
196
+ position: 'absolute',
197
+ zIndex: -1,
267
198
  },
268
199
  row: {
269
200
  flexDirection: 'row',
270
- flex: 1,
201
+ position: 'relative',
271
202
  },
272
203
  column: {
273
204
  flexDirection: 'column',
274
- flex: 1,
205
+ position: 'relative',
275
206
  },
276
207
  });
208
+ export default MarqueeText;
209
+ export { AnimationMode };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,18 @@
1
- import { AutoScroll } from './AutoScroll';
2
- import { Marquee } from './MarqueeText';
3
- import { AnimationMode } from './constants';
4
- export { AutoScroll, AnimationMode, Marquee };
5
- export default AutoScroll;
1
+ import { AnimationMode } from './MarqueeText';
2
+ export { AnimationMode } from './constants';
3
+ declare const Marquee: {
4
+ AutoScroll: ({ children, mode, speed, delay, endPauseDuration, style, textStyle, enabled, direction, }: {
5
+ children: React.ReactNode;
6
+ mode?: "loop" | "bounce";
7
+ speed?: number;
8
+ delay?: number;
9
+ endPauseDuration?: number;
10
+ style?: import("react-native").ViewStyle;
11
+ textStyle?: import("react-native").TextStyle;
12
+ enabled?: boolean;
13
+ direction?: "horizontal" | "vertical";
14
+ }) => import("react").JSX.Element;
15
+ MarqueeText: import("react").ForwardRefExoticComponent<import("./MarqueeText").MarqueeTextProps & import("react").RefAttributes<import("./MarqueeText").MarqueeTextRef>>;
16
+ AnimationMode: typeof AnimationMode;
17
+ };
18
+ export default Marquee;
package/dist/index.js CHANGED
@@ -1,5 +1,10 @@
1
- import { AutoScroll } from './AutoScroll';
2
- import { Marquee } from './MarqueeText';
3
- import { AnimationMode } from './constants';
4
- export { AutoScroll, AnimationMode, Marquee };
5
- export default AutoScroll;
1
+ import AutoScroll from './AutoScroll';
2
+ import MarqueeText, { AnimationMode } from './MarqueeText';
3
+ export { AnimationMode } from './constants';
4
+ // Optional: Default export (choose either AutoScroll or MarqueeText as default)
5
+ var Marquee = {
6
+ AutoScroll: AutoScroll,
7
+ MarqueeText: MarqueeText,
8
+ AnimationMode: AnimationMode
9
+ };
10
+ export default Marquee;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rn-marquee-text",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "A customizable marquee (scrolling) text component for React Native",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",