@matteodicristofalo/text-animations 1.1.2 → 2.0.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 +27 -25
- package/dist/components/index.d.ts +2 -2
- package/dist/components/text-reveal/text-reveal.d.ts +24 -0
- package/dist/components/{split-text-reveal/split-text-reveal.stories.d.ts → text-reveal/text-reveal.stories.d.ts} +15 -5
- package/dist/components/text-rotate/text-rotate.d.ts +20 -0
- package/dist/components/{split-text-rotate/split-text-rotate.stories.d.ts → text-rotate/text-rotate.stories.d.ts} +10 -5
- package/dist/hooks/index.d.ts +2 -1
- package/dist/hooks/use-is-hydrated.d.ts +1 -0
- package/dist/index.css +14 -28
- package/dist/index.js +113 -77
- package/dist/utils/jsx.d.ts +10 -0
- package/dist/utils/types.d.ts +5 -0
- package/package.json +17 -4
- package/dist/components/split-text-reveal/split-test-reveal.types.d.ts +0 -14
- package/dist/components/split-text-reveal/split-text-reveal.d.ts +0 -3
- package/dist/components/split-text-rotate/split-text-rotate.d.ts +0 -3
- package/dist/components/split-text-rotate/split-text-rotate.types.d.ts +0 -9
- /package/dist/hooks/{intersection-overser/intersection-observer.d.ts → use-intersection-observer.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -42,22 +42,27 @@ export default function RootLayout({
|
|
|
42
42
|
|
|
43
43
|
Once imported the CSS you can use the React components.
|
|
44
44
|
|
|
45
|
-
##
|
|
45
|
+
## textReveal
|
|
46
46
|
|
|
47
47
|
```js
|
|
48
48
|
"use client";
|
|
49
49
|
|
|
50
|
-
import {
|
|
50
|
+
import { textReveal } from "@matteodicristofalo/text-animations";
|
|
51
51
|
|
|
52
52
|
export function MyComponent() {
|
|
53
53
|
return (
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
54
|
+
<>
|
|
55
|
+
<textReveal.h1 text="Hello World" />
|
|
56
|
+
<textReveal.h2 text="Hello World" />
|
|
57
|
+
<textReveal.p text="Hello World" />
|
|
58
|
+
<textReveal.span text="Hello World" />
|
|
59
|
+
</>
|
|
57
60
|
);
|
|
58
61
|
}
|
|
59
62
|
```
|
|
60
63
|
|
|
64
|
+
If you're using **Next.JS** make sure the component importing `<textReveal>` is marked as **Client Component** via the `"use client"` directive otherwise it won't compile.
|
|
65
|
+
|
|
61
66
|
You can decide how to split the text between one of this options:
|
|
62
67
|
|
|
63
68
|
- char (default)
|
|
@@ -65,7 +70,7 @@ You can decide how to split the text between one of this options:
|
|
|
65
70
|
- sentence
|
|
66
71
|
|
|
67
72
|
```js
|
|
68
|
-
<
|
|
73
|
+
<textReveal.h1 text="Hello World" splitType="word" />
|
|
69
74
|
```
|
|
70
75
|
|
|
71
76
|
You can also configure the reveal animation by specifying:
|
|
@@ -79,10 +84,10 @@ You can also configure the reveal animation by specifying:
|
|
|
79
84
|
"use client";
|
|
80
85
|
|
|
81
86
|
import { useMemo } from "react";
|
|
82
|
-
import {
|
|
87
|
+
import { textReveal } from "@matteodicristofalo/text-animations";
|
|
83
88
|
|
|
84
89
|
export function MyComponent() {
|
|
85
|
-
const
|
|
90
|
+
const animation = useMemo(
|
|
86
91
|
() => ({
|
|
87
92
|
duration: 1,
|
|
88
93
|
stagger: 0.5,
|
|
@@ -92,40 +97,41 @@ export function MyComponent() {
|
|
|
92
97
|
[]
|
|
93
98
|
);
|
|
94
99
|
|
|
95
|
-
return
|
|
96
|
-
<h1>
|
|
97
|
-
<SplitTextReveal text="Hello World" revealOptions={revealOptions} />
|
|
98
|
-
</h1>
|
|
99
|
-
);
|
|
100
|
+
return <textReveal.h1 text="Hello World" animation={animation} />;
|
|
100
101
|
}
|
|
101
102
|
```
|
|
102
103
|
|
|
103
|
-
##
|
|
104
|
+
## textRotate
|
|
104
105
|
|
|
105
106
|
```js
|
|
106
107
|
"use client";
|
|
107
108
|
|
|
108
|
-
import {
|
|
109
|
+
import { textRotate } from "@matteodicristofalo/text-animations";
|
|
109
110
|
|
|
110
111
|
export function MyComponent() {
|
|
111
112
|
return (
|
|
112
|
-
|
|
113
|
-
<
|
|
114
|
-
|
|
113
|
+
<>
|
|
114
|
+
<textRotate.h1 text="Hello World" />
|
|
115
|
+
<textRotate.h1 text="Hello World" />
|
|
116
|
+
<textRotate.p text="Hello World" />
|
|
117
|
+
<textRotate.span text="Hello World" />
|
|
118
|
+
</>
|
|
115
119
|
);
|
|
116
120
|
}
|
|
117
121
|
```
|
|
118
122
|
|
|
123
|
+
As well as `<textReveal>`, If you're using **Next.JS** make sure the component importing `<textRotate>` is marked as **Client Component** via the `"use client"`.
|
|
124
|
+
|
|
119
125
|
This component split always by char, the only thing you can configure is the rotate animation
|
|
120
126
|
|
|
121
127
|
```js
|
|
122
128
|
"use client";
|
|
123
129
|
|
|
124
130
|
import { useMemo } from "react";
|
|
125
|
-
import {
|
|
131
|
+
import { textRotate } from "@matteodicristofalo/text-animations";
|
|
126
132
|
|
|
127
133
|
export function MyComponent() {
|
|
128
|
-
const
|
|
134
|
+
const animation = useMemo(
|
|
129
135
|
() => ({
|
|
130
136
|
duration: 1,
|
|
131
137
|
stagger: 0.5,
|
|
@@ -133,10 +139,6 @@ export function MyComponent() {
|
|
|
133
139
|
[]
|
|
134
140
|
);
|
|
135
141
|
|
|
136
|
-
return
|
|
137
|
-
<h1>
|
|
138
|
-
<SplitTextRotate text="Hello World" rotateOptions={rotateOptions} />
|
|
139
|
-
</h1>
|
|
140
|
-
);
|
|
142
|
+
return <textRotate.h1 text="Hello World" animation={animation} />;
|
|
141
143
|
}
|
|
142
144
|
```
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * from "./text-reveal/text-reveal";
|
|
2
|
+
export * from "./text-rotate/text-rotate";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { WithTag } from "@utils/types";
|
|
3
|
+
type TextRevealProps = WithTag<{
|
|
4
|
+
text: string;
|
|
5
|
+
splitType?: "sentence" | "word" | "char";
|
|
6
|
+
animation?: Partial<{
|
|
7
|
+
duration: number;
|
|
8
|
+
delay: number;
|
|
9
|
+
stagger: number;
|
|
10
|
+
threshold: number;
|
|
11
|
+
once: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
}>;
|
|
14
|
+
declare const textReveal: {
|
|
15
|
+
h1: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
|
|
16
|
+
h2: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
|
|
17
|
+
h3: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
|
|
18
|
+
h4: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
|
|
19
|
+
h5: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
|
|
20
|
+
h6: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
|
|
21
|
+
p: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
|
|
22
|
+
span: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
|
|
23
|
+
};
|
|
24
|
+
export { textReveal };
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { StoryObj } from "@storybook/react";
|
|
3
|
-
import { SplitTextReveal } from "./split-text-reveal";
|
|
4
3
|
declare const meta: {
|
|
5
4
|
title: string;
|
|
6
|
-
component:
|
|
5
|
+
component: React.FC<import("../../utils/types").WithoutTag<{
|
|
6
|
+
text: string;
|
|
7
|
+
splitType?: "sentence" | "word" | "char";
|
|
8
|
+
animation?: Partial<{
|
|
9
|
+
duration: number;
|
|
10
|
+
delay: number;
|
|
11
|
+
stagger: number;
|
|
12
|
+
threshold: number;
|
|
13
|
+
once: boolean;
|
|
14
|
+
}>;
|
|
15
|
+
} & {
|
|
16
|
+
tag: import("../../utils/jsx").HtmlTextElementTag;
|
|
17
|
+
}>>;
|
|
7
18
|
argTypes: {
|
|
8
19
|
text: {
|
|
9
20
|
control: "text";
|
|
@@ -12,7 +23,7 @@ declare const meta: {
|
|
|
12
23
|
control: "radio";
|
|
13
24
|
options: string[];
|
|
14
25
|
};
|
|
15
|
-
|
|
26
|
+
animation: {
|
|
16
27
|
control: {
|
|
17
28
|
type: "object";
|
|
18
29
|
};
|
|
@@ -22,7 +33,6 @@ declare const meta: {
|
|
|
22
33
|
text: string;
|
|
23
34
|
splitType: "char";
|
|
24
35
|
};
|
|
25
|
-
render: (args: import("./split-test-reveal.types").SplitTextRevealProps) => React.JSX.Element;
|
|
26
36
|
};
|
|
27
37
|
export default meta;
|
|
28
38
|
type Story = StoryObj<typeof meta>;
|
|
@@ -33,4 +43,4 @@ export declare const Duration: Story;
|
|
|
33
43
|
export declare const Delay: Story;
|
|
34
44
|
export declare const Stagger: Story;
|
|
35
45
|
export declare const Threshold: Story;
|
|
36
|
-
export declare const
|
|
46
|
+
export declare const MultipleTimes: Story;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { WithTag } from "@utils/types";
|
|
3
|
+
type TextRotateProps = WithTag<{
|
|
4
|
+
text: string;
|
|
5
|
+
animation?: Partial<{
|
|
6
|
+
duration: number;
|
|
7
|
+
stagger: number;
|
|
8
|
+
}>;
|
|
9
|
+
}>;
|
|
10
|
+
declare const textRotate: {
|
|
11
|
+
h1: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
|
|
12
|
+
h2: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
|
|
13
|
+
h3: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
|
|
14
|
+
h4: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
|
|
15
|
+
h5: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
|
|
16
|
+
h6: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
|
|
17
|
+
p: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
|
|
18
|
+
span: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
|
|
19
|
+
};
|
|
20
|
+
export { textRotate };
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import type { StoryObj } from "@storybook/react";
|
|
3
|
-
import { SplitTextRotate } from "./split-text-rotate";
|
|
4
2
|
declare const meta: {
|
|
5
3
|
title: string;
|
|
6
|
-
component:
|
|
4
|
+
component: import("react").FC<import("../../utils/types").WithoutTag<{
|
|
5
|
+
text: string;
|
|
6
|
+
animation?: Partial<{
|
|
7
|
+
duration: number;
|
|
8
|
+
stagger: number;
|
|
9
|
+
}>;
|
|
10
|
+
} & {
|
|
11
|
+
tag: import("../../utils/jsx").HtmlTextElementTag;
|
|
12
|
+
}>>;
|
|
7
13
|
argTypes: {
|
|
8
14
|
text: {
|
|
9
15
|
control: "text";
|
|
10
16
|
};
|
|
11
|
-
|
|
17
|
+
animation: {
|
|
12
18
|
control: {
|
|
13
19
|
type: "object";
|
|
14
20
|
};
|
|
@@ -17,7 +23,6 @@ declare const meta: {
|
|
|
17
23
|
args: {
|
|
18
24
|
text: string;
|
|
19
25
|
};
|
|
20
|
-
render: (args: import("./split-text-rotate.types").SplitTextRotateProps) => React.JSX.Element;
|
|
21
26
|
};
|
|
22
27
|
export default meta;
|
|
23
28
|
type Story = StoryObj<typeof meta>;
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { useIntersectionObserver } from "./
|
|
1
|
+
export { useIntersectionObserver } from "./use-intersection-observer";
|
|
2
|
+
export { useIsHydrated } from "./use-is-hydrated";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useIsHydrated(): boolean;
|
package/dist/index.css
CHANGED
|
@@ -1,60 +1,46 @@
|
|
|
1
|
-
.
|
|
1
|
+
.text-reveal-module_text__YrhhT {
|
|
2
2
|
position: relative;
|
|
3
3
|
}
|
|
4
|
-
.
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
.split-text-reveal-module_text__-9O5o .split-text-reveal-module_sr-only__luZuY {
|
|
8
|
-
height: 0;
|
|
9
|
-
overflow: hidden;
|
|
10
|
-
position: absolute;
|
|
11
|
-
width: 0;
|
|
12
|
-
}
|
|
13
|
-
.split-text-reveal-module_text__-9O5o .split-text-reveal-module_container__mkD-P {
|
|
14
|
-
display: inline-flex;
|
|
4
|
+
.text-reveal-module_text__YrhhT .text-reveal-module_container__HD1Pu {
|
|
5
|
+
display: inline-block;
|
|
15
6
|
overflow: hidden;
|
|
16
7
|
}
|
|
17
|
-
.
|
|
18
|
-
|
|
8
|
+
.text-reveal-module_text__YrhhT .text-reveal-module_container__HD1Pu span {
|
|
9
|
+
display: inline-block;
|
|
19
10
|
transform: translateY(100%);
|
|
20
11
|
transition-delay: calc(var(--var-delay) + var(--var-stagger));
|
|
21
12
|
transition-duration: var(--var-duration);
|
|
22
13
|
transition-property: transform;
|
|
23
14
|
transition-timing-function: ease-out;
|
|
15
|
+
white-space: pre;
|
|
24
16
|
will-change: transform;
|
|
25
17
|
}
|
|
26
|
-
.
|
|
18
|
+
.text-reveal-module_text__YrhhT.text-reveal-module_animate__kbOPX .text-reveal-module_container__HD1Pu span {
|
|
27
19
|
transform: translateY(0);
|
|
28
20
|
}
|
|
29
|
-
.
|
|
21
|
+
.text-rotate-module_text__WKSY- {
|
|
30
22
|
display: block;
|
|
31
23
|
overflow: hidden;
|
|
32
24
|
position: relative;
|
|
33
25
|
}
|
|
34
|
-
.
|
|
35
|
-
|
|
36
|
-
overflow: hidden;
|
|
37
|
-
position: absolute;
|
|
38
|
-
width: 0;
|
|
39
|
-
}
|
|
40
|
-
.split-text-rotate-module_text__xzran .split-text-rotate-module_container__EAJkE {
|
|
41
|
-
display: flex;
|
|
26
|
+
.text-rotate-module_text__WKSY- .text-rotate-module_container__2qsrm {
|
|
27
|
+
display: block;
|
|
42
28
|
}
|
|
43
|
-
.
|
|
29
|
+
.text-rotate-module_text__WKSY- .text-rotate-module_container__2qsrm:nth-of-type(2) {
|
|
44
30
|
position: absolute;
|
|
45
31
|
top: 0;
|
|
46
32
|
transform: translateY(100%);
|
|
47
33
|
}
|
|
48
|
-
.
|
|
34
|
+
.text-rotate-module_text__WKSY- .text-rotate-module_container__2qsrm .text-rotate-module_char__LG3ps {
|
|
49
35
|
display: inline-block;
|
|
50
|
-
min-width: 0.25em;
|
|
51
36
|
transition-delay: var(--var-delay-out);
|
|
52
37
|
transition-duration: var(--var-duration);
|
|
53
38
|
transition-property: transform;
|
|
54
39
|
transition-timing-function: ease-out;
|
|
40
|
+
white-space: pre;
|
|
55
41
|
will-change: transform;
|
|
56
42
|
}
|
|
57
|
-
.
|
|
43
|
+
.text-rotate-module_text__WKSY-:hover .text-rotate-module_container__2qsrm .text-rotate-module_char__LG3ps {
|
|
58
44
|
transform: translateY(-100%);
|
|
59
45
|
transition-delay: var(--var-delay-in);
|
|
60
46
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,27 +1,5 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
|
|
3
2
|
|
|
4
|
-
function round(num, digits) {
|
|
5
|
-
return parseFloat(num.toFixed(digits));
|
|
6
|
-
}
|
|
7
|
-
function clamp(n, min, max) {
|
|
8
|
-
return Math.min(Math.max(min, n), max);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function sentences(text) {
|
|
12
|
-
return segment(text, "sentence");
|
|
13
|
-
}
|
|
14
|
-
function words(text) {
|
|
15
|
-
return segment(text, "word");
|
|
16
|
-
}
|
|
17
|
-
function characters(text) {
|
|
18
|
-
return segment(text, "grapheme");
|
|
19
|
-
}
|
|
20
|
-
function segment(text, granularity) {
|
|
21
|
-
var segmenter = new Intl.Segmenter(undefined, { granularity: granularity });
|
|
22
|
-
return Array.from(segmenter.segment(text), function (segment) { return segment.segment; });
|
|
23
|
-
}
|
|
24
|
-
|
|
25
3
|
/******************************************************************************
|
|
26
4
|
Copyright (c) Microsoft Corporation.
|
|
27
5
|
|
|
@@ -55,6 +33,27 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
55
33
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
56
34
|
};
|
|
57
35
|
|
|
36
|
+
function round(num, digits) {
|
|
37
|
+
return parseFloat(num.toFixed(digits));
|
|
38
|
+
}
|
|
39
|
+
function clamp(n, min, max) {
|
|
40
|
+
return Math.min(Math.max(min, n), max);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function sentences(text) {
|
|
44
|
+
return segment(text, "sentence");
|
|
45
|
+
}
|
|
46
|
+
function words(text) {
|
|
47
|
+
return segment(text, "word");
|
|
48
|
+
}
|
|
49
|
+
function characters(text) {
|
|
50
|
+
return segment(text, "grapheme");
|
|
51
|
+
}
|
|
52
|
+
function segment(text, granularity) {
|
|
53
|
+
var segmenter = new Intl.Segmenter(undefined, { granularity: granularity });
|
|
54
|
+
return Array.from(segmenter.segment(text), function (segment) { return segment.segment; });
|
|
55
|
+
}
|
|
56
|
+
|
|
58
57
|
function useIntersectionObserver(ref, options) {
|
|
59
58
|
var _a = useState(false), isIntersecting = _a[0], setIntersecting = _a[1];
|
|
60
59
|
useEffect(function () {
|
|
@@ -78,34 +77,69 @@ function useIntersectionObserver(ref, options) {
|
|
|
78
77
|
return isIntersecting;
|
|
79
78
|
}
|
|
80
79
|
|
|
80
|
+
function useIsHydrated() {
|
|
81
|
+
var _a = useState(false), isHydrated = _a[0], setIsHydrated = _a[1];
|
|
82
|
+
useEffect(function () {
|
|
83
|
+
setIsHydrated(true);
|
|
84
|
+
}, []);
|
|
85
|
+
return isHydrated;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
var htmlTextElementTags = [
|
|
89
|
+
"h1",
|
|
90
|
+
"h2",
|
|
91
|
+
"h3",
|
|
92
|
+
"h4",
|
|
93
|
+
"h5",
|
|
94
|
+
"h6",
|
|
95
|
+
"p",
|
|
96
|
+
"span",
|
|
97
|
+
];
|
|
98
|
+
function generateJsxVariations(Component) {
|
|
99
|
+
return htmlTextElementTags.reduce(function (variations, tag) {
|
|
100
|
+
variations[tag] = function (props) { return (React.createElement(Component, __assign({}, props, { tag: tag }))); };
|
|
101
|
+
return variations;
|
|
102
|
+
}, {});
|
|
103
|
+
}
|
|
104
|
+
|
|
81
105
|
function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
|
|
82
106
|
|
|
83
|
-
var styles$1 = {"text":"
|
|
107
|
+
var styles$1 = {"text":"text-reveal-module_text__YrhhT","container":"text-reveal-module_container__HD1Pu","animate":"text-reveal-module_animate__kbOPX"};
|
|
84
108
|
|
|
85
109
|
var DEFAULT_DURATION$1 = 0.5;
|
|
86
110
|
var DEFAULT_STAGGER$1 = 0.01;
|
|
87
111
|
var MIN_THRESHOLD = 0;
|
|
88
112
|
var MAX_THRESHOLD = 1;
|
|
89
113
|
var DEFAULT_THRESHOLD = 0.25;
|
|
90
|
-
function
|
|
114
|
+
function TextReveal(props) {
|
|
115
|
+
var isHydrated = useIsHydrated();
|
|
116
|
+
if (isHydrated === false) {
|
|
117
|
+
return React.createElement(TextRevealServer, __assign({}, props));
|
|
118
|
+
}
|
|
119
|
+
return React.createElement(TextRevealClient, __assign({}, props));
|
|
120
|
+
}
|
|
121
|
+
function TextRevealServer(_a) {
|
|
122
|
+
var Tag = _a.tag, text = _a.text;
|
|
123
|
+
return React.createElement(Tag, { style: { opacity: 0 } }, text);
|
|
124
|
+
}
|
|
125
|
+
function TextRevealClient(_a) {
|
|
91
126
|
var _b;
|
|
92
|
-
var text = _a.text, _c = _a.splitType, splitType = _c === void 0 ? "char" : _c,
|
|
127
|
+
var Tag = _a.tag, text = _a.text, _c = _a.splitType, splitType = _c === void 0 ? "char" : _c, animation = _a.animation;
|
|
93
128
|
var ref = useRef(null);
|
|
94
|
-
var
|
|
95
|
-
var memoizedOptions = useMemo(function () {
|
|
129
|
+
var intersectionObserverOptions = useMemo(function () {
|
|
96
130
|
var _a;
|
|
97
|
-
var once = (_a =
|
|
98
|
-
var threshold = (
|
|
99
|
-
? clamp(
|
|
131
|
+
var once = (_a = animation === null || animation === void 0 ? void 0 : animation.once) !== null && _a !== void 0 ? _a : true;
|
|
132
|
+
var threshold = (animation === null || animation === void 0 ? void 0 : animation.threshold)
|
|
133
|
+
? clamp(animation.threshold, MIN_THRESHOLD, MAX_THRESHOLD)
|
|
100
134
|
: DEFAULT_THRESHOLD;
|
|
101
135
|
return {
|
|
102
136
|
once: once,
|
|
103
137
|
threshold: threshold,
|
|
104
138
|
};
|
|
105
|
-
}, [
|
|
106
|
-
var isInView = useIntersectionObserver(ref,
|
|
107
|
-
var
|
|
108
|
-
var
|
|
139
|
+
}, [animation]);
|
|
140
|
+
var isInView = useIntersectionObserver(ref, intersectionObserverOptions);
|
|
141
|
+
var duration = (animation === null || animation === void 0 ? void 0 : animation.duration) || DEFAULT_DURATION$1;
|
|
142
|
+
var stagger = (animation === null || animation === void 0 ? void 0 : animation.stagger) || DEFAULT_STAGGER$1;
|
|
109
143
|
var splittedText = useMemo(function () {
|
|
110
144
|
switch (splitType) {
|
|
111
145
|
case "sentence":
|
|
@@ -116,56 +150,58 @@ function SplitTextReveal(_a) {
|
|
|
116
150
|
return characters(text);
|
|
117
151
|
}
|
|
118
152
|
}, [splitType, text]);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
React.createElement("span", { "aria-hidden": "true", style: {
|
|
130
|
-
"--var-delay": "".concat((_a = revealOptions === null || revealOptions === void 0 ? void 0 : revealOptions.delay) !== null && _a !== void 0 ? _a : 0, "s"),
|
|
131
|
-
"--var-stagger": "".concat(round(transitionStagger * i, 3), "s"),
|
|
132
|
-
} }, el)));
|
|
133
|
-
}))) : (React.createElement("span", { className: styles$1["is-hidden"] }, text))));
|
|
153
|
+
return (React.createElement(Tag, { ref: ref, className: clsx(styles$1["text"], (_b = {}, _b[styles$1["animate"]] = isInView, _b)), style: {
|
|
154
|
+
"--var-duration": "".concat(duration, "s"),
|
|
155
|
+
}, "aria-label": text }, splittedText.map(function (el, i) {
|
|
156
|
+
var _a;
|
|
157
|
+
return (React.createElement("span", { key: i, className: styles$1["container"], "aria-hidden": "true" },
|
|
158
|
+
React.createElement("span", { "aria-hidden": "true", style: {
|
|
159
|
+
"--var-delay": "".concat((_a = animation === null || animation === void 0 ? void 0 : animation.delay) !== null && _a !== void 0 ? _a : 0, "s"),
|
|
160
|
+
"--var-stagger": "".concat(round(stagger * i, 3), "s"),
|
|
161
|
+
} }, el)));
|
|
162
|
+
})));
|
|
134
163
|
}
|
|
164
|
+
var textReveal = generateJsxVariations(TextReveal);
|
|
135
165
|
|
|
136
|
-
var styles = {"text":"
|
|
166
|
+
var styles = {"text":"text-rotate-module_text__WKSY-","container":"text-rotate-module_container__2qsrm","char":"text-rotate-module_char__LG3ps"};
|
|
137
167
|
|
|
168
|
+
var NUMBER_OF_COPIES = 2;
|
|
138
169
|
var DEFAULT_DURATION = 0.25;
|
|
139
170
|
var DEFAULT_STAGGER = 0.025;
|
|
140
|
-
function
|
|
141
|
-
var
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
171
|
+
function TextRotate(props) {
|
|
172
|
+
var isHydrated = useIsHydrated();
|
|
173
|
+
if (isHydrated === false) {
|
|
174
|
+
return React.createElement(TextRotateServer, __assign({}, props));
|
|
175
|
+
}
|
|
176
|
+
return React.createElement(TextRotateClient, __assign({}, props));
|
|
177
|
+
}
|
|
178
|
+
function TextRotateServer(_a) {
|
|
179
|
+
var Tag = _a.tag, text = _a.text;
|
|
180
|
+
return React.createElement(Tag, null, text);
|
|
181
|
+
}
|
|
182
|
+
function TextRotateClient(_a) {
|
|
183
|
+
var Tag = _a.tag, text = _a.text, animation = _a.animation;
|
|
184
|
+
var duration = (animation === null || animation === void 0 ? void 0 : animation.duration) || DEFAULT_DURATION;
|
|
185
|
+
var stagger = (animation === null || animation === void 0 ? void 0 : animation.stagger) || DEFAULT_STAGGER;
|
|
186
|
+
var chars = useMemo(function () { return characters(text); }, [text]);
|
|
148
187
|
var delays = useCallback(function (index) {
|
|
149
|
-
var
|
|
188
|
+
var precision = 3;
|
|
189
|
+
var noElementsZeroBased = chars.length - 1;
|
|
150
190
|
return {
|
|
151
|
-
in: round(
|
|
152
|
-
out: round(
|
|
191
|
+
in: round(stagger * index, precision),
|
|
192
|
+
out: round(stagger * (noElementsZeroBased - index), precision),
|
|
153
193
|
};
|
|
154
|
-
}, [
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return (React.createElement("span", { key: i, className: styles["char"], "aria-hidden": "true", style: {
|
|
165
|
-
"--var-delay-in": "".concat(charDelays.in, "s"),
|
|
166
|
-
"--var-delay-out": "".concat(charDelays.out, "s"),
|
|
167
|
-
} }, char));
|
|
168
|
-
}))); }))) : (React.createElement("span", null, text))));
|
|
194
|
+
}, [chars, stagger]);
|
|
195
|
+
return (React.createElement(Tag, { className: styles["text"], style: {
|
|
196
|
+
"--var-duration": "".concat(duration, "s"),
|
|
197
|
+
}, "aria-label": text }, Array.from({ length: NUMBER_OF_COPIES }).map(function (_, i) { return (React.createElement("span", { key: i, className: styles["container"], "aria-hidden": "true" }, chars.map(function (char, i) {
|
|
198
|
+
var charDelays = delays(i);
|
|
199
|
+
return (React.createElement("span", { key: i, className: styles["char"], "aria-hidden": "true", style: {
|
|
200
|
+
"--var-delay-in": "".concat(charDelays.in, "s"),
|
|
201
|
+
"--var-delay-out": "".concat(charDelays.out, "s"),
|
|
202
|
+
} }, char));
|
|
203
|
+
}))); })));
|
|
169
204
|
}
|
|
205
|
+
var textRotate = generateJsxVariations(TextRotate);
|
|
170
206
|
|
|
171
|
-
export {
|
|
207
|
+
export { textReveal, textRotate };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { WithoutTag } from "./types";
|
|
3
|
+
declare const htmlTextElementTags: readonly ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span"];
|
|
4
|
+
export type HtmlTextElementTag = (typeof htmlTextElementTags)[number];
|
|
5
|
+
export declare function generateJsxVariations<Props extends {
|
|
6
|
+
tag: HtmlTextElementTag;
|
|
7
|
+
}>(Component: React.FC<Props>): {
|
|
8
|
+
[k in HtmlTextElementTag]: React.FC<WithoutTag<Props>>;
|
|
9
|
+
};
|
|
10
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matteodicristofalo/text-animations",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "React library to animate text",
|
|
3
|
+
"version": "2.0.2",
|
|
4
|
+
"description": "Lightweight React component library to animate text",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rimraf dist && rollup -c",
|
|
7
7
|
"storybook": "storybook dev -p 6006"
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
"rollup": "^4.28.0",
|
|
28
28
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
29
29
|
"rollup-plugin-postcss": "^4.0.2",
|
|
30
|
-
"rollup-preserve-directives": "^1.1.3",
|
|
31
30
|
"sass": "^1.81.0",
|
|
32
31
|
"storybook": "^8.4.6",
|
|
33
32
|
"tslib": "^2.8.1",
|
|
@@ -53,5 +52,19 @@
|
|
|
53
52
|
"repository": {
|
|
54
53
|
"type": "git",
|
|
55
54
|
"url": "git@github.com:matteodicristofalo/text-animations.git"
|
|
56
|
-
}
|
|
55
|
+
},
|
|
56
|
+
"keywords": [
|
|
57
|
+
"react",
|
|
58
|
+
"react-components",
|
|
59
|
+
"ui-library",
|
|
60
|
+
"animation",
|
|
61
|
+
"animated-text",
|
|
62
|
+
"text-animation",
|
|
63
|
+
"text-reveal",
|
|
64
|
+
"hover",
|
|
65
|
+
"hover-effects",
|
|
66
|
+
"scroll-animation",
|
|
67
|
+
"typography",
|
|
68
|
+
"microinteractions"
|
|
69
|
+
]
|
|
57
70
|
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export type SplitTextRevealProps = {
|
|
2
|
-
text: string;
|
|
3
|
-
splitType?: SplitType;
|
|
4
|
-
revealOptions?: Partial<RevealOptions>;
|
|
5
|
-
};
|
|
6
|
-
type SplitType = "sentence" | "word" | "char";
|
|
7
|
-
type RevealOptions = {
|
|
8
|
-
duration: number;
|
|
9
|
-
delay: number;
|
|
10
|
-
stagger: number;
|
|
11
|
-
threshold: number;
|
|
12
|
-
once: boolean;
|
|
13
|
-
};
|
|
14
|
-
export {};
|
|
File without changes
|