gsap-react-marquee 0.3.0 → 0.3.2
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 +177 -95
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.esm.js +1 -1
- package/package.json +11 -11
- package/dist/gsap-react-marquee.css +0 -1
- package/dist/index.cjs.js +0 -1
- package/dist/types/components/gsap-react-marquee.d.ts +0 -4
- package/dist/types/components/gsap-react-marquee.styles.d.ts +0 -2
- package/dist/types/components/gsap-react-marquee.type.d.ts +0 -18
- package/dist/types/components/gsap-reactmarquee.utils.d.ts +0 -10
- package/dist/types/index.d.ts +0 -3
package/README.md
CHANGED
|
@@ -1,150 +1,232 @@
|
|
|
1
1
|
# GSAP React Marquee
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`gsap-react-marquee` is a React marquee component powered by GSAP. It supports horizontal and vertical scrolling, seamless looping, optional fill mode, pause-on-hover, scroll-follow speed changes, draggable interaction, gradient overlays, and TypeScript types.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install gsap-react-marquee
|
|
9
|
-
# or
|
|
10
|
-
yarn add gsap-react-marquee
|
|
11
|
-
# or
|
|
12
|
-
pnpm add gsap-react-marquee
|
|
8
|
+
npm install gsap-react-marquee gsap @gsap/react
|
|
13
9
|
```
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
```bash
|
|
12
|
+
yarn add gsap-react-marquee gsap @gsap/react
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add gsap-react-marquee gsap @gsap/react
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`react`, `react-dom`, `gsap`, and `@gsap/react` are peer dependencies and must be installed by the consuming app.
|
|
20
|
+
|
|
21
|
+
## Basic Usage
|
|
16
22
|
|
|
17
23
|
```tsx
|
|
18
24
|
import Marquee from "gsap-react-marquee";
|
|
19
25
|
|
|
20
|
-
function App() {
|
|
26
|
+
export function App() {
|
|
21
27
|
return (
|
|
22
|
-
<Marquee dir="left" speed={100}
|
|
23
|
-
<
|
|
28
|
+
<Marquee dir="left" speed={100} spacing={16}>
|
|
29
|
+
<span>Scrolling content</span>
|
|
24
30
|
</Marquee>
|
|
25
31
|
);
|
|
26
32
|
}
|
|
27
33
|
```
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- **Intelligent Container Detection**: Automatically detects whether containers have fixed dimensions or adapt to content, preventing recursive expansion loops
|
|
32
|
-
- **Seamless Looping**: Advanced duplicate calculation ensures smooth infinite scrolling without gaps
|
|
33
|
-
- **High Performance**: Built with GSAP for optimal animation performance
|
|
34
|
-
- **Responsive Design**: Adapts to different screen sizes and container dimensions
|
|
35
|
-
- **Multiple Directions**: Support for horizontal (left/right) and vertical (up/down) scrolling with proper axis handling
|
|
36
|
-
- **Smart Gradient Overlays**: Automatic gradient positioning based on marquee orientation
|
|
37
|
-
- **Interactive Controls**: Optional draggable interface and scroll synchronization
|
|
38
|
-
- **TypeScript Support**: Full type safety and IntelliSense support
|
|
35
|
+
The package injects its base CSS through the bundled entrypoint, so no separate stylesheet import is required.
|
|
39
36
|
|
|
40
|
-
##
|
|
37
|
+
## Examples
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
| --------------- | ------------------------------------- | -------- | ------------------------------------------------------------- |
|
|
44
|
-
| `children` | `ReactNode` | – | Content to render inside the marquee |
|
|
45
|
-
| `className` | `string` | – | Additional CSS classes for styling |
|
|
46
|
-
| `dir` | `"right" \| "left" \| "up" \| "down"` | `"left"` | Direction of the marquee movement |
|
|
47
|
-
| `loop` | `number` | `-1` | Number of loops (`-1` = infinite) |
|
|
48
|
-
| `paused` | `boolean` | `false` | Whether the marquee animation should be paused |
|
|
49
|
-
| `delay` | `number` | `0` | Delay before the animation starts (in seconds) |
|
|
50
|
-
| `speed` | `number` | `100` | Speed of the marquee animation in px/s |
|
|
51
|
-
| `fill` | `boolean` | `false` | Whether the marquee should continuously fill the space |
|
|
52
|
-
| `pauseOnHover` | `boolean` | `false` | Pause the marquee when hovering |
|
|
53
|
-
| `gradient` | `boolean` | `false` | Enable gradient overlay (auto-adapts to orientation) |
|
|
54
|
-
| `gradientColor` | `string` | – | Color of the gradient if enabled |
|
|
55
|
-
| `spacing` | `number` | `16` | Spacing between repeated elements in px |
|
|
56
|
-
| `draggable` | `boolean` | `false` | Enable dragging to scroll manually |
|
|
57
|
-
| `scrollFollow` | `boolean` | `false` | Sync marquee with page scroll direction |
|
|
58
|
-
| `scrollSpeed` | `number` | `2.5` | Speed factor when syncing with page scroll (max: 4, min: 1.1) |
|
|
39
|
+
### Continuous Fill
|
|
59
40
|
|
|
60
|
-
|
|
41
|
+
Use `fill` when a short piece of content should repeat enough times to cover the visible marquee area.
|
|
61
42
|
|
|
62
|
-
|
|
43
|
+
```tsx
|
|
44
|
+
<Marquee fill spacing={24} speed={80}>
|
|
45
|
+
<span>React</span>
|
|
46
|
+
<span>GSAP</span>
|
|
47
|
+
<span>Animation</span>
|
|
48
|
+
</Marquee>
|
|
49
|
+
```
|
|
63
50
|
|
|
64
|
-
|
|
51
|
+
### Vertical Marquee
|
|
65
52
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
53
|
+
```tsx
|
|
54
|
+
<Marquee dir="up" speed={80} spacing={12}>
|
|
55
|
+
<div>Item 1</div>
|
|
56
|
+
<div>Item 2</div>
|
|
57
|
+
<div>Item 3</div>
|
|
58
|
+
</Marquee>
|
|
59
|
+
```
|
|
70
60
|
|
|
71
|
-
###
|
|
61
|
+
### Gradient Overlay
|
|
72
62
|
|
|
73
|
-
|
|
63
|
+
When `gradient` is enabled, the component detects the nearest non-transparent background color and uses it for the edge fade. You can override the color with `gradientColor`.
|
|
74
64
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
```tsx
|
|
66
|
+
<Marquee gradient gradientColor="#ffffff">
|
|
67
|
+
<span>Faded edges</span>
|
|
68
|
+
</Marquee>
|
|
69
|
+
```
|
|
78
70
|
|
|
79
|
-
###
|
|
71
|
+
### Pause On Hover
|
|
80
72
|
|
|
81
|
-
|
|
73
|
+
```tsx
|
|
74
|
+
<Marquee pauseOnHover>
|
|
75
|
+
<span>Hover to pause</span>
|
|
76
|
+
</Marquee>
|
|
77
|
+
```
|
|
82
78
|
|
|
83
|
-
|
|
84
|
-
- **Vertical (`up`/`down`)**: Uses `yPercent` for Y-axis animations with `flexDirection: column`
|
|
85
|
-
- **Smart Gradients**: Gradient overlays automatically position themselves based on scroll direction
|
|
86
|
-
- Horizontal: Side gradients (left and right edges)
|
|
87
|
-
- Vertical: Top and bottom gradients
|
|
79
|
+
### Scroll-Follow
|
|
88
80
|
|
|
89
|
-
|
|
81
|
+
`scrollFollow` changes the marquee timeline speed and direction based on vertical wheel movement.
|
|
90
82
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
83
|
+
```tsx
|
|
84
|
+
<Marquee scrollFollow scrollSpeed={3}>
|
|
85
|
+
<span>Scroll the page or wheel over the document</span>
|
|
86
|
+
</Marquee>
|
|
87
|
+
```
|
|
95
88
|
|
|
96
|
-
|
|
89
|
+
### Draggable
|
|
97
90
|
|
|
98
|
-
|
|
91
|
+
`draggable` lets users drag the marquee track manually. Momentum throwing uses GSAP's `InertiaPlugin`. The package imports the plugin from `gsap/all.js`; if your GSAP setup does not include access to InertiaPlugin, dragging still initializes but momentum behavior may be limited by GSAP availability.
|
|
99
92
|
|
|
100
93
|
```tsx
|
|
101
|
-
<Marquee
|
|
102
|
-
<
|
|
94
|
+
<Marquee draggable pauseOnHover>
|
|
95
|
+
<img src="/image-1.jpg" alt="Gallery image 1" />
|
|
96
|
+
<img src="/image-2.jpg" alt="Gallery image 2" />
|
|
97
|
+
<img src="/image-3.jpg" alt="Gallery image 3" />
|
|
103
98
|
</Marquee>
|
|
104
99
|
```
|
|
105
100
|
|
|
106
|
-
###
|
|
101
|
+
### Forwarded Ref
|
|
102
|
+
|
|
103
|
+
The component forwards a ref to the root marquee container. Both object refs and callback refs are supported.
|
|
107
104
|
|
|
108
105
|
```tsx
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
import { useRef } from "react";
|
|
107
|
+
import Marquee from "gsap-react-marquee";
|
|
108
|
+
|
|
109
|
+
export function Example() {
|
|
110
|
+
const ref = useRef<HTMLDivElement | null>(null);
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<Marquee ref={ref} fill>
|
|
114
|
+
<span>Measured container</span>
|
|
115
|
+
</Marquee>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
114
118
|
```
|
|
115
119
|
|
|
116
|
-
|
|
120
|
+
## Props
|
|
117
121
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
| Prop | Type | Default | Description |
|
|
123
|
+
| --- | --- | --- | --- |
|
|
124
|
+
| `children` | `ReactNode` | Required | Content rendered inside each marquee item. |
|
|
125
|
+
| `className` | `string` | `undefined` | Class applied to each `.gsap-react-marquee-content` element. |
|
|
126
|
+
| `dir` | `"left" \| "right" \| "up" \| "down"` | `"left"` | Direction of movement. |
|
|
127
|
+
| `loop` | `number` | `-1` | Number of timeline repeats. `-1` means infinite. |
|
|
128
|
+
| `paused` | `boolean` | `false` | Starts the timeline paused. |
|
|
129
|
+
| `delay` | `number` | `0` | Delay in seconds before the timeline starts. |
|
|
130
|
+
| `speed` | `number` | `100` | Animation speed in pixels per second. |
|
|
131
|
+
| `fill` | `boolean` | `false` | Repeats content enough times to cover the measured marquee area. |
|
|
132
|
+
| `pauseOnHover` | `boolean` | `false` | Pauses on pointer hover and resumes on leave. |
|
|
133
|
+
| `gradient` | `boolean` | `false` | Enables edge gradient overlays. |
|
|
134
|
+
| `gradientColor` | `string` | `undefined` | Explicit gradient color. Overrides automatic background detection. |
|
|
135
|
+
| `spacing` | `number` | `16` | Gap between marquee items, in pixels. |
|
|
136
|
+
| `draggable` | `boolean` | `false` | Enables manual drag control. |
|
|
137
|
+
| `scrollFollow` | `boolean` | `false` | Adjusts timeline speed from wheel/scroll direction. |
|
|
138
|
+
| `scrollSpeed` | `number` | `2.5` | Scroll-follow multiplier. Clamped between `1.1` and `4`. |
|
|
139
|
+
|
|
140
|
+
## How Sizing Works
|
|
141
|
+
|
|
142
|
+
The component measures the root container and first content item after mount. It then creates enough cloned marquee items for the selected mode and starts a GSAP timeline.
|
|
143
|
+
|
|
144
|
+
In normal mode (`fill={false}`), the component renders one original item plus one clone. This is suitable when your content is already large enough to create a continuous loop.
|
|
145
|
+
|
|
146
|
+
In fill mode (`fill={true}`), the component calculates how many clones are required to cover the measured target size. The duplicate count is capped to prevent excessive DOM growth, and the component re-measures when the container, the first content item, child content, or unloaded images change size.
|
|
147
|
+
|
|
148
|
+
If the container has no reliable defined size, the component falls back to the viewport width or height as its measurement target. This avoids recursive expansion when the marquee is placed inside content-sized layouts.
|
|
149
|
+
|
|
150
|
+
## Styling
|
|
151
|
+
|
|
152
|
+
The root element receives these classes:
|
|
153
|
+
|
|
154
|
+
```html
|
|
155
|
+
<div class="gsap-react-marquee-container">
|
|
156
|
+
<div class="gsap-react-marquee">
|
|
157
|
+
<div class="gsap-react-marquee-content">...</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
124
160
|
```
|
|
125
161
|
|
|
126
|
-
|
|
162
|
+
Vertical marquees also receive:
|
|
163
|
+
|
|
164
|
+
```html
|
|
165
|
+
<div class="gsap-react-marquee-container gsap-react-marquee-vertical">
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Use `className` to style the repeated content wrapper:
|
|
127
169
|
|
|
128
170
|
```tsx
|
|
129
|
-
<Marquee
|
|
130
|
-
<
|
|
171
|
+
<Marquee className="items-center gap-4">
|
|
172
|
+
<span>One</span>
|
|
173
|
+
<span>Two</span>
|
|
131
174
|
</Marquee>
|
|
132
175
|
```
|
|
133
176
|
|
|
134
|
-
|
|
177
|
+
For predictable measurement, give the marquee or its parent a stable width for horizontal marquees and a stable height for vertical marquees.
|
|
178
|
+
|
|
179
|
+
## Runtime Notes
|
|
180
|
+
|
|
181
|
+
- The component uses `useLayoutEffect`, `ResizeObserver`, `requestAnimationFrame`, and DOM measurements, so it is intended for client-side rendering.
|
|
182
|
+
- In SSR frameworks such as Next.js, render it from a client component. Add `"use client"` to the file that imports and renders the marquee.
|
|
183
|
+
- Images that are not complete at mount are watched and trigger a re-measure after `load` or `error`.
|
|
184
|
+
- Changing animation props such as `dir`, `speed`, `delay`, `fill`, `draggable`, `spacing`, `loop`, or `paused` re-initializes the GSAP timeline.
|
|
185
|
+
|
|
186
|
+
## Troubleshooting
|
|
187
|
+
|
|
188
|
+
### The marquee has gaps
|
|
189
|
+
|
|
190
|
+
Use `fill={true}` for short content, increase `spacing` only as much as needed, and make sure images/fonts have stable dimensions. For image-heavy marquees, set explicit image width and height to reduce layout shifts.
|
|
191
|
+
|
|
192
|
+
### Vertical marquee does not move correctly
|
|
193
|
+
|
|
194
|
+
Give the container or one of its parents a real height. Vertical mode measures height, not width.
|
|
195
|
+
|
|
196
|
+
### The marquee expands the page
|
|
197
|
+
|
|
198
|
+
Place it in a container with an explicit width or max width. In fill mode, the component falls back to viewport measurement when it detects content-sized containers, but explicit layout constraints are still more predictable.
|
|
199
|
+
|
|
200
|
+
### Dragging has no momentum
|
|
201
|
+
|
|
202
|
+
Momentum depends on GSAP `InertiaPlugin` availability. If the plugin is not available in your GSAP installation, dragging can still work without inertia-style throwing.
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
Install dependencies:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
pnpm install
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Run a TypeScript check:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
pnpm exec tsc --noEmit
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Build the package:
|
|
135
219
|
|
|
136
|
-
|
|
220
|
+
```bash
|
|
221
|
+
pnpm run build
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Preview the published package contents:
|
|
137
225
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
- 🔧 Refactored animation engine to support both X and Y axis seamlessly
|
|
142
|
-
- 📊 Improved dimension calculations for vertical marquees
|
|
226
|
+
```bash
|
|
227
|
+
npm pack --dry-run
|
|
228
|
+
```
|
|
143
229
|
|
|
144
|
-
|
|
230
|
+
## License
|
|
145
231
|
|
|
146
|
-
|
|
147
|
-
- 🚀 Enhanced duplicate calculation algorithm for better performance
|
|
148
|
-
- 🔧 Improved seamless looping with smarter target width calculation
|
|
149
|
-
- 📝 Added comprehensive debug logging for development
|
|
150
|
-
- 🛡️ Added safety limits to prevent extreme duplicate scenarios
|
|
232
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),t=require("@gsap/react"),r=require("gsap"),a=require("gsap/all.js"),n=require("react"),s=require("clsx"),o=require("tailwind-merge");!function(e,t){void 0===t&&(t={});var r=t.insertAt;if("undefined"!=typeof document){var a=document.head||document.getElementsByTagName("head")[0],n=document.createElement("style");n.type="text/css","top"===r&&a.firstChild?a.insertBefore(n,a.firstChild):a.appendChild(n),n.styleSheet?n.styleSheet.cssText=e:n.appendChild(document.createTextNode(e))}}('.gsap-react-marquee-container{display:flex;overflow:hidden;position:relative;white-space:nowrap;width:100%}.gsap-react-marquee-container:after{background:linear-gradient(270deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);left:0}.gsap-react-marquee-container:after,.gsap-react-marquee-container:before{content:"";height:100%;pointer-events:none;position:absolute;top:0;width:15%;z-index:10}.gsap-react-marquee-container:before{background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);right:0}.gsap-react-marquee-vertical:after{background:linear-gradient(180deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;top:60%;width:100%}.gsap-react-marquee-vertical:before{background:linear-gradient(1turn,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;right:auto;top:0;width:100%}.gsap-react-marquee{flex:1;height:max-content;width:auto}.gsap-react-marquee,.gsap-react-marquee-content{display:flex;line-height:100%;white-space:nowrap}.gsap-react-marquee-content{overflow:hidden;width:max-content}');const i=new Set(["","auto","fit-content","min-content","max-content","-moz-fit-content","-webkit-fit-content"]),l=(...e)=>o.twMerge(s.clsx(e)),c=e=>{let t=e;for(;t;){const e=window.getComputedStyle(t).backgroundColor;if(e&&"rgba(0, 0, 0, 0)"!==e&&"transparent"!==e)return e;t=t.parentElement}return"transparent"},u=(e,t,a,n,s)=>{const{spacing:o=16}=s;r.gsap.set(e,{gap:`${o}px`,flexDirection:n?"column":"row"}),r.gsap.set(t,{gap:`${o}px`}),r.gsap.set(a,{overflow:n?"visible":"hidden"})},p=(e,t)=>{const r=e.style[t];if(r)return!i.has(r);const a=window.getComputedStyle(e)[t];if(i.has(a))return!1;const n=e.parentElement;if(!n)return!0;const s=window.getComputedStyle(n)[t];return!("100%"===a&&i.has(s))},d=(e,t)=>p(e,t?"height":"width")?t?e.offsetHeight:e.offsetWidth:t?window.innerHeight:window.innerWidth,g=d,f=(e,t,r)=>{const{fill:a=!1}=r;if(!a||e<=0||t<=0)return 1;const n=e<t?Math.ceil(t/e):1;return Math.min(n,15)},m=f,h=(e,t,r)=>{const{fill:a=!1}=r;return a?"auto":e<=t?"100%":`${e}px`},v=h,w=(e,t,n,s,o,i,l)=>{const{spacing:c=16,speed:u=100,delay:p=0,paused:d=!1,draggable:g=!1}=l,f=e.length-1;if(f<0)return;const m=[],h=[],v=i?"yPercent":"xPercent",w=i?"y":"x",x=i?"height":"width";r.gsap.set(e,{[v]:(e,t)=>{const a=parseFloat(String(r.gsap.getProperty(t,x,"px"))),n=parseFloat(String(r.gsap.getProperty(t,w,"px"))),s=Number(r.gsap.getProperty(t,v));return m[e]=a,h[e]=n/a*100+s,h[e]}}),r.gsap.set(e,{[w]:0});const y=e[f],q=i?y.offsetTop:y.offsetLeft,b=i?y.offsetHeight:y.offsetWidth,S=q+h[f]/100*m[f]-t+b+c;let C,E,T,k;e.forEach((e,r)=>{const a=m[r],s=h[r]/100*a,o=(i?e.offsetTop:e.offsetLeft)+s-t+a;n.to(e,{[v]:(s-o)/a*100,duration:o/u},0).fromTo(e,{[v]:(s-o+S)/a*100},{[v]:h[r],duration:(S-o)/u,immediateRender:!1},o/u)}),n.delay(p);const A=()=>r.gsap.delayedCall(p,()=>{n.reverse(),n.eventCallback("onReverseComplete",()=>{n.totalTime(n.rawTime()+100*n.duration())})});if(s){if(d)return void n.pause();n.progress(1).pause(),C=A()}if("function"==typeof a.Draggable&&g){const e=document.createElement("div");k=e;const t=r.gsap.utils.wrap(0,1);let l,c;const u=()=>{if(!T)return;const e=i?T.startY-T.y:T.startX-T.x;n.progress(t(c+e*l))};a.InertiaPlugin,T=a.Draggable.create(e,{trigger:o,type:i?"y":"x",onPress(){r.gsap.killTweensOf(n),n.pause(),c=n.progress(),l=1/S,r.gsap.set(e,{[w]:c/-l})},onDrag:u,onThrowUpdate:u,overshootTolerance:0,inertia:!0,onThrowComplete(){s?d?n.pause():(n.progress(n.progress()).pause(),null==E||E.kill(),E=A()):n.play()}})[0]}return()=>{null==C||C.kill(),null==E||E.kill(),null==T||T.kill(),null==k||k.remove()}},x=w;r.gsap.registerPlugin(t.useGSAP,a.Observer,a.InertiaPlugin,a.Draggable);const y=n.forwardRef((s,o)=>{var i;const{children:p,className:g,dir:m="left",loop:v=-1,paused:x=!1,delay:y=0,fill:q=!1,scrollFollow:b=!1,scrollSpeed:S=2.5,gradient:C=!1,gradientColor:E=null,pauseOnHover:T=!1,spacing:k=16,speed:A=100,draggable:L=!1}=s,P=n.useRef(null),M=n.useRef(null),[N,W]=n.useState(1),[O,R]=n.useState(null),[j,D]=n.useState(0),H=n.useCallback(e=>{P.current=e,"function"!=typeof o?o&&(o.current=e):o(e)},[o]);n.useLayoutEffect(()=>{if(!C||!P.current)return;const e=c(P.current);R(e)},[C]);const z="up"===m||"down"===m,F="down"===m||"right"===m;n.useLayoutEffect(()=>{const e=P.current;if(!e)return;const t=e.querySelector(".gsap-react-marquee .gsap-react-marquee-content");let r=null;const a=()=>{null==r&&(r=requestAnimationFrame(()=>{D(e=>e+1),r=null}))},n="undefined"!=typeof ResizeObserver?new ResizeObserver(a):null;n&&(n.observe(e),t&&n.observe(t));const s=Array.from(e.querySelectorAll("img")),o=()=>a();return s.forEach(e=>{e.complete||(e.addEventListener("load",o),e.addEventListener("error",o))}),()=>{null==n||n.disconnect(),s.forEach(e=>{e.removeEventListener("load",o),e.removeEventListener("error",o)}),null!=r&&cancelAnimationFrame(r)}},[p,g]),t.useGSAP((e,t)=>{if(!M.current||!P.current||!t)return;const n=P.current,s={fill:q,spacing:k,speed:A,delay:y,paused:x,draggable:L},o=r.gsap.utils.toArray(n.querySelectorAll(".gsap-react-marquee")),i=r.gsap.utils.toArray(n.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));if(!i.length)return;u(n,o,i,z,s);const l=z?n.offsetHeight:n.offsetWidth,c=z?i[0].offsetHeight:i[0].offsetWidth,p=d(n,z),g=z?i[0].offsetTop:i[0].offsetLeft;let m=null;const C=Math.min(4,Math.max(1.1,S)),E=f(c,p,s);if(N!==E)return void W(E);const O=r.gsap.timeline({paused:x,repeat:v,defaults:{ease:"none"},onReverseComplete(){O.totalTime(O.rawTime()+100*O.duration())}}),R=o.map(e=>z?e.offsetHeight:e.offsetWidth).reduce((e,t)=>e+t,0),j=h(q?0:R/2,l,s);r.gsap.set(o,{[z?"minHeight":"minWidth"]:j,flex:q?"0 0 auto":"1"});const D=w(q?i:o,g,O,F,o,z,s);b&&(m=a.Observer.create({onChangeY(e){let t=C*(F?-1:1);e.deltaY<0&&(t*=-1),r.gsap.timeline({defaults:{ease:"none"}}).to(O,{timeScale:t*C,duration:.2,overwrite:!0}).to(O,{timeScale:t/C,duration:1},"+=0.3")}}));const H=t(()=>{O.pause()}),B=t(()=>{F?O.reverse():O.play()});return T&&(n.addEventListener("mouseenter",H),n.addEventListener("mouseleave",B)),()=>{n.removeEventListener("mouseenter",H),n.removeEventListener("mouseleave",B),r.gsap.killTweensOf(O),O.kill(),null==m||m.kill(),null==D||D()}},{dependencies:[N,m,v,x,y,q,b,S,T,k,A,L,g,p,j],revertOnUpdate:!0});const B=null!==(i=null!=E?E:C?O:null)&&void 0!==i?i:"transparent",G=n.useMemo(()=>!Number.isFinite(N)||N<=0?null:Array.from({length:N},(t,r)=>e.jsx("div",{className:l("gsap-react-marquee"),children:e.jsx("div",{className:l("gsap-react-marquee-content",g),children:p})},r)),[N,g,p]);return e.jsxs("div",{ref:H,style:{"--gradient-color":B},className:l("gsap-react-marquee-container",{"gsap-react-marquee-vertical":z}),children:[e.jsx("div",{ref:M,className:l("gsap-react-marquee"),children:e.jsx("div",{className:l("gsap-react-marquee-content",g),children:p})}),G]})});y.displayName="GSAPReactMarquee",exports.calculateDuplicateCount=f,exports.calculateDuplicates=m,exports.cn=l,exports.coreAnimation=x,exports.createMarqueeAnimation=w,exports.default=y,exports.getEffectiveBackgroundColor=c,exports.getMinSize=h,exports.getMinWidth=v,exports.getTargetSize=d,exports.getTargetWidth=g,exports.hasDefinedWidth=e=>p(e,"width"),exports.setupContainerStyles=u;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { ClassValue } from 'clsx';
|
|
4
|
+
import { gsap } from 'gsap';
|
|
4
5
|
|
|
5
6
|
type GSAPReactMarqueeProps = {
|
|
6
7
|
children: ReactNode;
|
|
@@ -22,14 +23,19 @@ type GSAPReactMarqueeProps = {
|
|
|
22
23
|
|
|
23
24
|
declare const GSAPReactMarquee: react.ForwardRefExoticComponent<GSAPReactMarqueeProps & react.RefAttributes<HTMLDivElement>>;
|
|
24
25
|
|
|
26
|
+
type GSAPTimeline = ReturnType<typeof gsap.timeline>;
|
|
25
27
|
declare const cn: (...inputs: ClassValue[]) => string;
|
|
26
28
|
declare const getEffectiveBackgroundColor: (el: HTMLElement) => string;
|
|
27
|
-
declare const setupContainerStyles: (
|
|
29
|
+
declare const setupContainerStyles: (containerElement: HTMLElement, marqueeElements: HTMLElement[], contentElements: HTMLElement[], isVertical: boolean, props: GSAPReactMarqueeProps) => void;
|
|
28
30
|
declare const hasDefinedWidth: (element: HTMLElement) => boolean;
|
|
31
|
+
declare const getTargetSize: (containerElement: HTMLElement, isVertical: boolean) => number;
|
|
29
32
|
declare const getTargetWidth: (containerElement: HTMLElement, isVertical: boolean) => number;
|
|
30
|
-
declare const
|
|
31
|
-
declare const
|
|
32
|
-
declare const
|
|
33
|
+
declare const calculateDuplicateCount: (contentSize: number, targetSize: number, props: GSAPReactMarqueeProps) => number;
|
|
34
|
+
declare const calculateDuplicates: (contentSize: number, targetSize: number, props: GSAPReactMarqueeProps) => number;
|
|
35
|
+
declare const getMinSize: (itemSize: number, containerSize: number, props: GSAPReactMarqueeProps) => string | number;
|
|
36
|
+
declare const getMinWidth: (itemSize: number, containerSize: number, props: GSAPReactMarqueeProps) => string | number;
|
|
37
|
+
declare const createMarqueeAnimation: (items: HTMLElement[], startPosition: number, timeline: GSAPTimeline, isReverse: boolean, dragTrigger: HTMLElement | HTMLElement[], isVertical: boolean, props: GSAPReactMarqueeProps) => (() => void) | undefined;
|
|
38
|
+
declare const coreAnimation: (items: HTMLElement[], startPosition: number, timeline: GSAPTimeline, isReverse: boolean, dragTrigger: HTMLElement | HTMLElement[], isVertical: boolean, props: GSAPReactMarqueeProps) => (() => void) | undefined;
|
|
33
39
|
|
|
34
|
-
export { calculateDuplicates, cn, coreAnimation, GSAPReactMarquee as default, getEffectiveBackgroundColor, getMinWidth, getTargetWidth, hasDefinedWidth, setupContainerStyles };
|
|
40
|
+
export { calculateDuplicateCount, calculateDuplicates, cn, coreAnimation, createMarqueeAnimation, GSAPReactMarquee as default, getEffectiveBackgroundColor, getMinSize, getMinWidth, getTargetSize, getTargetWidth, hasDefinedWidth, setupContainerStyles };
|
|
35
41
|
export type { GSAPReactMarqueeProps };
|
package/dist/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e,{forwardRef as t,useRef as r,useState as n,useLayoutEffect as o,useMemo as a}from"react";import{useGSAP as i}from"@gsap/react";import l from"gsap";import{Draggable as s,InertiaPlugin as c,Observer as u}from"gsap/all";import{clsx as f}from"clsx";import{twMerge as p}from"tailwind-merge";var d,m,y={exports:{}},g={},v={};"production"===process.env.NODE_ENV?y.exports=function(){if(d)return g;d=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function r(t,r,n){var o=null;if(void 0!==n&&(o=""+n),void 0!==r.key&&(o=""+r.key),"key"in r)for(var a in n={},r)"key"!==a&&(n[a]=r[a]);else n=r;return r=n.ref,{$$typeof:e,type:t,key:o,ref:void 0!==r?r:null,props:n}}return g.Fragment=t,g.jsx=r,g.jsxs=r,g}():y.exports=(m||(m=1,"production"!==process.env.NODE_ENV&&function(){function t(e){if(null==e)return null;if("function"==typeof e)return e.$$typeof===O?null:e.displayName||e.name||null;if("string"==typeof e)return e;switch(e){case d:return"Fragment";case y:return"Profiler";case m:return"StrictMode";case w:return"Suspense";case S:return"SuspenseList";case _:return"Activity"}if("object"==typeof e)switch(e.tag,e.$$typeof){case p:return"Portal";case h:return(e.displayName||"Context")+".Provider";case g:return(e._context.displayName||"Context")+".Consumer";case b:var r=e.render;return(e=e.displayName)||(e=""!==(e=r.displayName||r.name||"")?"ForwardRef("+e+")":"ForwardRef"),e;case k:return null!==(r=e.displayName||null)?r:t(e.type)||"Memo";case x:r=e._payload,e=e._init;try{return t(e(r))}catch(e){}}return null}function r(e){return""+e}function n(e){try{r(e);var t=!1}catch(e){t=!0}if(t){var n=(t=console).error,o="function"==typeof Symbol&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return n.call(t,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",o),r(e)}}function o(e){if(e===d)return"<>";if("object"==typeof e&&null!==e&&e.$$typeof===x)return"<...>";try{var r=t(e);return r?"<"+r+">":"<...>"}catch(e){return"<...>"}}function a(){return Error("react-stack-top-frame")}function i(){var e=t(this.type);return q[e]||(q[e]=!0),void 0!==(e=this.props.ref)?e:null}function l(e,r,o,a,l,u,p,d){var m,y=r.children;if(void 0!==y)if(a){if(N(y)){for(a=0;a<y.length;a++)s(y[a]);Object.freeze&&Object.freeze(y)}}else s(y);if(E.call(r,"key")){y=t(e);var g=Object.keys(r).filter(function(e){return"key"!==e});a=0<g.length?"{key: someKey, "+g.join(": ..., ")+": ...}":"{key: someKey}",A[y+a]||(g=0<g.length?"{"+g.join(": ..., ")+": ...}":"{}",A[y+a]=!0)}if(y=null,void 0!==o&&(n(o),y=""+o),function(e){if(E.call(e,"key")){var t=Object.getOwnPropertyDescriptor(e,"key").get;if(t&&t.isReactWarning)return!1}return void 0!==e.key}(r)&&(n(r.key),y=""+r.key),"key"in r)for(var v in o={},r)"key"!==v&&(o[v]=r[v]);else o=r;return y&&function(e){function t(){c||(c=!0)}t.isReactWarning=!0,Object.defineProperty(e,"key",{get:t,configurable:!0})}(o,"function"==typeof e&&(e.displayName||e.name)),function(e,t,r,n,o,a,l,s){return r=a.ref,e={$$typeof:f,type:e,key:t,props:a,_owner:o},null!==(void 0!==r?r:null)?Object.defineProperty(e,"ref",{enumerable:!1,get:i}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:l}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:s}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}(e,y,u,0,null===(m=j.A)?null:m.getOwner(),o,p,d)}function s(e){"object"==typeof e&&null!==e&&e.$$typeof===f&&e._store&&(e._store.validated=1)}var c,u=e,f=Symbol.for("react.transitional.element"),p=Symbol.for("react.portal"),d=Symbol.for("react.fragment"),m=Symbol.for("react.strict_mode"),y=Symbol.for("react.profiler"),g=Symbol.for("react.consumer"),h=Symbol.for("react.context"),b=Symbol.for("react.forward_ref"),w=Symbol.for("react.suspense"),S=Symbol.for("react.suspense_list"),k=Symbol.for("react.memo"),x=Symbol.for("react.lazy"),_=Symbol.for("react.activity"),O=Symbol.for("react.client.reference"),j=u.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,E=Object.prototype.hasOwnProperty,N=Array.isArray,T=console.createTask?console.createTask:function(){return null},q={},C=(u={react_stack_bottom_frame:function(e){return e()}}).react_stack_bottom_frame.bind(u,a)(),P=T(o(a)),A={};v.Fragment=d,v.jsx=function(e,t,r,n,a){var i=1e4>j.recentlyCreatedOwnerStacks++;return l(e,t,r,!1,0,a,i?Error("react-stack-top-frame"):C,i?T(o(e)):P)},v.jsxs=function(e,t,r,n,a){var i=1e4>j.recentlyCreatedOwnerStacks++;return l(e,t,r,!0,0,a,i?Error("react-stack-top-frame"):C,i?T(o(e)):P)}}()),v);var h=y.exports;!function(e,t){void 0===t&&(t={});var r=t.insertAt;if("undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css","top"===r&&n.firstChild?n.insertBefore(o,n.firstChild):n.appendChild(o),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e))}}('.gsap-react-marquee-container{display:flex;overflow:hidden;position:relative;white-space:preserve nowrap;width:100%}.gsap-react-marquee-container:after{background:linear-gradient(270deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);left:0}.gsap-react-marquee-container:after,.gsap-react-marquee-container:before{content:"";height:100%;pointer-events:none;position:absolute;top:0;width:15%;z-index:10}.gsap-react-marquee-container:before{background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);right:0}.gsap-react-marquee-vertical:after{background:linear-gradient(180deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;top:60%;width:100%}.gsap-react-marquee-vertical:before{background:linear-gradient(1turn,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;right:auto;top:0;width:100%}.gsap-react-marquee{flex:1;height:max-content;width:auto}.gsap-react-marquee,.gsap-react-marquee-content{display:flex;line-height:100%;white-space:preserve nowrap}.gsap-react-marquee-content{overflow:hidden;width:max-content}');const b=(...e)=>p(f(e)),w=e=>{let t=e;for(;t;){const e=window.getComputedStyle(t).backgroundColor;if(e&&"rgba(0, 0, 0, 0)"!==e&&"transparent"!==e)return e;t=t.parentElement}return"transparent"},S=(e,t,r,n,o)=>{const{spacing:a=16}=o;l.set(e,{gap:`${a}px`,flexDirection:n?"column":"row"}),l.set(t,{gap:`${a}px`}),n&&l.set(r,{overflow:"visible"})},k=e=>{if(e.style.width&&"auto"!==e.style.width)return!0;const t=window.getComputedStyle(e).width;if("auto"===t||!t)return!1;const r=e.parentElement;if(r){const e=window.getComputedStyle(r);if("100%"===t&&"auto"===e.width)return!1}return!0},x=(e,t)=>k(e)?t?e.offsetHeight:e.offsetWidth:t?window.innerHeight:window.innerWidth,_=(e,t,r)=>r.fill&&e<t?Math.ceil(t/e):1,O=(e,t,r)=>{const{fill:n=!1}=r;return n?"auto":e<t?"100%":e>t?`${e}px`:`${t}px`},j=(e,t,r,n,o,a,i)=>{const{spacing:c=16,speed:u=100,delay:f=0,paused:p=!1}=i,d=[],m=[],y=e.length-1,g=a?"yPercent":"xPercent",v=a?"y":"x",h=a?"height":"width";l.set(e,{[g]:(e,t)=>{const r=d[e]=parseFloat(String(l.getProperty(t,h,"px")));return m[e]=parseFloat(String(l.getProperty(t,v,"px")))/r*100+Number(l.getProperty(t,g)),m[e]}}),l.set(e,{[v]:0});const b=e[y],w=a?b.offsetTop:b.offsetLeft,S=a?b.offsetHeight:b.offsetWidth,k=w+m[y]/100*d[y]-t+S+c;if(e.forEach((e,n)=>{const o=m[n]/100*d[n],i=(a?e.offsetTop:e.offsetLeft)+o-t+d[n];r.to(e,{[g]:(o-i)/d[n]*100,duration:i/u},0).fromTo(e,{[g]:(o-i+k)/d[n]*100},{[g]:m[n],duration:(o-i+k-o)/u,immediateRender:!1},i/u)}),r.delay(f),n){if(p)return void r.pause();r.progress(1).pause(),l.delayedCall(f,()=>{r.reverse(),r.eventCallback("onReverseComplete",()=>{r.totalTime(r.rawTime()+100*r.duration())})})}let x;if("function"==typeof s&&i.draggable){x=document.createElement("div");const e=l.utils.wrap(0,1);let t,i;const c=()=>{const n=a?u.startY-u.y:u.startX-u.x;r.progress(e(i+n*t))},u=s.create(x,{trigger:o,type:a?"y":"x",onPress(){l.killTweensOf(r),r.pause(),i=r.progress(),t=1/k,l.set(x,{[v]:i/-t})},onDrag:c,onThrowUpdate:c,overshootTolerance:0,inertia:!0,onThrowComplete:()=>{if(n){if(p)return void r.pause();r.progress(r.progress()).pause(),l.delayedCall(f,()=>{r.reverse(),r.eventCallback("onReverseComplete",()=>{r.totalTime(r.rawTime()+100*r.duration())})})}else r.play()}})[0]}};l.registerPlugin(i,u,c,s);const E=t((e,t)=>{const{children:s,className:c,dir:f="left",loop:p=-1,paused:d=!1,fill:m=!1,scrollFollow:y=!1,scrollSpeed:g=2.5,gradient:v=!1,gradientColor:k=null,pauseOnHover:x=!1,spacing:E=16,speed:N=100}=e,T=r(null),q=t||T,C=r(null),[P,A]=n(1),[$,R]=n(null),[L,F]=n(0);o(()=>{if(!v||!(null==q?void 0:q.current))return;const e=w(q.current);R(e)},[v]);const W="up"===f||"down"===f,H="down"===f||"right"===f;o(()=>{const e=q.current;if(!e)return;const t=e.querySelector(".gsap-react-marquee .gsap-react-marquee-content");let r=null;const n=()=>{null==r&&(r=requestAnimationFrame(()=>{F(e=>e+1),r=null}))},o=t?new ResizeObserver(n):null;t&&o&&o.observe(t);const a=Array.from(e.querySelectorAll("img")),i=()=>n();return a.forEach(e=>{e.complete||(e.addEventListener("load",i),e.addEventListener("error",i))}),()=>{null==o||o.disconnect(),a.forEach(e=>{e.removeEventListener("load",i),e.removeEventListener("error",i)}),null!=r&&cancelAnimationFrame(r)}},[s]),i((t,r)=>{if(!(null==C?void 0:C.current)||!q.current||!r)return;const n=null==q?void 0:q.current,o=l.utils.toArray(n.querySelectorAll(".gsap-react-marquee")),a=l.utils.toArray(n.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));if(!C.current||!a)return;const i=l.timeline({paused:d,repeat:p,defaults:{ease:"none"},onReverseComplete(){i.totalTime(i.rawTime()+100*i.duration())}});S(n,o,a,W,e);const s=W?n.offsetHeight:n.offsetWidth,c=W?a[0].offsetHeight:a[0].offsetWidth,f=W?a[0].offsetTop:a[0].offsetLeft;let v=null;const h=Math.min(4,Math.max(1.1,g));A(_(c,s,e));const b=l.utils.toArray(o).map(e=>W?e.offsetHeight:e.offsetWidth).reduce((e,t)=>e+t,0),w=O(b/(1===P?2:P),s,e);l.set(o,{[W?"minHeight":"minWidth"]:w,flex:m?"0 0 auto":"1"}),j(m?a:o,f,i,H,o,W,e),y&&(v=u.create({onChangeY(e){let t=h*(H?-1:1);e.deltaY<0&&(t*=-1),l.timeline({defaults:{ease:"none"}}).to(i,{timeScale:t*h,duration:.2,overwrite:!0}).to(i,{timeScale:t/h,duration:1},"+=0.3")}}));const k=r(()=>{i.pause()}),E=r(()=>{H?i.reverse():i.play()});return x&&(n.addEventListener("mouseenter",k),n.addEventListener("mouseleave",E)),()=>{n.removeEventListener("mouseenter",k),n.removeEventListener("mouseleave",E),i.kill(),null==v||v.kill()}},{dependencies:[P,f,p,d,m,y,g,v,k,x,E,N,s,L],revertOnUpdate:!0});const z=a(()=>!Number.isFinite(P)||P<=0?null:Array.from({length:P},(e,t)=>h.jsx("div",{className:b("gsap-react-marquee"),children:h.jsx("div",{className:b("gsap-react-marquee-content",c),children:s})},t)),[P,c,s]);return h.jsxs("div",{ref:q,style:{"--gradient-color":k||(v&&$?$:"transparent")},className:b("gsap-react-marquee-container",{"gsap-react-marquee-vertical":W}),children:[h.jsx("div",{ref:C,className:b("gsap-react-marquee"),children:h.jsx("div",{className:b("gsap-react-marquee-content",c),children:s})}),z]})});E.displayName="GSAPReactMarquee";export{_ as calculateDuplicates,b as cn,j as coreAnimation,E as default,w as getEffectiveBackgroundColor,O as getMinWidth,x as getTargetWidth,k as hasDefinedWidth,S as setupContainerStyles};
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useGSAP as r}from"@gsap/react";import{gsap as n}from"gsap";import{Draggable as a,InertiaPlugin as o,Observer as i}from"gsap/all.js";import{forwardRef as l,useRef as s,useState as c,useCallback as u,useLayoutEffect as d,useMemo as p}from"react";import{clsx as f}from"clsx";import{twMerge as m}from"tailwind-merge";!function(e,t){void 0===t&&(t={});var r=t.insertAt;if("undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],a=document.createElement("style");a.type="text/css","top"===r&&n.firstChild?n.insertBefore(a,n.firstChild):n.appendChild(a),a.styleSheet?a.styleSheet.cssText=e:a.appendChild(document.createTextNode(e))}}('.gsap-react-marquee-container{display:flex;overflow:hidden;position:relative;white-space:nowrap;width:100%}.gsap-react-marquee-container:after{background:linear-gradient(270deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);left:0}.gsap-react-marquee-container:after,.gsap-react-marquee-container:before{content:"";height:100%;pointer-events:none;position:absolute;top:0;width:15%;z-index:10}.gsap-react-marquee-container:before{background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);right:0}.gsap-react-marquee-vertical:after{background:linear-gradient(180deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;top:60%;width:100%}.gsap-react-marquee-vertical:before{background:linear-gradient(1turn,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;right:auto;top:0;width:100%}.gsap-react-marquee{flex:1;height:max-content;width:auto}.gsap-react-marquee,.gsap-react-marquee-content{display:flex;line-height:100%;white-space:nowrap}.gsap-react-marquee-content{overflow:hidden;width:max-content}');const g=new Set(["","auto","fit-content","min-content","max-content","-moz-fit-content","-webkit-fit-content"]),h=(...e)=>m(f(e)),v=e=>{let t=e;for(;t;){const e=window.getComputedStyle(t).backgroundColor;if(e&&"rgba(0, 0, 0, 0)"!==e&&"transparent"!==e)return e;t=t.parentElement}return"transparent"},w=(e,t,r,a,o)=>{const{spacing:i=16}=o;n.set(e,{gap:`${i}px`,flexDirection:a?"column":"row"}),n.set(t,{gap:`${i}px`}),n.set(r,{overflow:a?"visible":"hidden"})},y=(e,t)=>{const r=e.style[t];if(r)return!g.has(r);const n=window.getComputedStyle(e)[t];if(g.has(n))return!1;const a=e.parentElement;if(!a)return!0;const o=window.getComputedStyle(a)[t];return!("100%"===n&&g.has(o))},q=e=>y(e,"width"),x=(e,t)=>y(e,t?"height":"width")?t?e.offsetHeight:e.offsetWidth:t?window.innerHeight:window.innerWidth,b=x,E=(e,t,r)=>{const{fill:n=!1}=r;if(!n||e<=0||t<=0)return 1;const a=e<t?Math.ceil(t/e):1;return Math.min(a,15)},S=E,T=(e,t,r)=>{const{fill:n=!1}=r;return n?"auto":e<=t?"100%":`${e}px`},k=T,C=(e,t,r,o,i,l,s)=>{const{spacing:c=16,speed:u=100,delay:d=0,paused:p=!1,draggable:f=!1}=s,m=e.length-1;if(m<0)return;const g=[],h=[],v=l?"yPercent":"xPercent",w=l?"y":"x",y=l?"height":"width";n.set(e,{[v]:(e,t)=>{const r=parseFloat(String(n.getProperty(t,y,"px"))),a=parseFloat(String(n.getProperty(t,w,"px"))),o=Number(n.getProperty(t,v));return g[e]=r,h[e]=a/r*100+o,h[e]}}),n.set(e,{[w]:0});const q=e[m],x=l?q.offsetTop:q.offsetLeft,b=l?q.offsetHeight:q.offsetWidth,E=x+h[m]/100*g[m]-t+b+c;let S,T,k,C;e.forEach((e,n)=>{const a=g[n],o=h[n]/100*a,i=(l?e.offsetTop:e.offsetLeft)+o-t+a;r.to(e,{[v]:(o-i)/a*100,duration:i/u},0).fromTo(e,{[v]:(o-i+E)/a*100},{[v]:h[n],duration:(E-i)/u,immediateRender:!1},i/u)}),r.delay(d);const A=()=>n.delayedCall(d,()=>{r.reverse(),r.eventCallback("onReverseComplete",()=>{r.totalTime(r.rawTime()+100*r.duration())})});if(o){if(p)return void r.pause();r.progress(1).pause(),S=A()}if("function"==typeof a&&f){const e=document.createElement("div");C=e;const t=n.utils.wrap(0,1);let s,c;const u=()=>{if(!k)return;const e=l?k.startY-k.y:k.startX-k.x;r.progress(t(c+e*s))};k=a.create(e,{trigger:i,type:l?"y":"x",onPress(){n.killTweensOf(r),r.pause(),c=r.progress(),s=1/E,n.set(e,{[w]:c/-s})},onDrag:u,onThrowUpdate:u,overshootTolerance:0,inertia:!0,onThrowComplete(){o?p?r.pause():(r.progress(r.progress()).pause(),null==T||T.kill(),T=A()):r.play()}})[0]}return()=>{null==S||S.kill(),null==T||T.kill(),null==k||k.kill(),null==C||C.remove()}},A=C;n.registerPlugin(r,i,o,a);const L=l((a,o)=>{var l;const{children:f,className:m,dir:g="left",loop:y=-1,paused:q=!1,delay:b=0,fill:S=!1,scrollFollow:k=!1,scrollSpeed:A=2.5,gradient:L=!1,gradientColor:N=null,pauseOnHover:H=!1,spacing:P=16,speed:W=100,draggable:F=!1}=a,O=s(null),R=s(null),[M,z]=c(1),[Y,$]=c(null),[j,B]=c(0),D=u(e=>{O.current=e,"function"!=typeof o?o&&(o.current=e):o(e)},[o]);d(()=>{if(!L||!O.current)return;const e=v(O.current);$(e)},[L]);const U="up"===g||"down"===g,G="down"===g||"right"===g;d(()=>{const e=O.current;if(!e)return;const t=e.querySelector(".gsap-react-marquee .gsap-react-marquee-content");let r=null;const n=()=>{null==r&&(r=requestAnimationFrame(()=>{B(e=>e+1),r=null}))},a="undefined"!=typeof ResizeObserver?new ResizeObserver(n):null;a&&(a.observe(e),t&&a.observe(t));const o=Array.from(e.querySelectorAll("img")),i=()=>n();return o.forEach(e=>{e.complete||(e.addEventListener("load",i),e.addEventListener("error",i))}),()=>{null==a||a.disconnect(),o.forEach(e=>{e.removeEventListener("load",i),e.removeEventListener("error",i)}),null!=r&&cancelAnimationFrame(r)}},[f,m]),r((e,t)=>{if(!R.current||!O.current||!t)return;const r=O.current,a={fill:S,spacing:P,speed:W,delay:b,paused:q,draggable:F},o=n.utils.toArray(r.querySelectorAll(".gsap-react-marquee")),l=n.utils.toArray(r.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));if(!l.length)return;w(r,o,l,U,a);const s=U?r.offsetHeight:r.offsetWidth,c=U?l[0].offsetHeight:l[0].offsetWidth,u=x(r,U),d=U?l[0].offsetTop:l[0].offsetLeft;let p=null;const f=Math.min(4,Math.max(1.1,A)),m=E(c,u,a);if(M!==m)return void z(m);const g=n.timeline({paused:q,repeat:y,defaults:{ease:"none"},onReverseComplete(){g.totalTime(g.rawTime()+100*g.duration())}}),h=o.map(e=>U?e.offsetHeight:e.offsetWidth).reduce((e,t)=>e+t,0),v=T(S?0:h/2,s,a);n.set(o,{[U?"minHeight":"minWidth"]:v,flex:S?"0 0 auto":"1"});const L=C(S?l:o,d,g,G,o,U,a);k&&(p=i.create({onChangeY(e){let t=f*(G?-1:1);e.deltaY<0&&(t*=-1),n.timeline({defaults:{ease:"none"}}).to(g,{timeScale:t*f,duration:.2,overwrite:!0}).to(g,{timeScale:t/f,duration:1},"+=0.3")}}));const N=t(()=>{g.pause()}),Y=t(()=>{G?g.reverse():g.play()});return H&&(r.addEventListener("mouseenter",N),r.addEventListener("mouseleave",Y)),()=>{r.removeEventListener("mouseenter",N),r.removeEventListener("mouseleave",Y),n.killTweensOf(g),g.kill(),null==p||p.kill(),null==L||L()}},{dependencies:[M,g,y,q,b,S,k,A,H,P,W,F,m,f,j],revertOnUpdate:!0});const X=null!==(l=null!=N?N:L?Y:null)&&void 0!==l?l:"transparent",I=p(()=>!Number.isFinite(M)||M<=0?null:Array.from({length:M},(t,r)=>e("div",{className:h("gsap-react-marquee"),children:e("div",{className:h("gsap-react-marquee-content",m),children:f})},r)),[M,m,f]);return t("div",{ref:D,style:{"--gradient-color":X},className:h("gsap-react-marquee-container",{"gsap-react-marquee-vertical":U}),children:[e("div",{ref:R,className:h("gsap-react-marquee"),children:e("div",{className:h("gsap-react-marquee-content",m),children:f})}),I]})});L.displayName="GSAPReactMarquee";export{E as calculateDuplicateCount,S as calculateDuplicates,h as cn,A as coreAnimation,C as createMarqueeAnimation,L as default,v as getEffectiveBackgroundColor,T as getMinSize,k as getMinWidth,x as getTargetSize,b as getTargetWidth,q as hasDefinedWidth,w as setupContainerStyles};
|
package/package.json
CHANGED
|
@@ -3,20 +3,22 @@
|
|
|
3
3
|
"author": "David Domenico Piscopo",
|
|
4
4
|
"description": "A high-performance React marquee component powered by GSAP",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "0.3.
|
|
6
|
+
"version": "0.3.2",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"main": "dist/index.cjs
|
|
8
|
+
"main": "dist/index.cjs",
|
|
9
9
|
"module": "dist/index.esm.js",
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
13
|
"import": "./dist/index.esm.js",
|
|
14
|
-
"require": "./dist/index.cjs
|
|
14
|
+
"require": "./dist/index.cjs",
|
|
15
15
|
"types": "./dist/index.d.ts"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
|
-
"dist",
|
|
19
|
+
"dist/index.cjs",
|
|
20
|
+
"dist/index.esm.js",
|
|
21
|
+
"dist/index.d.ts",
|
|
20
22
|
"README.md",
|
|
21
23
|
"LICENSE"
|
|
22
24
|
],
|
|
@@ -42,31 +44,29 @@
|
|
|
42
44
|
"bugs": {
|
|
43
45
|
"url": "https://github.com/daviddpi/gsap-react-marquee/issues"
|
|
44
46
|
},
|
|
45
|
-
"homepage": "https://
|
|
47
|
+
"homepage": "https://gsap-react-marquee-docs.vercel.app/",
|
|
46
48
|
"peerDependencies": {
|
|
47
49
|
"@gsap/react": "^2.1.1",
|
|
48
|
-
"clsx": "^2.1.0",
|
|
49
50
|
"gsap": "^3.12.0",
|
|
50
51
|
"react": ">=18",
|
|
51
|
-
"react-dom": ">=18"
|
|
52
|
-
"tailwind-merge": "^2.2.0"
|
|
52
|
+
"react-dom": ">=18"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@gsap/react": "^2.1.1",
|
|
56
56
|
"@rollup/plugin-commonjs": "^25.0.7",
|
|
57
57
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
58
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
58
59
|
"@rollup/plugin-typescript": "^11.1.6",
|
|
59
60
|
"@types/react": "^18.3.3",
|
|
60
61
|
"@types/react-dom": "^18.3.0",
|
|
61
|
-
"clsx": "^2.1.1",
|
|
62
62
|
"rollup": "^4.18.0",
|
|
63
63
|
"rollup-plugin-dts": "^6.1.1",
|
|
64
64
|
"rollup-plugin-postcss": "^4.0.2",
|
|
65
|
-
"tailwind-merge": "^2.3.0",
|
|
66
65
|
"tslib": "^2.8.1",
|
|
67
66
|
"typescript": "^5.5.2"
|
|
68
67
|
},
|
|
69
68
|
"dependencies": {
|
|
70
|
-
"
|
|
69
|
+
"clsx": "^2.1.1",
|
|
70
|
+
"tailwind-merge": "^2.3.0"
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.gsap-react-marquee-container:after{background:linear-gradient(270deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);left:0}.gsap-react-marquee-container:after,.gsap-react-marquee-container:before{content:"";height:100%;pointer-events:none;position:absolute;top:0;width:15%;z-index:10}.gsap-react-marquee-container:before{background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);right:0}.gsap-react-marquee{flex:1;height:max-content;width:auto}.gsap-react-marquee,.gsap-react-marquee-content{display:flex;line-height:100%;white-space:preserve nowrap}.gsap-react-marquee-content{overflow:hidden;width:max-content}
|
package/dist/index.cjs.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t,r=require("react"),n=require("@gsap/react"),a=require("gsap"),o=require("gsap/all"),i=require("clsx"),s=require("tailwind-merge"),l={exports:{}},c={},u={};"production"===process.env.NODE_ENV?l.exports=function(){if(e)return c;e=1;var t=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function n(e,r,n){var a=null;if(void 0!==n&&(a=""+n),void 0!==r.key&&(a=""+r.key),"key"in r)for(var o in n={},r)"key"!==o&&(n[o]=r[o]);else n=r;return r=n.ref,{$$typeof:t,type:e,key:a,ref:void 0!==r?r:null,props:n}}return c.Fragment=r,c.jsx=n,c.jsxs=n,c}():l.exports=(t||(t=1,"production"!==process.env.NODE_ENV&&function(){function e(t){if(null==t)return null;if("function"==typeof t)return t.$$typeof===q?null:t.displayName||t.name||null;if("string"==typeof t)return t;switch(t){case m:return"Fragment";case y:return"Profiler";case g:return"StrictMode";case w:return"Suspense";case S:return"SuspenseList";case _:return"Activity"}if("object"==typeof t)switch(t.tag,t.$$typeof){case p:return"Portal";case h:return(t.displayName||"Context")+".Provider";case v:return(t._context.displayName||"Context")+".Consumer";case b:var r=t.render;return(t=t.displayName)||(t=""!==(t=r.displayName||r.name||"")?"ForwardRef("+t+")":"ForwardRef"),t;case x:return null!==(r=t.displayName||null)?r:e(t.type)||"Memo";case k:r=t._payload,t=t._init;try{return e(t(r))}catch(e){}}return null}function t(e){return""+e}function n(e){try{t(e);var r=!1}catch(e){r=!0}if(r){var n=(r=console).error,a="function"==typeof Symbol&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return n.call(r,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",a),t(e)}}function a(t){if(t===m)return"<>";if("object"==typeof t&&null!==t&&t.$$typeof===k)return"<...>";try{var r=e(t);return r?"<"+r+">":"<...>"}catch(e){return"<...>"}}function o(){return Error("react-stack-top-frame")}function i(){var t=e(this.type);return N[t]||(N[t]=!0),void 0!==(t=this.props.ref)?t:null}function s(t,r,a,o,s,u,f,p){var m,g=r.children;if(void 0!==g)if(o){if(j(g)){for(o=0;o<g.length;o++)l(g[o]);Object.freeze&&Object.freeze(g)}}else l(g);if(E.call(r,"key")){g=e(t);var y=Object.keys(r).filter(function(e){return"key"!==e});o=0<y.length?"{key: someKey, "+y.join(": ..., ")+": ...}":"{key: someKey}",A[g+o]||(y=0<y.length?"{"+y.join(": ..., ")+": ...}":"{}",A[g+o]=!0)}if(g=null,void 0!==a&&(n(a),g=""+a),function(e){if(E.call(e,"key")){var t=Object.getOwnPropertyDescriptor(e,"key").get;if(t&&t.isReactWarning)return!1}return void 0!==e.key}(r)&&(n(r.key),g=""+r.key),"key"in r)for(var v in a={},r)"key"!==v&&(a[v]=r[v]);else a=r;return g&&function(e){function t(){c||(c=!0)}t.isReactWarning=!0,Object.defineProperty(e,"key",{get:t,configurable:!0})}(a,"function"==typeof t&&(t.displayName||t.name)),function(e,t,r,n,a,o,s,l){return r=o.ref,e={$$typeof:d,type:e,key:t,props:o,_owner:a},null!==(void 0!==r?r:null)?Object.defineProperty(e,"ref",{enumerable:!1,get:i}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:s}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:l}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}(t,g,u,0,null===(m=O.A)?null:m.getOwner(),a,f,p)}function l(e){"object"==typeof e&&null!==e&&e.$$typeof===d&&e._store&&(e._store.validated=1)}var c,f=r,d=Symbol.for("react.transitional.element"),p=Symbol.for("react.portal"),m=Symbol.for("react.fragment"),g=Symbol.for("react.strict_mode"),y=Symbol.for("react.profiler"),v=Symbol.for("react.consumer"),h=Symbol.for("react.context"),b=Symbol.for("react.forward_ref"),w=Symbol.for("react.suspense"),S=Symbol.for("react.suspense_list"),x=Symbol.for("react.memo"),k=Symbol.for("react.lazy"),_=Symbol.for("react.activity"),q=Symbol.for("react.client.reference"),O=f.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,E=Object.prototype.hasOwnProperty,j=Array.isArray,T=console.createTask?console.createTask:function(){return null},N={},C=(f={react_stack_bottom_frame:function(e){return e()}}).react_stack_bottom_frame.bind(f,o)(),P=T(a(o)),A={};u.Fragment=m,u.jsx=function(e,t,r,n,o){var i=1e4>O.recentlyCreatedOwnerStacks++;return s(e,t,r,!1,0,o,i?Error("react-stack-top-frame"):C,i?T(a(e)):P)},u.jsxs=function(e,t,r,n,o){var i=1e4>O.recentlyCreatedOwnerStacks++;return s(e,t,r,!0,0,o,i?Error("react-stack-top-frame"):C,i?T(a(e)):P)}}()),u);var f=l.exports;!function(e,t){void 0===t&&(t={});var r=t.insertAt;if("undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],a=document.createElement("style");a.type="text/css","top"===r&&n.firstChild?n.insertBefore(a,n.firstChild):n.appendChild(a),a.styleSheet?a.styleSheet.cssText=e:a.appendChild(document.createTextNode(e))}}('.gsap-react-marquee-container{display:flex;overflow:hidden;position:relative;white-space:preserve nowrap;width:100%}.gsap-react-marquee-container:after{background:linear-gradient(270deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);left:0}.gsap-react-marquee-container:after,.gsap-react-marquee-container:before{content:"";height:100%;pointer-events:none;position:absolute;top:0;width:15%;z-index:10}.gsap-react-marquee-container:before{background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);right:0}.gsap-react-marquee-vertical:after{background:linear-gradient(180deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;top:60%;width:100%}.gsap-react-marquee-vertical:before{background:linear-gradient(1turn,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;right:auto;top:0;width:100%}.gsap-react-marquee{flex:1;height:max-content;width:auto}.gsap-react-marquee,.gsap-react-marquee-content{display:flex;line-height:100%;white-space:preserve nowrap}.gsap-react-marquee-content{overflow:hidden;width:max-content}');const d=(...e)=>s.twMerge(i.clsx(e)),p=e=>{let t=e;for(;t;){const e=window.getComputedStyle(t).backgroundColor;if(e&&"rgba(0, 0, 0, 0)"!==e&&"transparent"!==e)return e;t=t.parentElement}return"transparent"},m=(e,t,r,n,o)=>{const{spacing:i=16}=o;a.set(e,{gap:`${i}px`,flexDirection:n?"column":"row"}),a.set(t,{gap:`${i}px`}),n&&a.set(r,{overflow:"visible"})},g=e=>{if(e.style.width&&"auto"!==e.style.width)return!0;const t=window.getComputedStyle(e).width;if("auto"===t||!t)return!1;const r=e.parentElement;if(r){const e=window.getComputedStyle(r);if("100%"===t&&"auto"===e.width)return!1}return!0},y=(e,t,r)=>r.fill&&e<t?Math.ceil(t/e):1,v=(e,t,r)=>{const{fill:n=!1}=r;return n?"auto":e<t?"100%":e>t?`${e}px`:`${t}px`},h=(e,t,r,n,i,s,l)=>{const{spacing:c=16,speed:u=100,delay:f=0,paused:d=!1}=l,p=[],m=[],g=e.length-1,y=s?"yPercent":"xPercent",v=s?"y":"x",h=s?"height":"width";a.set(e,{[y]:(e,t)=>{const r=p[e]=parseFloat(String(a.getProperty(t,h,"px")));return m[e]=parseFloat(String(a.getProperty(t,v,"px")))/r*100+Number(a.getProperty(t,y)),m[e]}}),a.set(e,{[v]:0});const b=e[g],w=s?b.offsetTop:b.offsetLeft,S=s?b.offsetHeight:b.offsetWidth,x=w+m[g]/100*p[g]-t+S+c;if(e.forEach((e,n)=>{const a=m[n]/100*p[n],o=(s?e.offsetTop:e.offsetLeft)+a-t+p[n];r.to(e,{[y]:(a-o)/p[n]*100,duration:o/u},0).fromTo(e,{[y]:(a-o+x)/p[n]*100},{[y]:m[n],duration:(a-o+x-a)/u,immediateRender:!1},o/u)}),r.delay(f),n){if(d)return void r.pause();r.progress(1).pause(),a.delayedCall(f,()=>{r.reverse(),r.eventCallback("onReverseComplete",()=>{r.totalTime(r.rawTime()+100*r.duration())})})}let k;if("function"==typeof o.Draggable&&l.draggable){k=document.createElement("div");const e=a.utils.wrap(0,1);let t,l;const c=()=>{const n=s?u.startY-u.y:u.startX-u.x;r.progress(e(l+n*t))};o.InertiaPlugin;const u=o.Draggable.create(k,{trigger:i,type:s?"y":"x",onPress(){a.killTweensOf(r),r.pause(),l=r.progress(),t=1/x,a.set(k,{[v]:l/-t})},onDrag:c,onThrowUpdate:c,overshootTolerance:0,inertia:!0,onThrowComplete:()=>{if(n){if(d)return void r.pause();r.progress(r.progress()).pause(),a.delayedCall(f,()=>{r.reverse(),r.eventCallback("onReverseComplete",()=>{r.totalTime(r.rawTime()+100*r.duration())})})}else r.play()}})[0]}};a.registerPlugin(n.useGSAP,o.Observer,o.InertiaPlugin,o.Draggable);const b=r.forwardRef((e,t)=>{const{children:i,className:s,dir:l="left",loop:c=-1,paused:u=!1,fill:g=!1,scrollFollow:b=!1,scrollSpeed:w=2.5,gradient:S=!1,gradientColor:x=null,pauseOnHover:k=!1,spacing:_=16,speed:q=100}=e,O=r.useRef(null),E=t||O,j=r.useRef(null),[T,N]=r.useState(1),[C,P]=r.useState(null),[A,R]=r.useState(0);r.useLayoutEffect(()=>{if(!S||!(null==E?void 0:E.current))return;const e=p(E.current);P(e)},[S]);const L="up"===l||"down"===l,$="down"===l||"right"===l;r.useLayoutEffect(()=>{const e=E.current;if(!e)return;const t=e.querySelector(".gsap-react-marquee .gsap-react-marquee-content");let r=null;const n=()=>{null==r&&(r=requestAnimationFrame(()=>{R(e=>e+1),r=null}))},a=t?new ResizeObserver(n):null;t&&a&&a.observe(t);const o=Array.from(e.querySelectorAll("img")),i=()=>n();return o.forEach(e=>{e.complete||(e.addEventListener("load",i),e.addEventListener("error",i))}),()=>{null==a||a.disconnect(),o.forEach(e=>{e.removeEventListener("load",i),e.removeEventListener("error",i)}),null!=r&&cancelAnimationFrame(r)}},[i]),n.useGSAP((t,r)=>{if(!(null==j?void 0:j.current)||!E.current||!r)return;const n=null==E?void 0:E.current,i=a.utils.toArray(n.querySelectorAll(".gsap-react-marquee")),s=a.utils.toArray(n.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));if(!j.current||!s)return;const l=a.timeline({paused:u,repeat:c,defaults:{ease:"none"},onReverseComplete(){l.totalTime(l.rawTime()+100*l.duration())}});m(n,i,s,L,e);const f=L?n.offsetHeight:n.offsetWidth,d=L?s[0].offsetHeight:s[0].offsetWidth,p=L?s[0].offsetTop:s[0].offsetLeft;let S=null;const x=Math.min(4,Math.max(1.1,w));N(y(d,f,e));const _=a.utils.toArray(i).map(e=>L?e.offsetHeight:e.offsetWidth).reduce((e,t)=>e+t,0),q=v(_/(1===T?2:T),f,e);a.set(i,{[L?"minHeight":"minWidth"]:q,flex:g?"0 0 auto":"1"}),h(g?s:i,p,l,$,i,L,e),b&&(S=o.Observer.create({onChangeY(e){let t=x*($?-1:1);e.deltaY<0&&(t*=-1),a.timeline({defaults:{ease:"none"}}).to(l,{timeScale:t*x,duration:.2,overwrite:!0}).to(l,{timeScale:t/x,duration:1},"+=0.3")}}));const O=r(()=>{l.pause()}),C=r(()=>{$?l.reverse():l.play()});return k&&(n.addEventListener("mouseenter",O),n.addEventListener("mouseleave",C)),()=>{n.removeEventListener("mouseenter",O),n.removeEventListener("mouseleave",C),l.kill(),null==S||S.kill()}},{dependencies:[T,l,c,u,g,b,w,S,x,k,_,q,i,A],revertOnUpdate:!0});const W=r.useMemo(()=>!Number.isFinite(T)||T<=0?null:Array.from({length:T},(e,t)=>f.jsx("div",{className:d("gsap-react-marquee"),children:f.jsx("div",{className:d("gsap-react-marquee-content",s),children:i})},t)),[T,s,i]);return f.jsxs("div",{ref:E,style:{"--gradient-color":x||(S&&C?C:"transparent")},className:d("gsap-react-marquee-container",{"gsap-react-marquee-vertical":L}),children:[f.jsx("div",{ref:j,className:d("gsap-react-marquee"),children:f.jsx("div",{className:d("gsap-react-marquee-content",s),children:i})}),W]})});b.displayName="GSAPReactMarquee",exports.calculateDuplicates=y,exports.cn=d,exports.coreAnimation=h,exports.default=b,exports.getEffectiveBackgroundColor=p,exports.getMinWidth=v,exports.getTargetWidth=(e,t)=>g(e)?t?e.offsetHeight:e.offsetWidth:t?window.innerHeight:window.innerWidth,exports.hasDefinedWidth=g,exports.setupContainerStyles=m;
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import "./gsap-react-marquee.style.css";
|
|
2
|
-
import type { GSAPReactMarqueeProps } from "./gsap-react-marquee.type";
|
|
3
|
-
declare const GSAPReactMarquee: import("react").ForwardRefExoticComponent<GSAPReactMarqueeProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
4
|
-
export default GSAPReactMarquee;
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export declare const marqueeStyles = "\n.gsap-react-marquee-container::after {\n content: \"\";\n position: absolute;\n top: 0;\n left: 0;\n width: 15%;\n height: 100%;\n background: linear-gradient(\n 270deg,\n rgba(255, 255, 255, 0) 0%,\n var(--gradient-color) 75%\n );\n z-index: 10;\n pointer-events: none;\n}\n\n.gsap-react-marquee-container::before {\n content: \"\";\n position: absolute;\n top: 0;\n right: 0;\n width: 15%;\n height: 100%;\n background: linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0) 0%,\n var(--gradient-color) 75%\n );\n z-index: 10;\n pointer-events: none;\n}\n\n.gsap-react-marquee {\n width: auto;\n height: max-content;\n line-height: 100%;\n white-space: preserve nowrap;\n display: flex;\n flex: 1;\n}\n\n.gsap-react-marquee-content {\n width: max-content;\n line-height: 100%;\n white-space: preserve nowrap;\n display: flex;\n overflow: hidden;\n}\n";
|
|
2
|
-
export declare const injectStyles: () => void;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
export type GSAPReactMarqueeProps = {
|
|
3
|
-
children: ReactNode;
|
|
4
|
-
className?: string;
|
|
5
|
-
dir?: "right" | "left" | "up" | "down";
|
|
6
|
-
loop?: number;
|
|
7
|
-
paused?: boolean;
|
|
8
|
-
delay?: number;
|
|
9
|
-
speed?: number;
|
|
10
|
-
fill?: boolean;
|
|
11
|
-
pauseOnHover?: boolean;
|
|
12
|
-
gradient?: boolean;
|
|
13
|
-
gradientColor?: string;
|
|
14
|
-
spacing?: number;
|
|
15
|
-
draggable?: boolean;
|
|
16
|
-
scrollFollow?: boolean;
|
|
17
|
-
scrollSpeed?: number;
|
|
18
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { type ClassValue } from "clsx";
|
|
2
|
-
import type { GSAPReactMarqueeProps } from "./gsap-react-marquee.type";
|
|
3
|
-
export declare const cn: (...inputs: ClassValue[]) => string;
|
|
4
|
-
export declare const getEffectiveBackgroundColor: (el: HTMLElement) => string;
|
|
5
|
-
export declare const setupContainerStyles: (containerMarquee: HTMLElement, marquees: HTMLElement[], marqueesChildren: HTMLElement[], isVertical: boolean, props: GSAPReactMarqueeProps) => void;
|
|
6
|
-
export declare const hasDefinedWidth: (element: HTMLElement) => boolean;
|
|
7
|
-
export declare const getTargetWidth: (containerElement: HTMLElement, isVertical: boolean) => number;
|
|
8
|
-
export declare const calculateDuplicates: (marqueeChildrenWidth: number, containerMarqueeWidth: number, props: GSAPReactMarqueeProps) => number;
|
|
9
|
-
export declare const getMinWidth: (totalSize: number, containerSize: number, props: GSAPReactMarqueeProps) => string | number;
|
|
10
|
-
export declare const coreAnimation: (elementsToAnimate: HTMLElement[], startPos: number, tl: gsap.core.Timeline, isReverse: boolean, draggableTrigger: HTMLElement | HTMLElement[], isVertical: boolean, props: GSAPReactMarqueeProps) => void;
|
package/dist/types/index.d.ts
DELETED