react-text-swap-animation 1.4.0 → 1.5.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 +15 -15
- package/dist/components/TextSwap.js +69 -62
- package/dist/components/constants.js +3 -5
- package/dist/components/index.css +34 -32
- package/dist/components/index.css.map +1 -0
- package/dist/components/index.js +13 -10
- package/dist/components/useFonts.js +1 -1
- package/dist/index.js +2 -3
- package/dist/utils.js +1 -1
- package/package.json +29 -25
- package/.eslintrc.js +0 -74
- package/.github/workflows/greetings.yml +0 -16
- package/.github/workflows/node.js.yml +0 -46
- package/babel.config.json +0 -18
- package/dist/components/Anagram.js +0 -156
- package/public/index.html +0 -19
- package/src/App.css +0 -12
- package/src/index.js +0 -44
- package/src/lib/components/TextSwap.jsx +0 -201
- package/src/lib/components/constants.js +0 -25
- package/src/lib/components/index.css +0 -32
- package/src/lib/components/index.jsx +0 -29
- package/src/lib/components/index.scss +0 -38
- package/src/lib/components/useFonts.js +0 -22
- package/src/lib/index.js +0 -3
- package/src/lib/utils.js +0 -17
package/README.md
CHANGED
|
@@ -65,26 +65,26 @@ API
|
|
|
65
65
|
|
|
66
66
|
### Props
|
|
67
67
|
|
|
68
|
-
| Prop | Type | Default | Description
|
|
69
|
-
| :----------------- | :-----
|
|
70
|
-
| `words` | array | `['Text Swap Animation', 'Antitoxin Swamp Tea']` | An array containing exactly 2 words which are an anagram of each other.
|
|
71
|
-
| `animationOptions` | object | `AnimationOptions` | Timing options for when to start, how fast to animate forwards, backwards, and when to loop (optional).
|
|
72
|
-
| `fontToObserve` | string |
|
|
68
|
+
| Prop | Type | Default | Description |
|
|
69
|
+
| :----------------- | :----- |:-------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------|
|
|
70
|
+
| `words` | array | `['Text Swap Animation', 'Antitoxin Swamp Tea']` | An array containing exactly 2 words which are an anagram of each other. |
|
|
71
|
+
| `animationOptions` | object | `AnimationOptions` | Timing options for when to start, how fast to animate forwards, backwards, and when to loop (optional). |
|
|
72
|
+
| `fontToObserve` | string | | The name of an embedded font to wait until loaded. If not specified, animation will be loaded immediately (optional). |
|
|
73
73
|
|
|
74
74
|
#### AnimationOptions
|
|
75
75
|
|
|
76
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.
|
|
77
77
|
|
|
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.
|
|
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. |
|
|
88
88
|
|
|
89
89
|
Run Locally
|
|
90
90
|
----
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
require("core-js/modules/es.
|
|
3
|
+
require("core-js/modules/es.iterator.filter.js");
|
|
4
|
+
require("core-js/modules/es.iterator.for-each.js");
|
|
4
5
|
Object.defineProperty(exports, "__esModule", {
|
|
5
6
|
value: true
|
|
6
7
|
});
|
|
7
8
|
exports.default = TextSwap;
|
|
9
|
+
require("core-js/modules/es.error.cause.js");
|
|
10
|
+
require("core-js/modules/es.array.push.js");
|
|
11
|
+
require("core-js/modules/es.iterator.constructor.js");
|
|
12
|
+
require("core-js/modules/es.iterator.map.js");
|
|
8
13
|
require("core-js/modules/web.dom-collections.iterator.js");
|
|
9
|
-
var _react =
|
|
14
|
+
var _react = require("react");
|
|
10
15
|
var _utils = require("../utils");
|
|
11
|
-
|
|
12
|
-
function
|
|
13
|
-
function
|
|
14
|
-
function
|
|
15
|
-
function
|
|
16
|
-
function
|
|
17
|
-
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); }
|
|
18
|
-
/**
|
|
16
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
18
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
19
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
20
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
21
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* eslint-disable react/no-array-index-key */ /**
|
|
19
22
|
* Render and animate from one word to another word and back again.
|
|
20
23
|
*
|
|
21
24
|
* @param {[{string}]} words The 2 words to animate between.
|
|
@@ -170,56 +173,60 @@ function TextSwap(_ref) {
|
|
|
170
173
|
animateFunc();
|
|
171
174
|
}, waitToStart);
|
|
172
175
|
}, [lettersRefs1, lettersRefs2, loopAnimation, updateAnimation, randomReverseMax, randomReverseMin, randomStartMax, randomStartMin, waitToStart, transitionDuration, timingFunction, words]);
|
|
173
|
-
return /*#__PURE__*/
|
|
174
|
-
className: "text-swap"
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
176
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
177
|
+
className: "text-swap",
|
|
178
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
179
|
+
className: "word word-1 hidden",
|
|
180
|
+
children: [...words[0]].map((letter, i) => {
|
|
181
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
182
|
+
ref: lettersRefs1.current[i],
|
|
183
|
+
className: "letter",
|
|
184
|
+
children: letter
|
|
185
|
+
}, "".concat(i).concat(letter));
|
|
186
|
+
})
|
|
187
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
188
|
+
className: "word word-2 hidden",
|
|
189
|
+
children: [...words[1]].map((letter, i) => {
|
|
190
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
191
|
+
ref: lettersRefs2.current[i],
|
|
192
|
+
className: "letter",
|
|
193
|
+
children: letter
|
|
194
|
+
}, "".concat(i).concat(letter));
|
|
195
|
+
})
|
|
196
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
197
|
+
className: "word word-animation",
|
|
198
|
+
children: swapAnimations.map(renderedLetter => {
|
|
199
|
+
const {
|
|
200
|
+
id,
|
|
201
|
+
letter,
|
|
202
|
+
playing,
|
|
203
|
+
disappear,
|
|
204
|
+
src,
|
|
205
|
+
dest
|
|
206
|
+
} = renderedLetter;
|
|
207
|
+
let letterStyles = {
|
|
208
|
+
transition: "left ".concat(transitionDuration, "ms ").concat(timingFunction, ", top ").concat(transitionDuration, "ms ").concat(timingFunction)
|
|
209
|
+
};
|
|
210
|
+
if (playing) {
|
|
211
|
+
const left = "".concat(dest.rect.x, "px");
|
|
212
|
+
letterStyles = _objectSpread(_objectSpread({}, letterStyles), {}, {
|
|
213
|
+
left
|
|
214
|
+
});
|
|
215
|
+
} else {
|
|
216
|
+
const left = "".concat(src.rect.x, "px");
|
|
217
|
+
letterStyles = _objectSpread(_objectSpread({}, letterStyles), {}, {
|
|
218
|
+
left
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
if (disappear) {
|
|
222
|
+
letterStyles.opacity = 0;
|
|
223
|
+
}
|
|
224
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
225
|
+
className: "letter",
|
|
226
|
+
style: letterStyles,
|
|
227
|
+
children: letter
|
|
228
|
+
}, id);
|
|
229
|
+
})
|
|
230
|
+
})]
|
|
231
|
+
});
|
|
225
232
|
}
|
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.DEFAULT_WORDS = exports.DEFAULT_ANIMATION_OPTIONS = void 0;
|
|
7
|
-
const DEFAULT_WORDS = ['Text Swap Animation', 'Antitoxin Swamp Tea'];
|
|
7
|
+
const DEFAULT_WORDS = exports.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.
|
|
@@ -19,8 +19,7 @@ const DEFAULT_WORDS = ['Text Swap Animation', 'Antitoxin Swamp Tea'];
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
/** @type AnimationOptions */
|
|
22
|
-
exports.
|
|
23
|
-
const DEFAULT_ANIMATION_OPTIONS = {
|
|
22
|
+
const DEFAULT_ANIMATION_OPTIONS = exports.DEFAULT_ANIMATION_OPTIONS = {
|
|
24
23
|
randomStartMin: 0,
|
|
25
24
|
randomStartMax: 3000,
|
|
26
25
|
randomReverseMin: 6000,
|
|
@@ -29,5 +28,4 @@ const DEFAULT_ANIMATION_OPTIONS = {
|
|
|
29
28
|
waitToStart: 0,
|
|
30
29
|
transitionDuration: 1000,
|
|
31
30
|
timingFunction: 'ease-in-out'
|
|
32
|
-
};
|
|
33
|
-
exports.DEFAULT_ANIMATION_OPTIONS = DEFAULT_ANIMATION_OPTIONS;
|
|
31
|
+
};
|
|
@@ -1,32 +1,34 @@
|
|
|
1
|
-
.text-swap {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
.text-swap .word {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.text-swap .word.hidden {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.text-swap .word .letter {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.text-swap .word-animation .letter {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
1
|
+
.text-swap {
|
|
2
|
+
color: #fff;
|
|
3
|
+
flex: 0 0 auto;
|
|
4
|
+
align-self: center;
|
|
5
|
+
width: 100%;
|
|
6
|
+
height: 100%;
|
|
7
|
+
margin: 0 auto;
|
|
8
|
+
text-align: left;
|
|
9
|
+
text-transform: uppercase;
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
padding: 0;
|
|
13
|
+
position: relative;
|
|
14
|
+
}
|
|
15
|
+
.text-swap .word {
|
|
16
|
+
position: relative;
|
|
17
|
+
}
|
|
18
|
+
.text-swap .word.hidden {
|
|
19
|
+
position: absolute;
|
|
20
|
+
visibility: hidden;
|
|
21
|
+
z-index: -9000;
|
|
22
|
+
}
|
|
23
|
+
.text-swap .word .letter {
|
|
24
|
+
white-space: pre;
|
|
25
|
+
z-index: 10;
|
|
26
|
+
display: inline-block;
|
|
27
|
+
}
|
|
28
|
+
.text-swap .word-animation .letter {
|
|
29
|
+
z-index: 10;
|
|
30
|
+
position: absolute;
|
|
31
|
+
transition: left 2s ease-in-out, top 2s ease-in-out;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/*# sourceMappingURL=index.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sourceRoot":"","sources":["index.scss"],"names":[],"mappings":"AAAA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAEA;EACI;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAKJ;EACI;EACA;EACA","file":"index.css"}
|
package/dist/components/index.js
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
require("core-js/modules/es.array.push.js");
|
|
4
|
+
require("core-js/modules/es.iterator.constructor.js");
|
|
5
|
+
require("core-js/modules/es.iterator.filter.js");
|
|
6
|
+
require("core-js/modules/es.iterator.for-each.js");
|
|
3
7
|
Object.defineProperty(exports, "__esModule", {
|
|
4
8
|
value: true
|
|
5
9
|
});
|
|
6
10
|
exports.default = Loader;
|
|
7
|
-
require("core-js/modules/es.
|
|
8
|
-
var _react = _interopRequireDefault(require("react"));
|
|
11
|
+
require("core-js/modules/es.error.cause.js");
|
|
9
12
|
var _useFonts = _interopRequireDefault(require("./useFonts"));
|
|
10
13
|
var _TextSwap = _interopRequireDefault(require("./TextSwap"));
|
|
11
14
|
var _constants = require("./constants");
|
|
12
15
|
require("./index.css");
|
|
13
|
-
|
|
14
|
-
function
|
|
15
|
-
function
|
|
16
|
-
function
|
|
17
|
-
function
|
|
18
|
-
function
|
|
19
|
-
/**
|
|
16
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
19
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
20
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
21
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
22
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /**
|
|
20
23
|
* Render and animate from one word to another word and back again.
|
|
21
24
|
* @param {[string]} [words] The 2 words to animate between.
|
|
22
25
|
* @param {AnimationOptions} [animationOptions] Timing options for when to start, how fast to animate forwards, backwards, and when to loop.
|
|
@@ -36,7 +39,7 @@ function Loader(_ref) {
|
|
|
36
39
|
const maxLength = Math.max(word1.length, word2.length);
|
|
37
40
|
word1 = word1.padEnd(maxLength, ' ');
|
|
38
41
|
word2 = word2.padEnd(maxLength, ' ');
|
|
39
|
-
return isFontLoaded ? /*#__PURE__*/
|
|
42
|
+
return isFontLoaded ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_TextSwap.default, {
|
|
40
43
|
words: [word1, word2],
|
|
41
44
|
animationOptions: animOptions
|
|
42
45
|
}) : null;
|
|
@@ -4,8 +4,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = useFonts;
|
|
7
|
-
require("core-js/modules/web.dom-collections.iterator.js");
|
|
8
7
|
require("core-js/modules/es.promise.js");
|
|
8
|
+
require("core-js/modules/web.dom-collections.iterator.js");
|
|
9
9
|
var _react = require("react");
|
|
10
10
|
function useFonts() {
|
|
11
11
|
for (var _len = arguments.length, fontNames = new Array(_len), _key = 0; _key < _len; _key++) {
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,5 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _index = _interopRequireDefault(require("./components/index"));
|
|
8
|
-
function _interopRequireDefault(
|
|
9
|
-
var _default = _index.default;
|
|
10
|
-
exports.default = _default;
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
var _default = exports.default = _index.default;
|
package/dist/utils.js
CHANGED
|
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.randomMinMax = randomMinMax;
|
|
7
7
|
exports.uuidv4 = uuidv4;
|
|
8
8
|
require("core-js/modules/es.regexp.exec.js");
|
|
9
|
-
require("core-js/modules/es.string.replace.js");
|
|
10
9
|
require("core-js/modules/es.regexp.to-string.js");
|
|
10
|
+
require("core-js/modules/es.string.replace.js");
|
|
11
11
|
/**
|
|
12
12
|
* Get a random number between `min` and `max`
|
|
13
13
|
* @param {number} min The minimum number you want to include in the random output
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-text-swap-animation",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
8
11
|
"private": false,
|
|
9
12
|
"scripts": {
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"build": "
|
|
14
|
-
"build-
|
|
15
|
-
"build-css": "sass src/:src/",
|
|
16
|
-
"watch-css": "sass --watch src/:src/",
|
|
17
|
-
"eject": "react-scripts eject"
|
|
13
|
+
"start": "vite",
|
|
14
|
+
"build-demo": "vite build --outDir demo-build",
|
|
15
|
+
"preview": "vite preview --outDir demo-build",
|
|
16
|
+
"build": "rimraf dist && cross-env NODE_ENV=production babel src/lib --out-dir dist --copy-files",
|
|
17
|
+
"build-css": "sass src/:src/"
|
|
18
18
|
},
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
@@ -29,26 +29,30 @@
|
|
|
29
29
|
"rearrange"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"core-js": "^3.
|
|
32
|
+
"core-js": "^3.49.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"react": "^17.0.
|
|
36
|
-
"react-dom": "^17.0.
|
|
35
|
+
"react": "^17.0.0 || ^18.0.0 || >=19.0.0",
|
|
36
|
+
"react-dom": "^17.0.0 || ^18.0.0 || >=19.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@babel/cli": "^7.
|
|
40
|
-
"@babel/core": "^7.
|
|
41
|
-
"@babel/
|
|
42
|
-
"@babel/preset-env": "^7.
|
|
43
|
-
"@babel/preset-react": "^7.
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
39
|
+
"@babel/cli": "^7.28.6",
|
|
40
|
+
"@babel/core": "^7.29.0",
|
|
41
|
+
"@babel/eslint-parser": "^7.28.6",
|
|
42
|
+
"@babel/preset-env": "^7.29.2",
|
|
43
|
+
"@babel/preset-react": "^7.28.5",
|
|
44
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
45
|
+
"cross-env": "^10.1.0",
|
|
46
|
+
"eslint": "^8.57.1",
|
|
47
|
+
"eslint-plugin-import": "^2.32.0",
|
|
48
|
+
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
49
|
+
"eslint-plugin-react": "^7.37.5",
|
|
50
|
+
"eslint-plugin-react-hooks": "^4.6.2",
|
|
51
|
+
"react": "^19.2.4",
|
|
52
|
+
"react-dom": "^19.2.4",
|
|
53
|
+
"rimraf": "^6.1.3",
|
|
54
|
+
"sass": "^1.98.0",
|
|
55
|
+
"vite": "^8.0.3"
|
|
52
56
|
},
|
|
53
57
|
"browserslist": {
|
|
54
58
|
"production": [
|
package/.eslintrc.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
extends: [
|
|
3
|
-
'react-app',
|
|
4
|
-
'react-app/jest',
|
|
5
|
-
],
|
|
6
|
-
rules: {
|
|
7
|
-
indent: ['warn', 4],
|
|
8
|
-
'react/jsx-indent': ['warn', 4],
|
|
9
|
-
'react/jsx-indent-props': ['warn', 4],
|
|
10
|
-
quotes: [0, 'double', { avoidEscape: true }],
|
|
11
|
-
'quote-props': ['error', 'as-needed'],
|
|
12
|
-
'jsx-quotes': [2, 'prefer-double'],
|
|
13
|
-
'no-undef': 2,
|
|
14
|
-
'id-length': 0,
|
|
15
|
-
'max-len': 0,
|
|
16
|
-
'brace-style': [
|
|
17
|
-
1,
|
|
18
|
-
'stroustrup',
|
|
19
|
-
{ allowSingleLine: true },
|
|
20
|
-
],
|
|
21
|
-
curly: 2,
|
|
22
|
-
'no-use-before-define': [
|
|
23
|
-
1,
|
|
24
|
-
'nofunc',
|
|
25
|
-
],
|
|
26
|
-
'no-unused-vars': [
|
|
27
|
-
1,
|
|
28
|
-
{
|
|
29
|
-
args: 'none',
|
|
30
|
-
ignoreRestSiblings: true,
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
'arrow-body-style': 0,
|
|
34
|
-
'no-unused-expressions': [
|
|
35
|
-
2,
|
|
36
|
-
{ allowShortCircuit: true },
|
|
37
|
-
],
|
|
38
|
-
'object-curly-spacing': ['warn', 'always', { objectsInObjects: true }],
|
|
39
|
-
'object-curly-newline': ['warn', { multiline: true }],
|
|
40
|
-
'prefer-const': 'warn',
|
|
41
|
-
'no-restricted-syntax': [
|
|
42
|
-
1,
|
|
43
|
-
'WithStatement',
|
|
44
|
-
'DebuggerStatement',
|
|
45
|
-
],
|
|
46
|
-
'no-underscore-dangle': 0,
|
|
47
|
-
'react/jsx-boolean-value': 0,
|
|
48
|
-
'react/jsx-first-prop-new-line': 0,
|
|
49
|
-
'react/jsx-no-bind': 0,
|
|
50
|
-
'react/no-did-mount-set-state': 0,
|
|
51
|
-
'react/prefer-stateless-function': 0,
|
|
52
|
-
'react/jsx-one-expression-per-line': [0],
|
|
53
|
-
// Allow jsx tags inside .js files.
|
|
54
|
-
'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }],
|
|
55
|
-
// Disable props spreading (<App {...props} />) warning.
|
|
56
|
-
'react/jsx-props-no-spreading': 0,
|
|
57
|
-
// Throw warning instead of error when using array index as a key.
|
|
58
|
-
'react/no-array-index-key': 1,
|
|
59
|
-
// Allow using (props) => <Component /> and ({propName}) => <Component /> syntax.
|
|
60
|
-
'react/destructuring-assignment': 'off',
|
|
61
|
-
// Disable <Fragment> => <> replacement. Feel free to change
|
|
62
|
-
'react/jsx-fragments': 'off',
|
|
63
|
-
// Allow modules with named exports only.
|
|
64
|
-
'import/prefer-default-export': 0,
|
|
65
|
-
// Throw warning when <a href="#"> or <a href="javascript:void(0)"> are used. Use <button> instead.
|
|
66
|
-
'jsx-a11y/anchor-is-valid': ['off', { aspects: ['invalidHref'] }],
|
|
67
|
-
},
|
|
68
|
-
// DeprecationWarning: The 'ecmaFeatures' config file property is deprecated and has no effect. (found in ".eslintrc.js")
|
|
69
|
-
// ecmaFeatures: {
|
|
70
|
-
// jsx: true,
|
|
71
|
-
// modules: true,
|
|
72
|
-
// },
|
|
73
|
-
parser: 'babel-eslint',
|
|
74
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
name: Greetings
|
|
2
|
-
|
|
3
|
-
on: [pull_request, issues]
|
|
4
|
-
|
|
5
|
-
jobs:
|
|
6
|
-
greeting:
|
|
7
|
-
runs-on: ubuntu-latest
|
|
8
|
-
permissions:
|
|
9
|
-
issues: write
|
|
10
|
-
pull-requests: write
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/first-interaction@v1
|
|
13
|
-
with:
|
|
14
|
-
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
15
|
-
issue-message: 'Hey, thanks for raising this issue!'
|
|
16
|
-
pr-message: 'You are so AWESOME for chipping in and helping out. Thanks! Keep up the GOOD WORK!'
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# This is a basic workflow to help you get started with Actions
|
|
2
|
-
|
|
3
|
-
name: CI
|
|
4
|
-
|
|
5
|
-
# Controls when the workflow will run
|
|
6
|
-
on:
|
|
7
|
-
# Triggers the workflow on push or pull request events but only for the main branch
|
|
8
|
-
push:
|
|
9
|
-
branches: [ main ]
|
|
10
|
-
pull_request:
|
|
11
|
-
branches: [ main ]
|
|
12
|
-
|
|
13
|
-
# Allows you to run this workflow manually from the Actions tab
|
|
14
|
-
workflow_dispatch:
|
|
15
|
-
|
|
16
|
-
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
|
17
|
-
jobs:
|
|
18
|
-
# This workflow contains a single job called "build"
|
|
19
|
-
build:
|
|
20
|
-
# The type of runner that the job will run on
|
|
21
|
-
runs-on: ubuntu-latest
|
|
22
|
-
|
|
23
|
-
strategy:
|
|
24
|
-
matrix:
|
|
25
|
-
node-version: [ 14.x, 16.x, 18.x ]
|
|
26
|
-
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
27
|
-
|
|
28
|
-
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
29
|
-
steps:
|
|
30
|
-
- uses: actions/checkout@v3
|
|
31
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
32
|
-
uses: actions/setup-node@v3
|
|
33
|
-
with:
|
|
34
|
-
node-version: ${{ matrix.node-version }}
|
|
35
|
-
cache: 'npm'
|
|
36
|
-
# Runs a single command using the runners shell
|
|
37
|
-
- name: Run a one-line script
|
|
38
|
-
run: echo Building now...
|
|
39
|
-
|
|
40
|
-
# - name: Install python2
|
|
41
|
-
# run: apt update && DEBIAN_FRONTEND=noninteractive apt install -y python2 make g++
|
|
42
|
-
# - name: Test python2
|
|
43
|
-
# run: python2 --version
|
|
44
|
-
# - run: npm install sass node-sass@4.14.1
|
|
45
|
-
- run: npm i
|
|
46
|
-
- run: npm run build-fresh --if-present
|
package/babel.config.json
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = Anagram;
|
|
7
|
-
|
|
8
|
-
require("core-js/modules/web.dom-collections.iterator.js");
|
|
9
|
-
|
|
10
|
-
var _react = _interopRequireWildcard(require("react"));
|
|
11
|
-
|
|
12
|
-
var _utils = require("../utils");
|
|
13
|
-
|
|
14
|
-
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
15
|
-
|
|
16
|
-
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
17
|
-
|
|
18
|
-
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
|
|
19
|
-
|
|
20
|
-
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
|
|
21
|
-
|
|
22
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Render and animate from one word to another word and back again.
|
|
26
|
-
*
|
|
27
|
-
* @param {[{string}]} words The 2 words to animate between.
|
|
28
|
-
* @param {AnimationOptions} animationOptions Timing options for when to start, how fast forward/backwards, and when to loop.
|
|
29
|
-
* @returns {JSX.Element}
|
|
30
|
-
*/
|
|
31
|
-
function Anagram(_ref) {
|
|
32
|
-
let {
|
|
33
|
-
words,
|
|
34
|
-
animationOptions
|
|
35
|
-
} = _ref;
|
|
36
|
-
const lettersRefs1 = (0, _react.useRef)([...words[0]].map(() => /*#__PURE__*/(0, _react.createRef)()));
|
|
37
|
-
const lettersRefs2 = (0, _react.useRef)([...words[1]].map(() => /*#__PURE__*/(0, _react.createRef)()));
|
|
38
|
-
const [swapAnimations, setSwapAnimations] = (0, _react.useState)({});
|
|
39
|
-
const playAnimation = (0, _react.useCallback)(function (i) {
|
|
40
|
-
let playing = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
41
|
-
setSwapAnimations(prevState => {
|
|
42
|
-
return _objectSpread(_objectSpread({}, prevState), {}, {
|
|
43
|
-
[i]: _objectSpread(_objectSpread({}, prevState[i]), {}, {
|
|
44
|
-
playing
|
|
45
|
-
})
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
}, [setSwapAnimations]);
|
|
49
|
-
const {
|
|
50
|
-
randomStartMin,
|
|
51
|
-
randomStartMax,
|
|
52
|
-
randomReverseMin,
|
|
53
|
-
randomReverseMax,
|
|
54
|
-
loopAnimation,
|
|
55
|
-
waitToStart
|
|
56
|
-
} = animationOptions;
|
|
57
|
-
(0, _react.useEffect)(() => {
|
|
58
|
-
const swaps = [];
|
|
59
|
-
const destLettersPairedByIndex = [];
|
|
60
|
-
[...words[0]].forEach((letter, i) => {
|
|
61
|
-
// Find a matching dest character to execute the swap with
|
|
62
|
-
const destLetterIndex = [...words[1]].findIndex((destLetter, srcIndex) => {
|
|
63
|
-
return destLetter.toLowerCase() === letter.toLowerCase() && destLettersPairedByIndex[srcIndex] !== true;
|
|
64
|
-
});
|
|
65
|
-
destLettersPairedByIndex[destLetterIndex] = true; // mark this source paired/used
|
|
66
|
-
|
|
67
|
-
if (destLetterIndex === -1) {
|
|
68
|
-
throw new Error("Not sure how to animate since all source letters were paired already, disappear maybe?");
|
|
69
|
-
} // If the text wraps then the offset left isn't correct.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const swap = {
|
|
73
|
-
src: {
|
|
74
|
-
letter,
|
|
75
|
-
element: lettersRefs1.current[i].current,
|
|
76
|
-
offsetLeft: lettersRefs1.current[i].current.offsetLeft,
|
|
77
|
-
offsetTop: lettersRefs1.current[i].current.offsetTop // rect: lettersRefs1.current[i].current.getBoundingClientRect(),
|
|
78
|
-
|
|
79
|
-
},
|
|
80
|
-
dest: {
|
|
81
|
-
letter: words[1][destLetterIndex],
|
|
82
|
-
element: lettersRefs2.current[destLetterIndex].current,
|
|
83
|
-
offsetLeft: lettersRefs2.current[destLetterIndex].current.offsetLeft,
|
|
84
|
-
offsetTop: lettersRefs2.current[destLetterIndex].current.offsetTop // rect: lettersRefs2.current[destLetterIndex].current.getBoundingClientRect(),
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
swaps.push(swap);
|
|
89
|
-
});
|
|
90
|
-
setSwapAnimations(swaps);
|
|
91
|
-
|
|
92
|
-
const animateFunc = () => {
|
|
93
|
-
swaps.forEach((swap, i) => {
|
|
94
|
-
// Animate each character towards the destination
|
|
95
|
-
setTimeout(() => {
|
|
96
|
-
playAnimation(i);
|
|
97
|
-
}, (0, _utils.randomMinMax)(randomStartMin, randomStartMax)); // Animate each character back to their original location
|
|
98
|
-
|
|
99
|
-
setTimeout(() => {
|
|
100
|
-
playAnimation(i, false);
|
|
101
|
-
}, (0, _utils.randomMinMax)(randomReverseMin, randomReverseMax));
|
|
102
|
-
}); // Repeat forever
|
|
103
|
-
|
|
104
|
-
setTimeout(() => {
|
|
105
|
-
animateFunc();
|
|
106
|
-
}, loopAnimation);
|
|
107
|
-
}; // Start the process
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
setTimeout(() => {
|
|
111
|
-
animateFunc();
|
|
112
|
-
}, waitToStart);
|
|
113
|
-
}, [lettersRefs1, lettersRefs2, loopAnimation, playAnimation, randomReverseMax, randomReverseMin, randomStartMax, randomStartMin, waitToStart, words]);
|
|
114
|
-
return /*#__PURE__*/_react.default.createElement("div", {
|
|
115
|
-
className: "anagram-swap"
|
|
116
|
-
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
117
|
-
className: "word word-1 hidden"
|
|
118
|
-
}, [...words[0]].map((letter, i) => {
|
|
119
|
-
return /*#__PURE__*/_react.default.createElement("span", {
|
|
120
|
-
ref: lettersRefs1.current[i],
|
|
121
|
-
className: "letter",
|
|
122
|
-
key: "".concat(i).concat(letter)
|
|
123
|
-
}, letter);
|
|
124
|
-
})), /*#__PURE__*/_react.default.createElement("div", {
|
|
125
|
-
className: "word word-2 hidden"
|
|
126
|
-
}, [...words[1]].map((letter, i) => {
|
|
127
|
-
return /*#__PURE__*/_react.default.createElement("span", {
|
|
128
|
-
ref: lettersRefs2.current[i],
|
|
129
|
-
className: "letter",
|
|
130
|
-
key: "".concat(i).concat(letter)
|
|
131
|
-
}, letter);
|
|
132
|
-
})), /*#__PURE__*/_react.default.createElement("div", {
|
|
133
|
-
className: "word word-animation"
|
|
134
|
-
}, [...words[0]].map((letter, i) => {
|
|
135
|
-
let letterStyles = {};
|
|
136
|
-
const swap = swapAnimations[i];
|
|
137
|
-
|
|
138
|
-
if (swap && swap.playing) {
|
|
139
|
-
const left = "".concat(swap.dest.offsetLeft - swap.src.offsetLeft, "px");
|
|
140
|
-
const top = "".concat(swap.dest.offsetTop - swap.src.offsetTop, "px"); // Trying to fix issue with wrapped text
|
|
141
|
-
// const left = `${swap.dest.rect.x - swap.src.rect.x}px`;
|
|
142
|
-
// const top = `${swap.dest.rect.y - swap.src.rect.y}px`;
|
|
143
|
-
|
|
144
|
-
letterStyles = {
|
|
145
|
-
left,
|
|
146
|
-
top
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return /*#__PURE__*/_react.default.createElement("span", {
|
|
151
|
-
key: "".concat(i).concat(letter),
|
|
152
|
-
className: "letter",
|
|
153
|
-
style: letterStyles
|
|
154
|
-
}, letter);
|
|
155
|
-
})));
|
|
156
|
-
}
|
package/public/index.html
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
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/App.css
DELETED
package/src/index.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import ReactDOM from 'react-dom';
|
|
3
|
-
import TextSwap from './lib/components';
|
|
4
|
-
import './App.css';
|
|
5
|
-
|
|
6
|
-
ReactDOM.render(
|
|
7
|
-
<React.StrictMode>
|
|
8
|
-
<div>
|
|
9
|
-
<h1>React Text Swap Animation</h1>
|
|
10
|
-
<h2>Demo</h2>
|
|
11
|
-
|
|
12
|
-
<TextSwap fontToObserve="Open Sans" />
|
|
13
|
-
|
|
14
|
-
<br />
|
|
15
|
-
<br />
|
|
16
|
-
|
|
17
|
-
<TextSwap fontToObserve="Open Sans" words={['a witty saying', 'proves nothing']} />
|
|
18
|
-
|
|
19
|
-
<br />
|
|
20
|
-
<br />
|
|
21
|
-
|
|
22
|
-
<TextSwap fontToObserve="Open Sans" words={['don\'t be sad it\'s over', 'be happy that it happened']} />
|
|
23
|
-
|
|
24
|
-
<br />
|
|
25
|
-
<br />
|
|
26
|
-
|
|
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
|
-
}} />
|
|
41
|
-
</div>
|
|
42
|
-
</React.StrictMode>,
|
|
43
|
-
document.getElementById('root'),
|
|
44
|
-
);
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/* eslint-disable react/no-array-index-key */
|
|
2
|
-
import React, { createRef, useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
-
import { randomMinMax, uuidv4 } from '../utils';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Render and animate from one word to another word and back again.
|
|
7
|
-
*
|
|
8
|
-
* @param {[{string}]} words The 2 words to animate between.
|
|
9
|
-
* @param {AnimationOptions} animationOptions Timing options for when to start, how fast forward/backwards, and when to loop.
|
|
10
|
-
* @returns {JSX.Element}
|
|
11
|
-
*/
|
|
12
|
-
export default function TextSwap({ words, animationOptions }) {
|
|
13
|
-
const [swapAnimations, setAnimations] = useState([]);
|
|
14
|
-
const lettersRefs1 = useRef([...words[0]].map(() => createRef()));
|
|
15
|
-
const lettersRefs2 = useRef([...words[1]].map(() => createRef()));
|
|
16
|
-
const updateAnimation = useCallback((i, update = {}) => {
|
|
17
|
-
setAnimations((prevState) => {
|
|
18
|
-
const newState = [
|
|
19
|
-
...prevState,
|
|
20
|
-
];
|
|
21
|
-
newState[i] = {
|
|
22
|
-
...prevState[i],
|
|
23
|
-
...update,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
return newState;
|
|
27
|
-
});
|
|
28
|
-
}, [setAnimations]);
|
|
29
|
-
|
|
30
|
-
const {
|
|
31
|
-
randomStartMin,
|
|
32
|
-
randomStartMax,
|
|
33
|
-
randomReverseMin,
|
|
34
|
-
randomReverseMax,
|
|
35
|
-
loopAnimation,
|
|
36
|
-
waitToStart,
|
|
37
|
-
transitionDuration,
|
|
38
|
-
timingFunction,
|
|
39
|
-
} = animationOptions;
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
const swaps = [];
|
|
43
|
-
const destLettersPairedByIndex = [];
|
|
44
|
-
|
|
45
|
-
[...words[0]].forEach((letter, i) => {
|
|
46
|
-
// Find a matching destination character to execute the swap with
|
|
47
|
-
let destLetterIndex = [...words[1]].findIndex((destLetter, srcIndex) => {
|
|
48
|
-
return destLetter.toLowerCase() === letter.toLowerCase()
|
|
49
|
-
&& destLettersPairedByIndex[srcIndex] !== true;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// If no matching character
|
|
53
|
-
if (destLetterIndex === -1) {
|
|
54
|
-
// Find a space to swap with
|
|
55
|
-
destLetterIndex = [...words[1]].findIndex((destLetter, srcIndex) => {
|
|
56
|
-
return destLetter === ' ' && destLettersPairedByIndex[srcIndex] !== true;
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// If there was no space, find first non-used space
|
|
60
|
-
if (destLetterIndex === -1) {
|
|
61
|
-
destLetterIndex = [...words[1]].findIndex((destLetter, srcIndex) => {
|
|
62
|
-
return destLettersPairedByIndex[srcIndex] !== true;
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (destLetterIndex === -1) {
|
|
68
|
-
throw new Error(`Not sure how to animate since all source letters were paired already, disappear maybe?`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
destLettersPairedByIndex[destLetterIndex] = true; // mark this source paired/used
|
|
72
|
-
|
|
73
|
-
// If the text wraps then the offset left isn't correct.
|
|
74
|
-
const swap = {
|
|
75
|
-
id: uuidv4(), // for a unique key
|
|
76
|
-
letter, // the displayed letter
|
|
77
|
-
playing: false, // if this letter is animating to the destination
|
|
78
|
-
disappear: false, // if this letter should disappear (temporarily)
|
|
79
|
-
// the source location, starting place and letter
|
|
80
|
-
src: {
|
|
81
|
-
letter: words[0][i],
|
|
82
|
-
element: lettersRefs1.current[i].current,
|
|
83
|
-
offsetLeft: lettersRefs1.current[i].current.offsetLeft,
|
|
84
|
-
offsetTop: lettersRefs1.current[i].current.offsetTop,
|
|
85
|
-
rect: lettersRefs1.current[i].current.getBoundingClientRect(),
|
|
86
|
-
},
|
|
87
|
-
// the destination location and letter
|
|
88
|
-
dest: {
|
|
89
|
-
letter: words[1][destLetterIndex],
|
|
90
|
-
element: lettersRefs2.current[destLetterIndex].current,
|
|
91
|
-
offsetLeft: lettersRefs2.current[destLetterIndex].current.offsetLeft,
|
|
92
|
-
offsetTop: lettersRefs2.current[destLetterIndex].current.offsetTop,
|
|
93
|
-
rect: lettersRefs2.current[destLetterIndex].current.getBoundingClientRect(),
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
|
-
swaps.push(swap);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
setAnimations(swaps);
|
|
100
|
-
|
|
101
|
-
const animateFunc = () => {
|
|
102
|
-
swaps.forEach((swap, i) => {
|
|
103
|
-
// Animate each character towards the destination
|
|
104
|
-
const forwardStartTime = randomMinMax(randomStartMin, randomStartMax);
|
|
105
|
-
setTimeout(() => {
|
|
106
|
-
updateAnimation(i, { playing: true, disappear: false });
|
|
107
|
-
if (swap.src.letter !== ' ' && swap.dest.letter === ' ') {
|
|
108
|
-
updateAnimation(i, { disappear: true });
|
|
109
|
-
}
|
|
110
|
-
}, forwardStartTime);
|
|
111
|
-
|
|
112
|
-
// Halfway towards the destination, switch to the destination letter
|
|
113
|
-
setTimeout(() => {
|
|
114
|
-
if (swap.src.letter !== ' ' && swap.dest.letter === ' ') {
|
|
115
|
-
// do nothing
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
updateAnimation(i, { letter: swap.dest.letter });
|
|
119
|
-
}
|
|
120
|
-
}, forwardStartTime + 500);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// Animate each character back to their original location
|
|
124
|
-
const reverseStartTime = randomMinMax(randomReverseMin, randomReverseMax);
|
|
125
|
-
setTimeout(() => {
|
|
126
|
-
updateAnimation(i, { playing: false, disappear: false });
|
|
127
|
-
if (swap.dest.letter !== ' ' && swap.src.letter === ' ') {
|
|
128
|
-
updateAnimation(i, { disappear: true });
|
|
129
|
-
}
|
|
130
|
-
}, reverseStartTime);
|
|
131
|
-
|
|
132
|
-
// Half way back to the initial location, switch to the initial letter
|
|
133
|
-
setTimeout(() => {
|
|
134
|
-
if (swap.dest.letter !== ' ' && swap.src.letter === ' ') {
|
|
135
|
-
// do nothing
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
updateAnimation(i, { letter: swap.src.letter });
|
|
139
|
-
}
|
|
140
|
-
}, reverseStartTime + 500);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Repeat forever
|
|
144
|
-
setTimeout(() => {
|
|
145
|
-
animateFunc();
|
|
146
|
-
}, loopAnimation);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
// Start the process
|
|
150
|
-
setTimeout(() => {
|
|
151
|
-
animateFunc();
|
|
152
|
-
}, waitToStart);
|
|
153
|
-
|
|
154
|
-
}, [lettersRefs1, lettersRefs2, loopAnimation, updateAnimation, randomReverseMax, randomReverseMin, randomStartMax, randomStartMin, waitToStart, transitionDuration, timingFunction, words]);
|
|
155
|
-
|
|
156
|
-
return (
|
|
157
|
-
<div className="text-swap">
|
|
158
|
-
<div className="word word-1 hidden">
|
|
159
|
-
{
|
|
160
|
-
[...words[0]].map((letter, i) => {
|
|
161
|
-
return <span ref={lettersRefs1.current[i]} className="letter" key={`${i}${letter}`}>{letter}</span>;
|
|
162
|
-
})
|
|
163
|
-
}
|
|
164
|
-
</div>
|
|
165
|
-
<div className="word word-2 hidden">
|
|
166
|
-
{
|
|
167
|
-
[...words[1]].map((letter, i) => {
|
|
168
|
-
return <span ref={lettersRefs2.current[i]} className="letter" key={`${i}${letter}`}>{letter}</span>;
|
|
169
|
-
})
|
|
170
|
-
}
|
|
171
|
-
</div>
|
|
172
|
-
<div className="word word-animation">
|
|
173
|
-
{
|
|
174
|
-
swapAnimations.map((renderedLetter) => {
|
|
175
|
-
const { id, letter, playing, disappear, src, dest } = renderedLetter;
|
|
176
|
-
|
|
177
|
-
let letterStyles = { transition: `left ${transitionDuration}ms ${timingFunction}, top ${transitionDuration}ms ${timingFunction}` };
|
|
178
|
-
if (playing) {
|
|
179
|
-
const left = `${dest.rect.x}px`;
|
|
180
|
-
letterStyles = { ...letterStyles, left };
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
const left = `${src.rect.x}px`;
|
|
184
|
-
letterStyles = { ...letterStyles, left };
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (disappear) {
|
|
188
|
-
letterStyles.opacity = 0;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return (
|
|
192
|
-
<span key={id} className="letter" style={letterStyles}>
|
|
193
|
-
{letter}
|
|
194
|
-
</span>
|
|
195
|
-
);
|
|
196
|
-
})
|
|
197
|
-
}
|
|
198
|
-
</div>
|
|
199
|
-
</div>
|
|
200
|
-
);
|
|
201
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export const DEFAULT_WORDS = ['Text Swap Animation', 'Antitoxin Swamp Tea'];
|
|
2
|
-
|
|
3
|
-
/**
|
|
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. 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.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/** @type AnimationOptions */
|
|
16
|
-
export const DEFAULT_ANIMATION_OPTIONS = {
|
|
17
|
-
randomStartMin: 0,
|
|
18
|
-
randomStartMax: 3000,
|
|
19
|
-
randomReverseMin: 6000,
|
|
20
|
-
randomReverseMax: 9000,
|
|
21
|
-
loopAnimation: 12000,
|
|
22
|
-
waitToStart: 0,
|
|
23
|
-
transitionDuration: 1000,
|
|
24
|
-
timingFunction: 'ease-in-out',
|
|
25
|
-
};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
.text-swap {
|
|
2
|
-
color: #fff;
|
|
3
|
-
flex: 0 0 auto;
|
|
4
|
-
align-self: center;
|
|
5
|
-
width: 100%;
|
|
6
|
-
height: 100%;
|
|
7
|
-
margin: 0 auto;
|
|
8
|
-
text-align: left;
|
|
9
|
-
text-transform: uppercase;
|
|
10
|
-
display: flex;
|
|
11
|
-
flex-direction: column;
|
|
12
|
-
padding: 0;
|
|
13
|
-
position: relative; }
|
|
14
|
-
|
|
15
|
-
.text-swap .word {
|
|
16
|
-
position: relative; }
|
|
17
|
-
|
|
18
|
-
.text-swap .word.hidden {
|
|
19
|
-
position: absolute;
|
|
20
|
-
visibility: hidden;
|
|
21
|
-
z-index: -9000; }
|
|
22
|
-
|
|
23
|
-
.text-swap .word .letter {
|
|
24
|
-
white-space: pre;
|
|
25
|
-
z-index: 10;
|
|
26
|
-
display: inline-block; }
|
|
27
|
-
|
|
28
|
-
.text-swap .word-animation .letter {
|
|
29
|
-
z-index: 10;
|
|
30
|
-
position: absolute;
|
|
31
|
-
/*transition: all, 2s, cubic-bezier(0.1, 0.7, 1.0, 0.1), 2s;*/
|
|
32
|
-
transition: all, 2s, ease-in-out, 2s; }
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import useFonts from './useFonts';
|
|
3
|
-
import TextSwap from './TextSwap';
|
|
4
|
-
import { DEFAULT_ANIMATION_OPTIONS, DEFAULT_WORDS } from './constants';
|
|
5
|
-
|
|
6
|
-
import './index.css';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Render and animate from one word to another word and back again.
|
|
10
|
-
* @param {[string]} [words] The 2 words to animate between.
|
|
11
|
-
* @param {AnimationOptions} [animationOptions] Timing options for when to start, how fast to animate forwards, backwards, and when to loop.
|
|
12
|
-
* @param {string} [fontToObserve] A description of an embedded font to observe and wait until loaded.
|
|
13
|
-
* @returns {JSX.Element|null}
|
|
14
|
-
*/
|
|
15
|
-
export default function Loader({ words = DEFAULT_WORDS, animationOptions = {}, fontToObserve }) {
|
|
16
|
-
const isFontLoaded = useFonts(fontToObserve);
|
|
17
|
-
const animOptions = {
|
|
18
|
-
...DEFAULT_ANIMATION_OPTIONS,
|
|
19
|
-
...animationOptions,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
let word1 = words[0];
|
|
23
|
-
let word2 = words[1];
|
|
24
|
-
const maxLength = Math.max(word1.length, word2.length);
|
|
25
|
-
word1 = word1.padEnd(maxLength, ' ');
|
|
26
|
-
word2 = word2.padEnd(maxLength, ' ');
|
|
27
|
-
|
|
28
|
-
return isFontLoaded ? <TextSwap words={[word1, word2]} animationOptions={animOptions} /> : null;
|
|
29
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
.text-swap {
|
|
2
|
-
color: #fff;
|
|
3
|
-
flex: 0 0 auto;
|
|
4
|
-
align-self: center;
|
|
5
|
-
width: 100%;
|
|
6
|
-
height: 100%;
|
|
7
|
-
margin: 0 auto;
|
|
8
|
-
text-align: left;
|
|
9
|
-
text-transform: uppercase;
|
|
10
|
-
display: flex;
|
|
11
|
-
flex-direction: column;
|
|
12
|
-
padding: 0;
|
|
13
|
-
position: relative;
|
|
14
|
-
|
|
15
|
-
.word {
|
|
16
|
-
position: relative;
|
|
17
|
-
|
|
18
|
-
&.hidden {
|
|
19
|
-
position: absolute;
|
|
20
|
-
visibility: hidden;
|
|
21
|
-
z-index: -9000;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
.letter {
|
|
25
|
-
white-space: pre;
|
|
26
|
-
z-index: 10;
|
|
27
|
-
display: inline-block;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.word-animation {
|
|
32
|
-
.letter {
|
|
33
|
-
z-index: 10;
|
|
34
|
-
position: absolute;
|
|
35
|
-
transition: left 2s ease-in-out, top 2s ease-in-out;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
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
|
-
};
|
package/src/lib/index.js
DELETED
package/src/lib/utils.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Get a random number between `min` and `max`
|
|
3
|
-
* @param {number} min The minimum number you want to include in the random output
|
|
4
|
-
* @param {number} max The maximum number you want to include in the random output
|
|
5
|
-
* @returns {number} A random number including and between the `min` and `max`
|
|
6
|
-
*/
|
|
7
|
-
export function randomMinMax(min = 0, max = 100) {
|
|
8
|
-
return Math.floor(Math.random() * (max - min)) + min;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function uuidv4() {
|
|
12
|
-
return 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
13
|
-
const r = (Math.random() * 16) | 0,
|
|
14
|
-
v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
15
|
-
return v.toString(16);
|
|
16
|
-
});
|
|
17
|
-
}
|