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 +132 -219
- package/dist/AutoScroll.d.ts +2 -1
- package/dist/AutoScroll.js +2 -1
- package/dist/MarqueeText.d.ts +108 -34
- package/dist/MarqueeText.js +124 -191
- package/dist/index.d.ts +18 -5
- package/dist/index.js +10 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,233 +1,146 @@
|
|
|
1
|
-
# React Native Marquee Text
|
|
1
|
+
# React Native Marquee Text 🏃♂️
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/rn-marquee-text)
|
|
4
|
+
[](https://github.com/yourusername/rn-marquee-text/blob/main/LICENSE)
|
|
5
|
+
[](https://www.npmjs.com/package/rn-marquee-text)
|
|
6
|
+
[](https://github.com/yourusername/rn-marquee-text/actions)
|
|
3
7
|
|
|
4
|
-
A
|
|
8
|
+
A high-performance, customizable marquee component for React Native with smooth animations and gesture support.
|
|
5
9
|
|
|
6
|
-

|
|
8
|
-

|
|
10
|
+

|
|
9
11
|
|
|
10
|
-
##
|
|
12
|
+
## Features ✨
|
|
11
13
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
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
|
-
##
|
|
22
|
+
## Installation 💻
|
|
22
23
|
|
|
23
24
|
```bash
|
|
24
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
33
|
+
Configure Gesture Handler
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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={
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
fontSize={14}
|
|
95
|
-
direction="horizontal"
|
|
42
|
+
speed={50}
|
|
43
|
+
style={{ height: 40 }}
|
|
44
|
+
textStyle={{ fontSize: 16 }}
|
|
96
45
|
>
|
|
97
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
package/dist/AutoScroll.d.ts
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/dist/AutoScroll.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/dist/MarqueeText.d.ts
CHANGED
|
@@ -1,46 +1,120 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
111
|
+
* MarqueeText component for scrolling text or content
|
|
22
112
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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 };
|
package/dist/MarqueeText.js
CHANGED
|
@@ -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,
|
|
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,
|
|
125
|
-
var
|
|
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 ?
|
|
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
|
-
|
|
140
|
-
},
|
|
141
|
-
],
|
|
31
|
+
_a.transform = isVertical
|
|
32
|
+
? [{ translateY: translation }]
|
|
33
|
+
: [{ translateX: translation }],
|
|
142
34
|
_a;
|
|
143
|
-
}, [index, spacing,
|
|
144
|
-
return <Animated.View style={
|
|
35
|
+
}, [index, spacing, contentMeasurement, direction]);
|
|
36
|
+
return <Animated.View style={style}>{children}</Animated.View>;
|
|
145
37
|
});
|
|
146
38
|
/**
|
|
147
|
-
*
|
|
39
|
+
* MarqueeText component for scrolling text or content
|
|
148
40
|
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
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
|
-
|
|
161
|
-
var _b = _a.
|
|
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
|
|
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
|
|
55
|
+
var contentMeasurement = useSharedValue({
|
|
170
56
|
width: 0,
|
|
171
57
|
height: 0,
|
|
172
58
|
x: 0,
|
|
173
59
|
y: 0,
|
|
174
60
|
});
|
|
175
|
-
var
|
|
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
|
|
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
|
|
71
|
+
var deltaTime = frameRateMs
|
|
183
72
|
? frameInfo.timeSincePreviousFrame / frameRateMs
|
|
184
|
-
:
|
|
185
|
-
anim.value += (reverse ? -1 : 1) *
|
|
73
|
+
: frameInfo.timeSincePreviousFrame;
|
|
74
|
+
anim.value += (reverse ? -1 : 1) * pixelsPerMs * deltaTime;
|
|
186
75
|
}, false);
|
|
187
|
-
|
|
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
|
|
194
|
-
var
|
|
195
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 () {
|
|
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) {
|
|
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
|
-
|
|
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
|
-
|
|
242
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
Array.from({ length: cloneTimes }).map(function (_, index) { return (<AnimatedChild key={"clone-".concat(index)} index={index} anim={anim}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
266
|
-
|
|
196
|
+
position: 'absolute',
|
|
197
|
+
zIndex: -1,
|
|
267
198
|
},
|
|
268
199
|
row: {
|
|
269
200
|
flexDirection: 'row',
|
|
270
|
-
|
|
201
|
+
position: 'relative',
|
|
271
202
|
},
|
|
272
203
|
column: {
|
|
273
204
|
flexDirection: 'column',
|
|
274
|
-
|
|
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 {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
|
|
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;
|