react-text-swap-animation 0.2.0 → 1.4.0
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 +26 -40
- package/dist/components/TextSwap.js +11 -9
- package/dist/components/constants.js +15 -11
- package/dist/components/index.js +3 -11
- package/dist/components/index.scss +1 -2
- package/dist/components/useFonts.js +29 -0
- package/package.json +4 -5
- package/public/index.html +19 -20
- package/src/index.js +17 -4
- package/src/lib/components/TextSwap.jsx +7 -6
- package/src/lib/components/constants.js +14 -10
- package/src/lib/components/index.jsx +3 -12
- package/src/lib/components/index.scss +1 -2
- package/src/lib/components/useFonts.js +22 -0
package/README.md
CHANGED
|
@@ -45,22 +45,19 @@ To control the animation speed and timing, you can pass an object of `animationO
|
|
|
45
45
|
randomReverseMax: 6000,
|
|
46
46
|
loopAnimation: 20000,
|
|
47
47
|
waitToStart: 5000,
|
|
48
|
+
transitionDuration: 2000,
|
|
49
|
+
timingFunction: 'ease-in-out',
|
|
48
50
|
}} />
|
|
49
51
|
```
|
|
50
52
|
|
|
51
53
|
If you are using an embedded font and need to wait for it to load before animating,
|
|
52
|
-
then you should specify the `fontToObserve`
|
|
54
|
+
then you should specify the `fontToObserve` property with the font family name.
|
|
53
55
|
|
|
54
56
|
```js
|
|
55
|
-
<TextSwap fontToObserve=
|
|
57
|
+
<TextSwap fontToObserve="Open Sans" />
|
|
56
58
|
```
|
|
57
59
|
```js
|
|
58
|
-
<TextSwap fontToObserve=
|
|
59
|
-
family: 'Roboto',
|
|
60
|
-
weight: 600,
|
|
61
|
-
style: 'italic',
|
|
62
|
-
stretch: 'expanded',
|
|
63
|
-
}} />
|
|
60
|
+
<TextSwap fontToObserve="Roboto" />
|
|
64
61
|
```
|
|
65
62
|
|
|
66
63
|
API
|
|
@@ -69,43 +66,25 @@ API
|
|
|
69
66
|
### Props
|
|
70
67
|
|
|
71
68
|
| Prop | Type | Default | Description |
|
|
72
|
-
| :----------------- | :-----
|
|
69
|
+
| :----------------- | :----- |:-------------------------------------------------| :------------------------------------------------------ |
|
|
73
70
|
| `words` | array | `['Text Swap Animation', 'Antitoxin Swamp Tea']` | An array containing exactly 2 words which are an anagram of each other. |
|
|
74
71
|
| `animationOptions` | object | `AnimationOptions` | Timing options for when to start, how fast to animate forwards, backwards, and when to loop (optional). |
|
|
75
|
-
| `fontToObserve` |
|
|
72
|
+
| `fontToObserve` | string | | The name of an embedded font to wait until loaded. If not specified, animation will loaded immediately (optional). |
|
|
76
73
|
|
|
77
74
|
#### AnimationOptions
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
| :----------------- | :----- | :------ | :-------------------------------------------------------------------------------------------- |
|
|
81
|
-
| `randomStartMin` | number | `0` | The minimum amount of time to randomly wait before starting to animate each letter |
|
|
82
|
-
| `randomStartMax` | number | `3000` | The maximum amount of time to randomly wait before starting to animate each letter |
|
|
83
|
-
| `randomReverseMin` | number | `6000` | The minimum amount of time to randomly wait before starting to animate each letter in reverse |
|
|
84
|
-
| `randomReverseMax` | number | `9000` | The maximum amount of time to randomly wait before starting to animate each letter in reverse |
|
|
85
|
-
| `loopAnimation` | number | `12000` | The amount of time for each full loop of the animation |
|
|
86
|
-
| `waitToStart` | number | `0` | The amount of time to wait before beginning the animation on start up |
|
|
87
|
-
|
|
88
|
-
#### FontToObserve
|
|
89
|
-
|
|
90
|
-
This object is passed along to [Font Face Observer](https://github.com/iamskok/use-font-face-observer)
|
|
91
|
-
|
|
92
|
-
| Property | Type | Description |
|
|
93
|
-
| :---------| :--------------- | :------------------------------------------------------- |
|
|
94
|
-
| `family` | string | The font-family: `Roboto`, `Inter`, `Open Sans`, etc |
|
|
95
|
-
| `weight` | string or number | The font-weight: `normal`, `bold`, `800`, etc |
|
|
96
|
-
| `style` | string | The font-style: `normal`, `italic`, `oblique` |
|
|
97
|
-
| `stretch` | string | The font stretch: `normal`, `condensed`, `expanded`, etc |
|
|
98
|
-
|
|
99
|
-
Styling
|
|
100
|
-
----
|
|
101
|
-
|
|
102
|
-
You can use the CSS transition property to adjust the speed and duration of the animation completely. Can you find a neat transition animation? Please share! :)
|
|
76
|
+
All time values are in # of milliseconds. The randomness allows a nice jumble effect. You can use any values you want to create some fascinating animations.
|
|
103
77
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
78
|
+
| Property | Type | Default | Description |
|
|
79
|
+
| :------------------- | :----- | :------------ |:--------------------------------------------------------------------------------------------------------------------------------------|
|
|
80
|
+
| `randomStartMin` | number | `0` | The minimum amount of time to randomly wait before starting to animate each letter. |
|
|
81
|
+
| `randomStartMax` | number | `3000` | The maximum amount of time to randomly wait before starting to animate each letter. Should be `>= randomStartMin`. |
|
|
82
|
+
| `randomReverseMin` | number | `6000` | The minimum amount of time to randomly wait before starting to animate each letter in reverse. |
|
|
83
|
+
| `randomReverseMax` | number | `9000` | The maximum amount of time to randomly wait before starting to animate each letter in reverse. Should be `>= randomReverseMin`. |
|
|
84
|
+
| `loopAnimation` | number | `12000` | The amount of time to wait before starting the next full loop of the animation. Should be `>= randomReverseMax + transitionDuration`. |
|
|
85
|
+
| `waitToStart` | number | `0` | The amount of time to wait before beginning the animation on start up the first time. |
|
|
86
|
+
| `transitionDuration` | number | `1000` | How long should it take for a letter to move to its next position. Should be `<= randomReverseMin - randomStartMax`. |
|
|
87
|
+
| `timingFunction` | string | `ease-in-out` | What [timing function](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function) should be used for the animation. |
|
|
109
88
|
|
|
110
89
|
Run Locally
|
|
111
90
|
----
|
|
@@ -117,10 +96,17 @@ To run demo locally:
|
|
|
117
96
|
|
|
118
97
|
and a browser will open to the demo.
|
|
119
98
|
|
|
99
|
+
If you receive `Invalid hook call` errors because you are linking this module, you may need to point this library's React to your app's installed React so there is only one copy.
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
npm link ../my-app/node_modules/react
|
|
103
|
+
npm link ../my-app/node_modules/react-dom
|
|
104
|
+
```
|
|
105
|
+
|
|
120
106
|
Future Ideas
|
|
121
107
|
----
|
|
122
108
|
|
|
123
|
-
- Supply different animation easing.
|
|
109
|
+
- Supply different animation easing. Done!
|
|
124
110
|
|
|
125
111
|
|
|
126
112
|
License
|
|
@@ -44,7 +44,9 @@ function TextSwap(_ref) {
|
|
|
44
44
|
randomReverseMin,
|
|
45
45
|
randomReverseMax,
|
|
46
46
|
loopAnimation,
|
|
47
|
-
waitToStart
|
|
47
|
+
waitToStart,
|
|
48
|
+
transitionDuration,
|
|
49
|
+
timingFunction
|
|
48
50
|
} = animationOptions;
|
|
49
51
|
(0, _react.useEffect)(() => {
|
|
50
52
|
const swaps = [];
|
|
@@ -167,13 +169,12 @@ function TextSwap(_ref) {
|
|
|
167
169
|
setTimeout(() => {
|
|
168
170
|
animateFunc();
|
|
169
171
|
}, waitToStart);
|
|
170
|
-
}, [lettersRefs1, lettersRefs2, loopAnimation, updateAnimation, randomReverseMax, randomReverseMin, randomStartMax, randomStartMin, waitToStart, words]);
|
|
172
|
+
}, [lettersRefs1, lettersRefs2, loopAnimation, updateAnimation, randomReverseMax, randomReverseMin, randomStartMax, randomStartMin, waitToStart, transitionDuration, timingFunction, words]);
|
|
171
173
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
172
174
|
className: "text-swap"
|
|
173
175
|
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
174
176
|
className: "word word-1 hidden"
|
|
175
177
|
}, [...words[0]].map((letter, i) => {
|
|
176
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
177
178
|
return /*#__PURE__*/_react.default.createElement("span", {
|
|
178
179
|
ref: lettersRefs1.current[i],
|
|
179
180
|
className: "letter",
|
|
@@ -182,7 +183,6 @@ function TextSwap(_ref) {
|
|
|
182
183
|
})), /*#__PURE__*/_react.default.createElement("div", {
|
|
183
184
|
className: "word word-2 hidden"
|
|
184
185
|
}, [...words[1]].map((letter, i) => {
|
|
185
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
186
186
|
return /*#__PURE__*/_react.default.createElement("span", {
|
|
187
187
|
ref: lettersRefs2.current[i],
|
|
188
188
|
className: "letter",
|
|
@@ -199,17 +199,19 @@ function TextSwap(_ref) {
|
|
|
199
199
|
src,
|
|
200
200
|
dest
|
|
201
201
|
} = renderedLetter;
|
|
202
|
-
let letterStyles
|
|
202
|
+
let letterStyles = {
|
|
203
|
+
transition: "left ".concat(transitionDuration, "ms ").concat(timingFunction, ", top ").concat(transitionDuration, "ms ").concat(timingFunction)
|
|
204
|
+
};
|
|
203
205
|
if (playing) {
|
|
204
206
|
const left = "".concat(dest.rect.x, "px");
|
|
205
|
-
letterStyles = {
|
|
207
|
+
letterStyles = _objectSpread(_objectSpread({}, letterStyles), {}, {
|
|
206
208
|
left
|
|
207
|
-
};
|
|
209
|
+
});
|
|
208
210
|
} else {
|
|
209
211
|
const left = "".concat(src.rect.x, "px");
|
|
210
|
-
letterStyles = {
|
|
212
|
+
letterStyles = _objectSpread(_objectSpread({}, letterStyles), {}, {
|
|
211
213
|
left
|
|
212
|
-
};
|
|
214
|
+
});
|
|
213
215
|
}
|
|
214
216
|
if (disappear) {
|
|
215
217
|
letterStyles.opacity = 0;
|
|
@@ -8,22 +8,26 @@ const DEFAULT_WORDS = ['Text Swap Animation', 'Antitoxin Swamp Tea'];
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @typedef AnimationOptions Timing options for when to start, how fast to animate forwards, backwards, and when to loop.
|
|
11
|
-
* @property {number} randomStartMin The minimum amount of time to randomly wait before starting to animate each letter
|
|
12
|
-
* @property {number} randomStartMax The maximum amount of time to randomly wait before starting to animate each letter
|
|
13
|
-
* @property {number} randomReverseMin The minimum amount of time to randomly wait before starting to animate each letter in reverse
|
|
14
|
-
* @property {number} randomReverseMax The maximum amount of time to randomly wait before starting to animate each letter in reverse
|
|
15
|
-
* @property {number} loopAnimation The amount of time
|
|
16
|
-
* @property {number} waitToStart The amount of time to wait before beginning the animation on start up
|
|
11
|
+
* @property {number} randomStartMin The minimum amount of time to randomly wait before starting to animate each letter.
|
|
12
|
+
* @property {number} randomStartMax The maximum amount of time to randomly wait before starting to animate each letter. Should be `>= randomStartMin`.
|
|
13
|
+
* @property {number} randomReverseMin The minimum amount of time to randomly wait before starting to animate each letter in reverse.
|
|
14
|
+
* @property {number} randomReverseMax The maximum amount of time to randomly wait before starting to animate each letter in reverse. Should be `>= randomReverseMin`.
|
|
15
|
+
* @property {number} loopAnimation The amount of time to wait before starting the next full loop of the animation. Should be `>= randomReverseMax + transitionDuration`.
|
|
16
|
+
* @property {number} waitToStart The amount of time to wait before beginning the animation on start up the first time.
|
|
17
|
+
* @property {number} transitionDuration How long should it take for a letter to move to its next position. Should be `<= randomReverseMin - randomStartMax`.
|
|
18
|
+
* @property {string} timingFunction What [timing function](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function) should be used for the animation.
|
|
17
19
|
*/
|
|
18
20
|
|
|
19
21
|
/** @type AnimationOptions */
|
|
20
22
|
exports.DEFAULT_WORDS = DEFAULT_WORDS;
|
|
21
23
|
const DEFAULT_ANIMATION_OPTIONS = {
|
|
22
|
-
randomStartMin:
|
|
23
|
-
randomStartMax:
|
|
24
|
-
randomReverseMin:
|
|
25
|
-
randomReverseMax:
|
|
24
|
+
randomStartMin: 0,
|
|
25
|
+
randomStartMax: 3000,
|
|
26
|
+
randomReverseMin: 6000,
|
|
27
|
+
randomReverseMax: 9000,
|
|
26
28
|
loopAnimation: 12000,
|
|
27
|
-
waitToStart: 0
|
|
29
|
+
waitToStart: 0,
|
|
30
|
+
transitionDuration: 1000,
|
|
31
|
+
timingFunction: 'ease-in-out'
|
|
28
32
|
};
|
|
29
33
|
exports.DEFAULT_ANIMATION_OPTIONS = DEFAULT_ANIMATION_OPTIONS;
|
package/dist/components/index.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = Loader;
|
|
7
7
|
require("core-js/modules/es.symbol.description.js");
|
|
8
8
|
var _react = _interopRequireDefault(require("react"));
|
|
9
|
-
var
|
|
9
|
+
var _useFonts = _interopRequireDefault(require("./useFonts"));
|
|
10
10
|
var _TextSwap = _interopRequireDefault(require("./TextSwap"));
|
|
11
11
|
var _constants = require("./constants");
|
|
12
12
|
require("./index.css");
|
|
@@ -16,19 +16,11 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
|
|
|
16
16
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
17
17
|
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
18
18
|
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
19
|
-
/**
|
|
20
|
-
* @typedef FontToObserve A description of an embedded font to observe and wait until loaded.
|
|
21
|
-
* @property {string} [family] The font-family: Roboto, Inter, Open Sans, etc
|
|
22
|
-
* @property {string|number} [weight] The font-weight: normal, bold, 800, etc
|
|
23
|
-
* @property {string} [style] The font-style: normal, italic, oblique
|
|
24
|
-
* @property {string} [stretch] The font stretch: normal, condensed, expanded, etc
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
19
|
/**
|
|
28
20
|
* Render and animate from one word to another word and back again.
|
|
29
21
|
* @param {[string]} [words] The 2 words to animate between.
|
|
30
22
|
* @param {AnimationOptions} [animationOptions] Timing options for when to start, how fast to animate forwards, backwards, and when to loop.
|
|
31
|
-
* @param {
|
|
23
|
+
* @param {string} [fontToObserve] A description of an embedded font to observe and wait until loaded.
|
|
32
24
|
* @returns {JSX.Element|null}
|
|
33
25
|
*/
|
|
34
26
|
function Loader(_ref) {
|
|
@@ -37,8 +29,8 @@ function Loader(_ref) {
|
|
|
37
29
|
animationOptions = {},
|
|
38
30
|
fontToObserve
|
|
39
31
|
} = _ref;
|
|
32
|
+
const isFontLoaded = (0, _useFonts.default)(fontToObserve);
|
|
40
33
|
const animOptions = _objectSpread(_objectSpread({}, _constants.DEFAULT_ANIMATION_OPTIONS), animationOptions);
|
|
41
|
-
const isFontLoaded = (0, _useFontFaceObserver.default)(fontToObserve ? [fontToObserve] : []);
|
|
42
34
|
let word1 = words[0];
|
|
43
35
|
let word2 = words[1];
|
|
44
36
|
const maxLength = Math.max(word1.length, word2.length);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = useFonts;
|
|
7
|
+
require("core-js/modules/web.dom-collections.iterator.js");
|
|
8
|
+
require("core-js/modules/es.promise.js");
|
|
9
|
+
var _react = require("react");
|
|
10
|
+
function useFonts() {
|
|
11
|
+
for (var _len = arguments.length, fontNames = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
12
|
+
fontNames[_key] = arguments[_key];
|
|
13
|
+
}
|
|
14
|
+
const [isLoaded, setIsLoaded] = (0, _react.useState)(false);
|
|
15
|
+
(0, _react.useEffect)(() => {
|
|
16
|
+
// Inspired by https://stackoverflow.com/a/60138011
|
|
17
|
+
if (!document || !document.fonts) {
|
|
18
|
+
// eslint-disable-next-line no-console
|
|
19
|
+
console.warn('Browser does not support document.fonts API');
|
|
20
|
+
setIsLoaded(true);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
Promise.all(fontNames.map(fontName => document.fonts.load("16px \"".concat(fontName, "\"")))).then(() => {
|
|
24
|
+
setIsLoaded(true);
|
|
25
|
+
});
|
|
26
|
+
}, [fontNames]);
|
|
27
|
+
return isLoaded;
|
|
28
|
+
}
|
|
29
|
+
;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-text-swap-animation",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"author": "Scott Canoni",
|
|
5
5
|
"description": "A React component to use CSS animations to swap letters in 2 words. The text is animated in position after calculating initial and final positions of each letter. Words which are anagrams will animate well, but you can use any words or phrases.",
|
|
6
6
|
"license": "WTFPL",
|
|
@@ -29,12 +29,11 @@
|
|
|
29
29
|
"rearrange"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"core-js": "^3.18.3"
|
|
33
|
-
"use-font-face-observer": "^1.1.39"
|
|
32
|
+
"core-js": "^3.18.3"
|
|
34
33
|
},
|
|
35
34
|
"peerDependencies": {
|
|
36
|
-
"react": "^17.0.2",
|
|
37
|
-
"react-dom": "^17.0.2"
|
|
35
|
+
"react": "^17.0.2 || ^18.2.0",
|
|
36
|
+
"react-dom": "^17.0.2 || ^18.2.0"
|
|
38
37
|
},
|
|
39
38
|
"devDependencies": {
|
|
40
39
|
"@babel/cli": "^7.20.7",
|
package/public/index.html
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
-
<meta name="theme-color" content="#000000" />
|
|
8
|
-
<meta name="description"
|
|
9
|
-
content="A React component to use CSS animations to swap letters in 2 words. The text is animated in position after calculating initial and final positions of each letter. Words which are anagrams will animate well, but you can use any words or phrases." />
|
|
10
|
-
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
<link rel="preconnect" href="https://fonts.
|
|
14
|
-
<link
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
</
|
|
20
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<meta name="theme-color" content="#000000" />
|
|
8
|
+
<meta name="description"
|
|
9
|
+
content="A React component to use CSS animations to swap letters in 2 words. The text is animated in position after calculating initial and final positions of each letter. Words which are anagrams will animate well, but you can use any words or phrases." />
|
|
10
|
+
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
11
|
+
<title>React Text Swap Animation</title>
|
|
12
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
13
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
14
|
+
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
<div id="root"></div>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
package/src/index.js
CHANGED
|
@@ -9,22 +9,35 @@ ReactDOM.render(
|
|
|
9
9
|
<h1>React Text Swap Animation</h1>
|
|
10
10
|
<h2>Demo</h2>
|
|
11
11
|
|
|
12
|
-
<TextSwap fontToObserve=
|
|
12
|
+
<TextSwap fontToObserve="Open Sans" />
|
|
13
13
|
|
|
14
14
|
<br />
|
|
15
15
|
<br />
|
|
16
16
|
|
|
17
|
-
<TextSwap fontToObserve=
|
|
17
|
+
<TextSwap fontToObserve="Open Sans" words={['a witty saying', 'proves nothing']} />
|
|
18
18
|
|
|
19
19
|
<br />
|
|
20
20
|
<br />
|
|
21
21
|
|
|
22
|
-
<TextSwap fontToObserve=
|
|
22
|
+
<TextSwap fontToObserve="Open Sans" words={['don\'t be sad it\'s over', 'be happy that it happened']} />
|
|
23
23
|
|
|
24
24
|
<br />
|
|
25
25
|
<br />
|
|
26
26
|
|
|
27
|
-
<TextSwap fontToObserve=
|
|
27
|
+
<TextSwap fontToObserve="Open Sans" words={['debit card', 'bad credit']} />
|
|
28
|
+
<br />
|
|
29
|
+
<br />
|
|
30
|
+
|
|
31
|
+
<TextSwap fontToObserve="Open Sans" words={['debit card', 'bad credit']} animationOptions={{
|
|
32
|
+
randomStartMin: 0,
|
|
33
|
+
randomStartMax: 3000,
|
|
34
|
+
randomReverseMin: 12000,
|
|
35
|
+
randomReverseMax: 12000,
|
|
36
|
+
loopAnimation: 20000,
|
|
37
|
+
waitToStart: 0,
|
|
38
|
+
transitionDuration: 4000,
|
|
39
|
+
timingFunction: 'cubic-bezier(0.2,-2,0.8,2)'
|
|
40
|
+
}} />
|
|
28
41
|
</div>
|
|
29
42
|
</React.StrictMode>,
|
|
30
43
|
document.getElementById('root'),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable react/no-array-index-key */
|
|
1
2
|
import React, { createRef, useCallback, useEffect, useRef, useState } from 'react';
|
|
2
3
|
import { randomMinMax, uuidv4 } from '../utils';
|
|
3
4
|
|
|
@@ -33,6 +34,8 @@ export default function TextSwap({ words, animationOptions }) {
|
|
|
33
34
|
randomReverseMax,
|
|
34
35
|
loopAnimation,
|
|
35
36
|
waitToStart,
|
|
37
|
+
transitionDuration,
|
|
38
|
+
timingFunction,
|
|
36
39
|
} = animationOptions;
|
|
37
40
|
|
|
38
41
|
useEffect(() => {
|
|
@@ -148,14 +151,13 @@ export default function TextSwap({ words, animationOptions }) {
|
|
|
148
151
|
animateFunc();
|
|
149
152
|
}, waitToStart);
|
|
150
153
|
|
|
151
|
-
}, [lettersRefs1, lettersRefs2, loopAnimation, updateAnimation, randomReverseMax, randomReverseMin, randomStartMax, randomStartMin, waitToStart, words]);
|
|
154
|
+
}, [lettersRefs1, lettersRefs2, loopAnimation, updateAnimation, randomReverseMax, randomReverseMin, randomStartMax, randomStartMin, waitToStart, transitionDuration, timingFunction, words]);
|
|
152
155
|
|
|
153
156
|
return (
|
|
154
157
|
<div className="text-swap">
|
|
155
158
|
<div className="word word-1 hidden">
|
|
156
159
|
{
|
|
157
160
|
[...words[0]].map((letter, i) => {
|
|
158
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
159
161
|
return <span ref={lettersRefs1.current[i]} className="letter" key={`${i}${letter}`}>{letter}</span>;
|
|
160
162
|
})
|
|
161
163
|
}
|
|
@@ -163,7 +165,6 @@ export default function TextSwap({ words, animationOptions }) {
|
|
|
163
165
|
<div className="word word-2 hidden">
|
|
164
166
|
{
|
|
165
167
|
[...words[1]].map((letter, i) => {
|
|
166
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
167
168
|
return <span ref={lettersRefs2.current[i]} className="letter" key={`${i}${letter}`}>{letter}</span>;
|
|
168
169
|
})
|
|
169
170
|
}
|
|
@@ -173,14 +174,14 @@ export default function TextSwap({ words, animationOptions }) {
|
|
|
173
174
|
swapAnimations.map((renderedLetter) => {
|
|
174
175
|
const { id, letter, playing, disappear, src, dest } = renderedLetter;
|
|
175
176
|
|
|
176
|
-
let letterStyles;
|
|
177
|
+
let letterStyles = { transition: `left ${transitionDuration}ms ${timingFunction}, top ${transitionDuration}ms ${timingFunction}` };
|
|
177
178
|
if (playing) {
|
|
178
179
|
const left = `${dest.rect.x}px`;
|
|
179
|
-
letterStyles = { left };
|
|
180
|
+
letterStyles = { ...letterStyles, left };
|
|
180
181
|
}
|
|
181
182
|
else {
|
|
182
183
|
const left = `${src.rect.x}px`;
|
|
183
|
-
letterStyles = { left };
|
|
184
|
+
letterStyles = { ...letterStyles, left };
|
|
184
185
|
}
|
|
185
186
|
|
|
186
187
|
if (disappear) {
|
|
@@ -2,20 +2,24 @@ export const DEFAULT_WORDS = ['Text Swap Animation', 'Antitoxin Swamp Tea'];
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @typedef AnimationOptions Timing options for when to start, how fast to animate forwards, backwards, and when to loop.
|
|
5
|
-
* @property {number} randomStartMin The minimum amount of time to randomly wait before starting to animate each letter
|
|
6
|
-
* @property {number} randomStartMax The maximum amount of time to randomly wait before starting to animate each letter
|
|
7
|
-
* @property {number} randomReverseMin The minimum amount of time to randomly wait before starting to animate each letter in reverse
|
|
8
|
-
* @property {number} randomReverseMax The maximum amount of time to randomly wait before starting to animate each letter in reverse
|
|
9
|
-
* @property {number} loopAnimation The amount of time
|
|
10
|
-
* @property {number} waitToStart The amount of time to wait before beginning the animation on start up
|
|
5
|
+
* @property {number} randomStartMin The minimum amount of time to randomly wait before starting to animate each letter.
|
|
6
|
+
* @property {number} randomStartMax The maximum amount of time to randomly wait before starting to animate each letter. Should be `>= randomStartMin`.
|
|
7
|
+
* @property {number} randomReverseMin The minimum amount of time to randomly wait before starting to animate each letter in reverse.
|
|
8
|
+
* @property {number} randomReverseMax The maximum amount of time to randomly wait before starting to animate each letter in reverse. Should be `>= randomReverseMin`.
|
|
9
|
+
* @property {number} loopAnimation The amount of time to wait before starting the next full loop of the animation. Should be `>= randomReverseMax + transitionDuration`.
|
|
10
|
+
* @property {number} waitToStart The amount of time to wait before beginning the animation on start up the first time.
|
|
11
|
+
* @property {number} transitionDuration How long should it take for a letter to move to its next position. Should be `<= randomReverseMin - randomStartMax`.
|
|
12
|
+
* @property {string} timingFunction What [timing function](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function) should be used for the animation.
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
15
|
/** @type AnimationOptions */
|
|
14
16
|
export const DEFAULT_ANIMATION_OPTIONS = {
|
|
15
|
-
randomStartMin:
|
|
16
|
-
randomStartMax:
|
|
17
|
-
randomReverseMin:
|
|
18
|
-
randomReverseMax:
|
|
17
|
+
randomStartMin: 0,
|
|
18
|
+
randomStartMax: 3000,
|
|
19
|
+
randomReverseMin: 6000,
|
|
20
|
+
randomReverseMax: 9000,
|
|
19
21
|
loopAnimation: 12000,
|
|
20
22
|
waitToStart: 0,
|
|
23
|
+
transitionDuration: 1000,
|
|
24
|
+
timingFunction: 'ease-in-out',
|
|
21
25
|
};
|
|
@@ -1,33 +1,24 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
2
|
+
import useFonts from './useFonts';
|
|
3
3
|
import TextSwap from './TextSwap';
|
|
4
4
|
import { DEFAULT_ANIMATION_OPTIONS, DEFAULT_WORDS } from './constants';
|
|
5
5
|
|
|
6
6
|
import './index.css';
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
* @typedef FontToObserve A description of an embedded font to observe and wait until loaded.
|
|
10
|
-
* @property {string} [family] The font-family: Roboto, Inter, Open Sans, etc
|
|
11
|
-
* @property {string|number} [weight] The font-weight: normal, bold, 800, etc
|
|
12
|
-
* @property {string} [style] The font-style: normal, italic, oblique
|
|
13
|
-
* @property {string} [stretch] The font stretch: normal, condensed, expanded, etc
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
8
|
/**
|
|
17
9
|
* Render and animate from one word to another word and back again.
|
|
18
10
|
* @param {[string]} [words] The 2 words to animate between.
|
|
19
11
|
* @param {AnimationOptions} [animationOptions] Timing options for when to start, how fast to animate forwards, backwards, and when to loop.
|
|
20
|
-
* @param {
|
|
12
|
+
* @param {string} [fontToObserve] A description of an embedded font to observe and wait until loaded.
|
|
21
13
|
* @returns {JSX.Element|null}
|
|
22
14
|
*/
|
|
23
15
|
export default function Loader({ words = DEFAULT_WORDS, animationOptions = {}, fontToObserve }) {
|
|
16
|
+
const isFontLoaded = useFonts(fontToObserve);
|
|
24
17
|
const animOptions = {
|
|
25
18
|
...DEFAULT_ANIMATION_OPTIONS,
|
|
26
19
|
...animationOptions,
|
|
27
20
|
};
|
|
28
21
|
|
|
29
|
-
const isFontLoaded = useFontFaceObserver(fontToObserve ? [fontToObserve] : []);
|
|
30
|
-
|
|
31
22
|
let word1 = words[0];
|
|
32
23
|
let word2 = words[1];
|
|
33
24
|
const maxLength = Math.max(word1.length, word2.length);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export default function useFonts(...fontNames) {
|
|
4
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
// Inspired by https://stackoverflow.com/a/60138011
|
|
8
|
+
if (!document || !document.fonts) {
|
|
9
|
+
// eslint-disable-next-line no-console
|
|
10
|
+
console.warn('Browser does not support document.fonts API');
|
|
11
|
+
setIsLoaded(true);
|
|
12
|
+
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
Promise.all(fontNames.map((fontName) => document.fonts.load(`16px "${fontName}"`))).then(() => {
|
|
17
|
+
setIsLoaded(true);
|
|
18
|
+
});
|
|
19
|
+
}, [fontNames]);
|
|
20
|
+
|
|
21
|
+
return isLoaded;
|
|
22
|
+
};
|