Skip to content

Commit b0784b1

Browse files
author
Jan Fischer
committed
Merge branch 'feature/reveal'
2 parents 9030089 + ff66a93 commit b0784b1

26 files changed

+1309
-138
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ It abstracts away the direct use of the GSAP [Tween](https://greensock.com/docs/
1313

1414
If you need the full control it's possible by getting low level access to the underlying objects.
1515

16-
In addition to that it has it's own SVG drawing Plugin and some useful helper components.
16+
In addition to that it ships some GSAP Plugins and useful helper components.
1717

1818
From version 2 on it's build for GSAP 3 and only has `gsap` as a peer dependency. In this way you can update `gsap` separately from `react-gsap`.
1919

2020
It's built with TypeScript and ships the types directly in the package.
2121

2222
Documentation and examples are here: https://bitworking.github.io/react-gsap/
2323

24+
##### The examples on the documentation pages are all editable directly in the browser. So play with it!
25+
2426
## Installation
2527

2628
```bash

packages/docz/doczrc.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ export default {
77
'react-gsap lets you use the GreenSock Animation Platform (GSAP) in React in a fully declarative way. It abstracts away the direct use of the GSAP Tween and Timeline functions.',
88
menu: [
99
'Introduction',
10-
{ name: 'Components', menu: ['Tween', 'Timeline', 'SplitLetters', 'SplitWords', 'Controls'] },
10+
{
11+
name: 'Components',
12+
menu: ['Tween', 'Timeline', 'Reveal', 'SplitChars', 'SplitWords', 'Controls'],
13+
},
1114
{ name: 'Plugins', menu: ['GSAP Plugins', 'CountPlugin', 'SvgDrawPlugin'] },
1215
'Instructions',
1316
],
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import React, { Fragment, ReactComponentElement, useEffect, useRef, useState } from 'react';
2+
import { gsap } from 'gsap';
3+
import { SplitChars, SplitWords, Timeline, Tween } from './../../../react-gsap/src';
4+
5+
export const FadeIn = ({
6+
children,
7+
...rest
8+
}: {
9+
children: React.ReactNode;
10+
[key: string]: any;
11+
}) => (
12+
<Tween from={{ opacity: 0 }} {...rest}>
13+
{children}
14+
</Tween>
15+
);
16+
17+
export const FadeInLeft = ({
18+
children,
19+
...rest
20+
}: {
21+
children: React.ReactNode;
22+
[key: string]: any;
23+
}) => (
24+
<Tween
25+
from={{ opacity: 0, transform: 'translate3d(-100vw, 0, 0)' }}
26+
ease="back.out(1.4)"
27+
{...rest}
28+
>
29+
{children}
30+
</Tween>
31+
);
32+
33+
export const RubberBand = ({
34+
children,
35+
...rest
36+
}: {
37+
children: React.ReactNode;
38+
[key: string]: any;
39+
}) => (
40+
<Timeline target={children} {...rest}>
41+
<Tween to={{ scaleX: 1.25, scaleY: 0.75 }} ease="power1.inOut" duration={0.3} />
42+
<Tween to={{ scaleX: 0.75, scaleY: 1.25 }} ease="power1.inOut" duration={0.1} />
43+
<Tween to={{ scaleX: 1.15, scaleY: 0.85 }} ease="power1.inOut" duration={0.1} />
44+
<Tween to={{ scaleX: 0.95, scaleY: 1.05 }} ease="power1.inOut" duration={0.15} />
45+
<Tween to={{ scaleX: 1.05, scaleY: 0.95 }} ease="power1.inOut" duration={0.1} />
46+
<Tween to={{ scaleX: 1, scaleY: 1 }} ease="power1.inOut" duration={0.25} />
47+
</Timeline>
48+
);
49+
50+
export const FadeInLeftChars = ({
51+
children,
52+
wrapper,
53+
...rest
54+
}: {
55+
children: React.ReactNode;
56+
wrapper: ReactComponentElement<any>;
57+
[key: string]: any;
58+
}) => (
59+
<Tween from={{ opacity: 0, x: '-100vw' }} ease="power1.inOut" {...rest} stagger={0.1}>
60+
<SplitChars wrapper={wrapper}>{children}</SplitChars>
61+
</Tween>
62+
);
63+
64+
export const FadeInLeftWords = ({
65+
children,
66+
wrapper,
67+
...rest
68+
}: {
69+
children: React.ReactNode;
70+
wrapper: ReactComponentElement<any>;
71+
[key: string]: any;
72+
}) => (
73+
<Tween from={{ opacity: 0, x: '-100vw' }} ease="power1.inOut" {...rest} stagger={0.5}>
74+
<SplitWords wrapper={wrapper}>{children}</SplitWords>
75+
</Tween>
76+
);
77+
78+
export const CutText = ({
79+
children,
80+
numberSlices = 4,
81+
type = 0,
82+
...rest
83+
}: {
84+
children: string;
85+
numberSlices?: number;
86+
type?: number;
87+
[key: string]: any;
88+
}) => {
89+
const textRef = useRef<SVGTextElement>(null);
90+
const [viewBox, setViewBox] = useState({ x: 0, y: 0, width: 0, height: 0 });
91+
const [sliceHeight, setSliceHeight] = useState(0);
92+
93+
useEffect(() => {
94+
const boundingBox = textRef.current
95+
? textRef.current.getBBox()
96+
: { x: 0, y: 0, width: 0, height: 0 };
97+
const { x, y, width, height } = boundingBox;
98+
setViewBox({ x, y, width, height });
99+
100+
setSliceHeight(height / numberSlices);
101+
}, []);
102+
103+
return (
104+
<svg
105+
xmlns="http://www.w3.org/2000/svg"
106+
width={viewBox.width}
107+
height={viewBox.height}
108+
viewBox={`0 0 ${viewBox.width} ${viewBox.height}`}
109+
>
110+
<defs>
111+
<pattern
112+
id="cutPattern"
113+
patternUnits="userSpaceOnUse"
114+
width={viewBox.width}
115+
height={viewBox.height}
116+
x="0"
117+
y="0"
118+
>
119+
<text ref={textRef} x="0" y={-viewBox.y} textAnchor="left" fontSize="80" fill="#000">
120+
{children}
121+
</text>
122+
</pattern>
123+
</defs>
124+
<Timeline
125+
wrapper={<g fill="url(#cutPattern)" />}
126+
target={
127+
<Fragment>
128+
{Array.from({ length: numberSlices }).map((_, index) => (
129+
<rect
130+
key={index}
131+
x="0"
132+
y={index * sliceHeight}
133+
width={viewBox.width}
134+
height={sliceHeight + 1}
135+
/>
136+
))}
137+
</Fragment>
138+
}
139+
{...rest}
140+
>
141+
{type === 0 && (
142+
<Tween
143+
from={{
144+
x: gsap.utils.wrap([-1000, 1000]),
145+
ease: 'Back.easeOut',
146+
}}
147+
stagger={0.15}
148+
/>
149+
)}
150+
151+
{type === 1 && (
152+
<Tween
153+
duration={0.4}
154+
to={{
155+
x: gsap.utils.wrap([-50, 70, -70, 120]),
156+
opacity: gsap.utils.wrap([0.5, 0.8]),
157+
}}
158+
stagger={-0.05}
159+
repeat={1}
160+
repeatDelay={0.2}
161+
ease="Back.easeInOut"
162+
yoyoEase="Elastic.easeOut"
163+
/>
164+
)}
165+
{type === 2 && (
166+
<Tween
167+
duration={0.4}
168+
to={{
169+
y: gsap.utils.wrap([-30, -10, 10, 30]),
170+
repeat: 1,
171+
repeatDelay: 0.3,
172+
yoyo: true,
173+
ease: 'Circ.easeInOut',
174+
}}
175+
stagger={0}
176+
/>
177+
)}
178+
</Timeline>
179+
</svg>
180+
);
181+
};
182+
183+
export const AnimationTrigger = React.forwardRef<HTMLDivElement>(({ children }, ref) => (
184+
<div
185+
ref={ref}
186+
style={{
187+
paddingTop: '200px',
188+
paddingBottom: '200px',
189+
background: '#f0f0f0',
190+
textAlign: 'center',
191+
}}
192+
>
193+
{children}
194+
</div>
195+
));
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
---
2+
name: Reveal
3+
menu: Components
4+
---
5+
6+
import { Fragment } from 'react';
7+
import { Playground, Props } from 'docz'
8+
import { Reveal, SplitLetters } from './../../../react-gsap/src/'
9+
import { Tween, Comment } from './Tween'
10+
import { FadeInLeft, AnimationTrigger, RubberBand } from './Animation'
11+
12+
13+
# Reveal
14+
15+
The Reveal component is basically an Intersection Observer and a Timeline component combined.
16+
17+
All children Tween and Timeline components get played when they are visible in viewport.
18+
19+
20+
import { Reveal, Tween } from 'react-gsap';
21+
22+
## Usage with Tween
23+
24+
Add a little space for testing
25+
26+
27+
28+
29+
30+
|
31+
32+
so scroll down
33+
34+
|
35+
36+
|
37+
38+
|
39+
40+
|
41+
42+
|
43+
44+
|
45+
46+
|
47+
48+
|
49+
50+
|
51+
52+
|
53+
54+
<Playground>
55+
<Reveal repeat>
56+
<Tween from={{ opacity: 0 }} duration={2}>
57+
<h3>This headline is fading in</h3>
58+
</Tween>
59+
</Reveal>
60+
</Playground>
61+
62+
## Fade in from left example
63+
64+
Of course you can outsource animations to separate components. In this example a FadeInLeft animation:
65+
66+
```javascript
67+
const FadeInLeft = ({ children }) => (
68+
<Tween
69+
from={{ opacity: 0, transform: 'translate3d(-100vw, 0, 0)' }}
70+
ease="back.out(1.4)"
71+
>
72+
{children}
73+
</Tween>
74+
);
75+
```
76+
77+
Also note the use of the `trigger` prop.
78+
In this case the `<H3>` wouldn't trigger because it's out of the viewport initially: `translate3d(-100vw, 0, 0)`
79+
If no trigger is passed, the first element from any child Tween or Timeline that gets beyond the threshold is used.
80+
81+
<Playground>
82+
<Reveal repeat trigger={<div />}>
83+
<FadeInLeft>
84+
<h3>This headline is coming from left</h3>
85+
</FadeInLeft>
86+
</Reveal>
87+
</Playground>
88+
89+
## forwardRef trigger
90+
91+
You also can create a forwardRef component as trigger if you need a more complex wrapper or trigger.
92+
93+
```javascript
94+
export const AnimationTrigger = React.forwardRef<HTMLDivElement>(({ children }, ref) => (
95+
<div
96+
ref={ref}
97+
style={{
98+
paddingTop: '200px',
99+
paddingBottom: '200px',
100+
background: '#f0f0f0',
101+
textAlign: 'center',
102+
}}
103+
>
104+
{children}
105+
</div>
106+
));
107+
108+
```
109+
110+
<Playground>
111+
<Reveal repeat threshold={1} trigger={<AnimationTrigger />}>
112+
<RubberBand>
113+
<h2>I get triggered later</h2>
114+
</RubberBand>
115+
</Reveal>
116+
</Playground>
117+
118+
## Props
119+
120+
| Name | Type | Default | Description |
121+
| :-- | :-- | :-- | :-- |
122+
| children | React.ReactNode | | |
123+
| trigger? | React.ReactElement \| null | | This element triggers the animation if it gets into view. Needs to be a "refable" element like HTML element or forwardRef component |
124+
| repeat? | boolean | false | Trigger the animation again and again? |
125+
| root? | Element \| null | null | |
126+
| rootMargin? | string | 0px | |
127+
| threshold? | number | 0.66 | As opposed to the threshold value from IntersectionObserver options this is just a single number |
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
name: SplitChars
3+
menu: Components
4+
---
5+
6+
import { Fragment } from 'react';
7+
import { Playground, Props } from 'docz'
8+
import { Controls, PlayState, SplitChars } from './../../../react-gsap/src/'
9+
import { Tween } from './Tween'
10+
11+
# SplitChars
12+
13+
The SplitChars component is a small helper that splits a text by chars and returns one component per char.
14+
15+
import { Controls, PlayState, Tween, SplitChars } from 'react-gsap';
16+
17+
## Usage with Tween
18+
19+
<Playground>
20+
<Controls playState={PlayState.stop}>
21+
<Tween from={{ x: '200px' }} stagger={0.1}>
22+
<SplitChars wrapper={<div style={{ display: 'inline-block', fontSize: '40px' }} />}>
23+
This text gets splitted by chars.
24+
</SplitChars>
25+
</Tween>
26+
</Controls>
27+
</Playground>
28+

0 commit comments

Comments
 (0)