rn-marquee-text 2.0.3 → 2.0.4

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,175 @@
1
- # React Native Marquee Text
1
+ Here's a more professional and well-structured version of your README file with improved formatting, organization, and a "Copy All" button:
2
2
 
3
+ ```markdown
4
+ # React Native Marquee Text 🚀
3
5
 
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.
6
+ [![npm version](https://img.shields.io/npm/v/rn-marquee-text.svg?style=flat-square)](https://www.npmjs.com/package/rn-marquee-text)
7
+ [![license](https://img.shields.io/npm/l/rn-marquee-text.svg?style=flat-square)](https://github.com/yourusername/rn-marquee-text/blob/main/LICENSE)
8
+ [![downloads](https://img.shields.io/npm/dm/rn-marquee-text.svg?style=flat-square)](https://www.npmjs.com/package/rn-marquee-text)
9
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
5
10
 
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)
11
+ A high-performance, feature-rich marquee component for React Native with smooth animations and flexible configuration.
9
12
 
10
- ## 🚀 Features
13
+ ![Horizontal and Vertical Marquee Demo](demo-combined.gif)
11
14
 
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
15
+ ## Table of Contents
16
+ - [Features](#features-)
17
+ - [Installation](#installation-)
18
+ - [Usage](#usage-)
19
+ - [API Reference](#api-reference-)
20
+ - [Examples](#examples-)
21
+ - [Troubleshooting](#troubleshooting-)
22
+ - [Contributing](#contributing-)
23
+ - [License](#license-)
20
24
 
21
- ## 📦 Installation
25
+ ## Features
22
26
 
23
- ```bash
24
- # React Native CLI
25
- npm install rn-marquee-text react-native-reanimated
26
-
27
- # OR using yarn
28
- yarn add rn-marquee-text react-native-reanimated
29
- ```
27
+ - **Multi-directional scrolling**: Horizontal and vertical marquee effects
28
+ - **Animation modes**: Loop and bounce animations
29
+ - **Performance optimized**: Built with React Native Reanimated 2
30
+ - **Customizable**: Control speed, delay, spacing, and more
31
+ - **Interactive**: Gesture support for pausing/resuming
32
+ - **Flexible content**: Supports both text and custom components
33
+ - **TypeScript ready**: Complete type definitions included
30
34
 
31
- ### Expo Users
35
+ ## Installation 📦
32
36
 
33
37
  ```bash
34
- expo install react-native-reanimated
35
- ```
36
-
37
- Then update your `babel.config.js`:
38
+ # Using npm
39
+ npm install rn-marquee-text react-native-reanimated react-native-gesture-handler
38
40
 
39
- ```js
40
- module.exports = {
41
- presets: ['babel-preset-expo'],
42
- plugins: ['react-native-reanimated/plugin'],
43
- };
41
+ # Using yarn
42
+ yarn add rn-marquee-text react-native-reanimated react-native-gesture-handler
44
43
  ```
45
44
 
46
- ## 🧱 Components
45
+ ### Peer Dependencies
46
+ Ensure you've properly installed and configured:
47
+ - [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/installation)
48
+ - [React Native Gesture Handler](https://docs.swmansion.com/react-native-gesture-handler/docs/)
47
49
 
48
- ### 1️⃣ AutoScroll — Scroll any overflowed content
50
+ ## Usage 🚀
49
51
 
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
52
+ ### Basic Implementation
53
+ ```jsx
54
+ import React from 'react';
55
+ import { StyleSheet, View } from 'react-native';
56
+ import MarqueeText from 'rn-marquee-text';
67
57
 
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' }}>
58
+ const App = () => (
59
+ <View style={styles.container}>
90
60
  <MarqueeText
91
- speed={100}
92
- backgroundColor="#1a365d"
93
- textColor="#f0f4f8"
94
- fontSize={14}
95
- direction="horizontal"
61
+ style={styles.marquee}
62
+ speed={40}
63
+ textStyle={styles.text}
96
64
  >
97
- <Text>
98
- Breaking News: This is a marquee text scrolling horizontally!
99
- </Text>
65
+ Your scrolling text goes here
100
66
  </MarqueeText>
101
67
  </View>
102
68
  );
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
69
 
141
70
  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
- },
71
+ container: { flex: 1, justifyContent: 'center', padding: 20 },
72
+ marquee: { height: 50, backgroundColor: '#f5f5f5', borderRadius: 8 },
73
+ text: { fontSize: 16, color: '#333' }
163
74
  });
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>
179
- </MarqueeText>
180
- </View>
181
- );
182
75
 
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
- });
76
+ export default App;
195
77
  ```
196
78
 
197
- ## 🛠️ Tips for Optimal Performance
79
+ ## API Reference 📚
198
80
 
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
81
+ ### Props
82
+ | Prop | Type | Default | Description |
83
+ |------|------|---------|-------------|
84
+ | `children` | React.ReactNode | Required | Content to scroll (string or components) |
85
+ | `mode` | 'loop' \| 'bounce' | 'loop' | Animation behavior |
86
+ | `speed` | number | 30 | Pixels per second |
87
+ | `direction` | 'horizontal' \| 'vertical' | 'horizontal' | Scroll direction |
88
+ | `enabled` | boolean | true | Animation active state |
89
+ | `delay` | number | 1500 | Initial delay (ms) |
90
+ | `endPauseDuration` | number | 1000 | Pause at end (bounce mode) |
91
+
92
+ ### Methods (via ref)
93
+ ```jsx
94
+ const marqueeRef = useRef();
95
+
96
+ // Start animation
97
+ marqueeRef.current?.start();
98
+
99
+ // Stop animation
100
+ marqueeRef.current?.stop();
101
+
102
+ // Check if active
103
+ const isActive = marqueeRef.current?.isActive;
104
+ ```
204
105
 
205
- ## 🧩 Troubleshooting
106
+ ## Examples 🎨
107
+
108
+ ### Vertical Bounce Marquee
109
+ ```jsx
110
+ <MarqueeText
111
+ direction="vertical"
112
+ mode="bounce"
113
+ speed={20}
114
+ endPauseDuration={2000}
115
+ style={styles.verticalMarquee}
116
+ >
117
+ Line 1{'\n'}Line 2{'\n'}Line 3
118
+ </MarqueeText>
119
+ ```
206
120
 
207
- ### Text Not Scrolling?
121
+ ### Controlled Marquee with Buttons
122
+ ```jsx
123
+ const [paused, setPaused] = useState(false);
208
124
 
209
- - Ensure content overflows the container
210
- - Confirm `enabled` is set to `true`
211
- - Use `overflow: 'hidden'` on container
125
+ <>
126
+ <MarqueeText
127
+ ref={marqueeRef}
128
+ enabled={!paused}
129
+ style={styles.marquee}
130
+ >
131
+ Controllable marquee text
132
+ </MarqueeText>
133
+
134
+ <Button
135
+ title={paused ? "Resume" : "Pause"}
136
+ onPress={() => setPaused(!paused)}
137
+ />
138
+ </>
139
+ ```
212
140
 
213
- ### 🐢 Choppy Animations?
141
+ ## Troubleshooting 🛠️
214
142
 
215
- - Leverage `useNativeDriver` if supported
216
- - Minimize simultaneous animations
217
- - Simplify nested component structure
143
+ **Animation not working?**
144
+ - Verify Reanimated installation
145
+ - Check babel.config.js for Reanimated plugin
218
146
 
219
- ## 📄 License
147
+ **Text not visible?**
148
+ - Ensure container has proper dimensions
149
+ - Verify text color contrasts with background
220
150
 
221
- MIT
151
+ **Performance issues?**
152
+ - Reduce animation speed
153
+ - Simplify marquee content
154
+ - Use `frameRate` prop to limit FPS
222
155
 
223
- ## 🤝 Contributing
156
+ ## Contributing 🤝
157
+ Contributions are welcome! Please:
158
+ 1. Open an issue to discuss changes
159
+ 2. Ensure tests are updated
160
+ 3. Maintain consistent code style
224
161
 
225
- Pull requests welcome!
162
+ ## License 📄
163
+ MIT © [Your Name](https://github.com/yourusername)
226
164
 
227
- Steps:
165
+ <button onclick="copyToClipboard()">Copy All README Content</button>
228
166
 
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
167
+ <script>
168
+ function copyToClipboard() {
169
+ const content = document.querySelector('article').innerText;
170
+ navigator.clipboard.writeText(content)
171
+ .then(() => alert('README copied to clipboard!'))
172
+ .catch(err => console.error('Failed to copy:', err));
173
+ }
174
+ </script>
175
+ ```
@@ -1,46 +1,119 @@
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 { MarqueeText, AnimationMode };
119
+ 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,121 @@ 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 { MarqueeText, AnimationMode };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AutoScroll } from './AutoScroll';
2
- import { Marquee } from './MarqueeText';
2
+ import { MarqueeText } from './MarqueeText';
3
3
  import { AnimationMode } from './constants';
4
- export { AutoScroll, AnimationMode, Marquee };
4
+ export { AutoScroll, AnimationMode, MarqueeText };
5
5
  export default AutoScroll;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AutoScroll } from './AutoScroll';
2
- import { Marquee } from './MarqueeText';
2
+ import { MarqueeText } from './MarqueeText';
3
3
  import { AnimationMode } from './constants';
4
- export { AutoScroll, AnimationMode, Marquee };
4
+ export { AutoScroll, AnimationMode, MarqueeText };
5
5
  export default AutoScroll;
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.4",
4
4
  "description": "A customizable marquee (scrolling) text component for React Native",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",