diff --git a/README.md b/README.md index 1622d89..6a92199 100644 --- a/README.md +++ b/README.md @@ -45,22 +45,19 @@ To control the animation speed and timing, you can pass an object of `animationO randomReverseMax: 6000, loopAnimation: 20000, waitToStart: 5000, + transitionDuration: 2000, + timingFunction: 'ease-in-out', }} /> ``` If you are using an embedded font and need to wait for it to load before animating, -then you should specify the `fontToObserve` object with the font family name and/or other font specifics. +then you should specify the `fontToObserve` property with the font family name. ```js - + ``` ```js - + ``` API @@ -68,37 +65,26 @@ API ### Props -| Prop | Type | Default | Description | -| :----------------- | :----- | :------------------------------------------------------- | :------------------------------------------------------ | -| `words` | array | `['React Anagram Animation', 'Magenta Raincoat Airman']` | An array containing exactly 2 words which are an anagram of each other. | -| `animationOptions` | object | `AnimationOptions` | Timing options for when to start, how fast to animate forwards, backwards, and when to loop (optional). | -| `fontToObserve` | object | `FontToObserve` | A description of an embedded font to observe and wait until loaded. If not specified, animation will loaded immediately (optional). | +| Prop | Type | Default | Description | +| :----------------- |:-------|:---------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------| +| `words` | array | `['React Anagram Animation', 'Magenta Raincoat Airman']` | An array containing exactly 2 words which are an anagram of each other. | +| `animationOptions` | object | `AnimationOptions` | Timing options for when to start, how fast to animate forwards, backwards, and when to loop (optional). | +| `fontToObserve` | string | | The name of an embedded font to wait until loaded. If not specified, animation will loaded immediately (optional). | #### AnimationOptions -All time values are in # of milliseconds. - -| Property | Type | Default | Description | -| :------------------- | :----- | :------------ | :-------------------------------------------------------------------------------------------- | -| `randomStartMin` | number | `0` | The minimum amount of time to randomly wait before starting to animate each letter | -| `randomStartMax` | number | `3000` | The maximum amount of time to randomly wait before starting to animate each letter | -| `randomReverseMin` | number | `6000` | The minimum amount of time to randomly wait before starting to animate each letter in reverse | -| `randomReverseMax` | number | `9000` | The maximum amount of time to randomly wait before starting to animate each letter in reverse | -| `loopAnimation` | number | `12000` | The amount of time for each full loop of the animation | -| `waitToStart` | number | `0` | The amount of time to wait before beginning the animation on start up | -| `transitionDuration` | number | `2000` | How long should it take for a letter to move to its next position | -| `timingFunction` | string | `ease-in-out` | What timing function should be used for the animation | - -#### FontToObserve - -This object is passed along to [Font Face Observer](https://github.com/iamskok/use-font-face-observer) +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. -| Property | Type | Description | -| :---------| :--------------- | :------------------------------------------------------- | -| `family` | string | The font-family: `Roboto`, `Inter`, `Open Sans`, etc | -| `weight` | string or number | The font-weight: `normal`, `bold`, `800`, etc | -| `style` | string | The font-style: `normal`, `italic`, `oblique` | -| `stretch` | string | The font stretch: `normal`, `condensed`, `expanded`, etc | +| Property | Type | Default | Description | +| :------------------- | :----- |:--------------|:--------------------------------------------------------------------------------------------------------------------------------------| +| `randomStartMin` | number | `0` | The minimum amount of time to randomly wait before starting to animate each letter. | +| `randomStartMax` | number | `3000` | The maximum amount of time to randomly wait before starting to animate each letter. Should be `>= randomStartMin`. | +| `randomReverseMin` | number | `6000` | The minimum amount of time to randomly wait before starting to animate each letter in reverse. | +| `randomReverseMax` | number | `9000` | The maximum amount of time to randomly wait before starting to animate each letter in reverse. Should be `>= randomReverseMin`. | +| `loopAnimation` | number | `12000` | The amount of time to wait before starting the next full loop of the animation. Should be `>= randomReverseMax + transitionDuration`. | +| `waitToStart` | number | `0` | The amount of time to wait before beginning the animation on start up the first time. | +| `transitionDuration` | number | `1000` | How long should it take for a letter to move to its next position. Should be `<= randomReverseMin - randomStartMax`. | +| `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. | Run Locally ---- @@ -110,11 +96,18 @@ To run demo locally: and a browser will open to the demo. +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. + +``` +npm link ../my-app/node_modules/react +npm link ../my-app/node_modules/react-dom +``` + Future Ideas ---- - Animate between more than 2 words. -- Animate non-anagram words. +- Animate non-anagram words. [Done](https://www.npmjs.com/package/react-text-swap-animation). License diff --git a/dist/components/Anagram.js b/dist/components/Anagram.js index 7b79a67..4f0b340 100644 --- a/dist/components/Anagram.js +++ b/dist/components/Anagram.js @@ -1,6 +1,7 @@ "use strict"; require("core-js/modules/es.symbol.description.js"); +require("core-js/modules/es.weak-map.js"); Object.defineProperty(exports, "__esModule", { value: true }); @@ -76,19 +77,15 @@ function Anagram(_ref) { element: lettersRefs1.current[i].current, offsetLeft: lettersRefs1.current[i].current.offsetLeft, offsetTop: lettersRefs1.current[i].current.offsetTop - // rect: lettersRefs1.current[i].current.getBoundingClientRect(), }, - // the destination location and letter dest: { letter: words[1][destLetterIndex], element: lettersRefs2.current[destLetterIndex].current, offsetLeft: lettersRefs2.current[destLetterIndex].current.offsetLeft, offsetTop: lettersRefs2.current[destLetterIndex].current.offsetTop - // rect: lettersRefs2.current[destLetterIndex].current.getBoundingClientRect(), } }; - swaps.push(swap); }); setAnimations(swaps); @@ -127,7 +124,6 @@ function Anagram(_ref) { }, /*#__PURE__*/_react.default.createElement("div", { className: "word word-1 hidden" }, [...words[0]].map((letter, i) => { - // eslint-disable-next-line react/no-array-index-key return /*#__PURE__*/_react.default.createElement("span", { ref: lettersRefs1.current[i], className: "letter", @@ -136,7 +132,6 @@ function Anagram(_ref) { })), /*#__PURE__*/_react.default.createElement("div", { className: "word word-2 hidden" }, [...words[1]].map((letter, i) => { - // eslint-disable-next-line react/no-array-index-key return /*#__PURE__*/_react.default.createElement("span", { ref: lettersRefs2.current[i], className: "letter", @@ -144,7 +139,7 @@ function Anagram(_ref) { }, letter); })), /*#__PURE__*/_react.default.createElement("div", { className: "word word-animation" - }, swapAnimations.map((renderedLetter, i) => { + }, swapAnimations.map(renderedLetter => { const { id, letter, diff --git a/dist/components/constants.js b/dist/components/constants.js index 7f1d7eb..04d1977 100644 --- a/dist/components/constants.js +++ b/dist/components/constants.js @@ -8,12 +8,14 @@ const DEFAULT_WORDS = ['React Anagram Animation', 'Magenta Raincoat Airman']; /** * @typedef AnimationOptions Timing options for when to start, how fast to animate forwards, backwards, and when to loop. - * @property {number} randomStartMin The minimum amount of time to randomly wait before starting to animate each letter - * @property {number} randomStartMax The maximum amount of time to randomly wait before starting to animate each letter - * @property {number} randomReverseMin The minimum amount of time to randomly wait before starting to animate each letter in reverse - * @property {number} randomReverseMax The maximum amount of time to randomly wait before starting to animate each letter in reverse - * @property {number} loopAnimation The amount of time for each full loop of the animation - * @property {number} waitToStart The amount of time to wait before beginning the animation on start up + * @property {number} randomStartMin The minimum amount of time to randomly wait before starting to animate each letter. + * @property {number} randomStartMax The maximum amount of time to randomly wait before starting to animate each letter. Should be `>= randomStartMin`. + * @property {number} randomReverseMin The minimum amount of time to randomly wait before starting to animate each letter in reverse. + * @property {number} randomReverseMax The maximum amount of time to randomly wait before starting to animate each letter in reverse. Should be `>= randomReverseMin`. + * @property {number} loopAnimation The amount of time to wait before starting the next full loop of the animation. Should be `>= randomReverseMax + transitionDuration`. + * @property {number} waitToStart The amount of time to wait before beginning the animation on start up the first time. + * @property {number} transitionDuration How long should it take for a letter to move to its next position. Should be `<= randomReverseMin - randomStartMax`. + * @property {string} timingFunction What [timing function](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function) should be used for the animation. */ /** @type AnimationOptions */ @@ -25,7 +27,7 @@ const DEFAULT_ANIMATION_OPTIONS = { randomReverseMax: 9000, loopAnimation: 12000, waitToStart: 0, - transitionDuration: 2000, + transitionDuration: 1000, timingFunction: 'ease-in-out' }; exports.DEFAULT_ANIMATION_OPTIONS = DEFAULT_ANIMATION_OPTIONS; \ No newline at end of file diff --git a/dist/components/index.js b/dist/components/index.js index a0aae80..502f0b1 100644 --- a/dist/components/index.js +++ b/dist/components/index.js @@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { exports.default = Loader; require("core-js/modules/es.symbol.description.js"); var _react = _interopRequireDefault(require("react")); -var _useFontFaceObserver = _interopRequireDefault(require("use-font-face-observer")); +var _useFonts = _interopRequireDefault(require("./useFonts")); var _Anagram = _interopRequireDefault(require("./Anagram")); var _constants = require("./constants"); require("./index.css"); @@ -16,19 +16,11 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va 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; } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } 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); } -/** - * @typedef FontToObserve A description of an embedded font to observe and wait until loaded. - * @property {string} [family] The font-family: Roboto, Inter, Open Sans, etc - * @property {string|number} [weight] The font-weight: normal, bold, 800, etc - * @property {string} [style] The font-style: normal, italic, oblique - * @property {string} [stretch] The font stretch: normal, condensed, expanded, etc - */ - /** * Render and animate from one word to another word and back again. * @param {[string]} [words] The 2 words to animate between. * @param {AnimationOptions} [animationOptions] Timing options for when to start, how fast to animate forwards, backwards, and when to loop. - * @param {FontToObserve} [fontToObserve] A description of an embedded font to observe and wait until loaded. + * @param {string} [fontToObserve] A description of an embedded font to observe and wait until loaded. * @returns {JSX.Element|null} */ function Loader(_ref) { @@ -37,10 +29,9 @@ function Loader(_ref) { animationOptions = {}, fontToObserve } = _ref; - const animOptions = _objectSpread(_objectSpread({}, _constants.DEFAULT_ANIMATION_OPTIONS), animationOptions); - const isFontLoaded = (0, _useFontFaceObserver.default)(fontToObserve ? [fontToObserve] : []); + const isFontLoaded = (0, _useFonts.default)(fontToObserve); return isFontLoaded ? /*#__PURE__*/_react.default.createElement(_Anagram.default, { words: words, - animationOptions: animOptions + animationOptions: _objectSpread(_objectSpread({}, _constants.DEFAULT_ANIMATION_OPTIONS), animationOptions) }) : null; } \ No newline at end of file diff --git a/dist/components/useFonts.js b/dist/components/useFonts.js new file mode 100644 index 0000000..e231c37 --- /dev/null +++ b/dist/components/useFonts.js @@ -0,0 +1,29 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = useFonts; +require("core-js/modules/web.dom-collections.iterator.js"); +require("core-js/modules/es.promise.js"); +var _react = require("react"); +function useFonts() { + for (var _len = arguments.length, fontNames = new Array(_len), _key = 0; _key < _len; _key++) { + fontNames[_key] = arguments[_key]; + } + const [isLoaded, setIsLoaded] = (0, _react.useState)(false); + (0, _react.useEffect)(() => { + // Inspired by https://stackoverflow.com/a/60138011 + if (!document || !document.fonts) { + // eslint-disable-next-line no-console + console.warn('Browser does not support document.fonts API'); + setIsLoaded(true); + return; + } + Promise.all(fontNames.map(fontName => document.fonts.load("16px \"".concat(fontName, "\"")))).then(() => { + setIsLoaded(true); + }); + }, [fontNames]); + return isLoaded; +} +; \ No newline at end of file diff --git a/dist/utils.js b/dist/utils.js index 5e9679c..7d37df2 100644 --- a/dist/utils.js +++ b/dist/utils.js @@ -9,7 +9,6 @@ exports.uuidv4 = uuidv4; require("core-js/modules/es.regexp.exec.js"); require("core-js/modules/es.string.replace.js"); require("core-js/modules/es.array.sort.js"); -require("core-js/modules/es.string.split.js"); require("core-js/modules/es.regexp.to-string.js"); function isAnagram(stringA, stringB) { // Sanitizing diff --git a/package-lock.json b/package-lock.json index 621e61e..e3e8257 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react-anagram-animation", - "version": "1.3.1", + "version": "1.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6293,11 +6293,6 @@ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, - "fontfaceobserver": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.1.0.tgz", - "integrity": "sha512-ReOsO2F66jUa0jmv2nlM/s1MiutJx/srhAe2+TE8dJCMi02ZZOcCTxTCQFr3Yet+uODUtnr4Mewg+tNQ+4V1Ng==" - }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -9186,7 +9181,8 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "js-yaml": { "version": "3.14.1", @@ -9448,6 +9444,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -9844,7 +9841,8 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true }, "object-hash": { "version": "3.0.0", @@ -11284,6 +11282,23 @@ "dev": true, "requires": { "loose-envify": "^1.1.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + } } }, "react-app-polyfill": { @@ -11444,6 +11459,32 @@ "requires": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0" + } + } } }, "react-error-overlay": { @@ -12018,15 +12059,6 @@ "xmlchars": "^2.2.0" } }, - "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dev": true, - "requires": { - "loose-envify": "^1.1.0" - } - }, "schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -13075,26 +13107,6 @@ "requires-port": "^1.0.0" } }, - "use-font-face-observer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/use-font-face-observer/-/use-font-face-observer-1.2.1.tgz", - "integrity": "sha512-5ieKTMvtUux0l7YoOEz842djfgMH3oVg+tO13E/kyS+gGRLDyfAMmRv0D3fzM7UdFag1kz+3AQIFLkkfEF3TUg==", - "requires": { - "fontfaceobserver": "2.1.0", - "react": "17.0.2" - }, - "dependencies": { - "react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - } - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index e32ae94..856d4f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-anagram-animation", - "version": "1.3.2", + "version": "1.4.0", "author": "Scott Canoni", "description": "A React component to use CSS animations to swap letters in 2 words which are an anagram of each other. The text is animated in position after calculating initial and final positions of each letter.", "license": "WTFPL", @@ -30,8 +30,7 @@ "rearrange" ], "dependencies": { - "core-js": "^3.27.2", - "use-font-face-observer": "^1.2.1" + "core-js": "^3.27.2" }, "peerDependencies": { "react": "^17.0.2 || ^18.2.0", diff --git a/src/index.js b/src/index.js index c6fb886..03cd494 100644 --- a/src/index.js +++ b/src/index.js @@ -10,14 +10,14 @@ root.render(

React Anagram Animation

Demo

- +



- + }} fontToObserve="Open Sans" />



- + +
+
+
+
+ + , ); diff --git a/src/lib/components/Anagram.jsx b/src/lib/components/Anagram.jsx index f06a5a7..22b4c53 100644 --- a/src/lib/components/Anagram.jsx +++ b/src/lib/components/Anagram.jsx @@ -21,7 +21,7 @@ export default function Anagram({ words, animationOptions }) { newState[i] = { ...prevState[i], ...update, - } + }; return newState; }); @@ -46,7 +46,7 @@ export default function Anagram({ words, animationOptions }) { // Find a matching dest character to execute the swap with const destLetterIndex = [...words[1]].findIndex((destLetter, srcIndex) => { return destLetter.toLowerCase() === letter.toLowerCase() - && destLettersPairedByIndex[srcIndex] !== true; + && destLettersPairedByIndex[srcIndex] !== true; }); destLettersPairedByIndex[destLetterIndex] = true; // mark this source paired/used @@ -65,7 +65,6 @@ export default function Anagram({ words, animationOptions }) { element: lettersRefs1.current[i].current, offsetLeft: lettersRefs1.current[i].current.offsetLeft, offsetTop: lettersRefs1.current[i].current.offsetTop, - // rect: lettersRefs1.current[i].current.getBoundingClientRect(), }, // the destination location and letter dest: { @@ -73,7 +72,6 @@ export default function Anagram({ words, animationOptions }) { element: lettersRefs2.current[destLetterIndex].current, offsetLeft: lettersRefs2.current[destLetterIndex].current.offsetLeft, offsetTop: lettersRefs2.current[destLetterIndex].current.offsetTop, - // rect: lettersRefs2.current[destLetterIndex].current.getBoundingClientRect(), }, }; swaps.push(swap); @@ -114,7 +112,6 @@ export default function Anagram({ words, animationOptions }) {
{ [...words[0]].map((letter, i) => { - // eslint-disable-next-line react/no-array-index-key return {letter}; }) } @@ -122,14 +119,13 @@ export default function Anagram({ words, animationOptions }) {
{ [...words[1]].map((letter, i) => { - // eslint-disable-next-line react/no-array-index-key return {letter}; }) }
{ - swapAnimations.map((renderedLetter, i) => { + swapAnimations.map((renderedLetter) => { const { id, letter, playing, src, dest } = renderedLetter; const letterStyles = { transition: `left ${transitionDuration}ms ${timingFunction}, top ${transitionDuration}ms ${timingFunction}` }; diff --git a/src/lib/components/constants.js b/src/lib/components/constants.js index a0569e4..63da884 100644 --- a/src/lib/components/constants.js +++ b/src/lib/components/constants.js @@ -2,12 +2,14 @@ export const DEFAULT_WORDS = ['React Anagram Animation', 'Magenta Raincoat Airma /** * @typedef AnimationOptions Timing options for when to start, how fast to animate forwards, backwards, and when to loop. - * @property {number} randomStartMin The minimum amount of time to randomly wait before starting to animate each letter - * @property {number} randomStartMax The maximum amount of time to randomly wait before starting to animate each letter - * @property {number} randomReverseMin The minimum amount of time to randomly wait before starting to animate each letter in reverse - * @property {number} randomReverseMax The maximum amount of time to randomly wait before starting to animate each letter in reverse - * @property {number} loopAnimation The amount of time for each full loop of the animation - * @property {number} waitToStart The amount of time to wait before beginning the animation on start up + * @property {number} randomStartMin The minimum amount of time to randomly wait before starting to animate each letter. + * @property {number} randomStartMax The maximum amount of time to randomly wait before starting to animate each letter. Should be `>= randomStartMin`. + * @property {number} randomReverseMin The minimum amount of time to randomly wait before starting to animate each letter in reverse. + * @property {number} randomReverseMax The maximum amount of time to randomly wait before starting to animate each letter in reverse. Should be `>= randomReverseMin`. + * @property {number} loopAnimation The amount of time to wait before starting the next full loop of the animation. Should be `>= randomReverseMax + transitionDuration`. + * @property {number} waitToStart The amount of time to wait before beginning the animation on start up the first time. + * @property {number} transitionDuration How long should it take for a letter to move to its next position. Should be `<= randomReverseMin - randomStartMax`. + * @property {string} timingFunction What [timing function](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function) should be used for the animation. */ /** @type AnimationOptions */ @@ -18,6 +20,6 @@ export const DEFAULT_ANIMATION_OPTIONS = { randomReverseMax: 9000, loopAnimation: 12000, waitToStart: 0, - transitionDuration: 2000, + transitionDuration: 1000, timingFunction: 'ease-in-out', }; diff --git a/src/lib/components/index.jsx b/src/lib/components/index.jsx index 5695ffc..458aaff 100644 --- a/src/lib/components/index.jsx +++ b/src/lib/components/index.jsx @@ -1,27 +1,19 @@ import React from 'react'; -import useFontFaceObserver from 'use-font-face-observer'; +import useFonts from './useFonts'; import Anagram from './Anagram'; import { DEFAULT_ANIMATION_OPTIONS, DEFAULT_WORDS } from './constants'; import './index.css'; -/** - * @typedef FontToObserve A description of an embedded font to observe and wait until loaded. - * @property {string} [family] The font-family: Roboto, Inter, Open Sans, etc - * @property {string|number} [weight] The font-weight: normal, bold, 800, etc - * @property {string} [style] The font-style: normal, italic, oblique - * @property {string} [stretch] The font stretch: normal, condensed, expanded, etc - */ - /** * Render and animate from one word to another word and back again. * @param {[string]} [words] The 2 words to animate between. * @param {AnimationOptions} [animationOptions] Timing options for when to start, how fast to animate forwards, backwards, and when to loop. - * @param {FontToObserve} [fontToObserve] A description of an embedded font to observe and wait until loaded. + * @param {string} [fontToObserve] A description of an embedded font to observe and wait until loaded. * @returns {JSX.Element|null} */ -export default function Loader ({ words = DEFAULT_WORDS, animationOptions = {}, fontToObserve }) { - const isFontLoaded = useFontFaceObserver(fontToObserve ? [fontToObserve] : []); +export default function Loader({ words = DEFAULT_WORDS, animationOptions = {}, fontToObserve }) { + const isFontLoaded = useFonts(fontToObserve); return isFontLoaded ? { + // Inspired by https://stackoverflow.com/a/60138011 + if (!document || !document.fonts) { + // eslint-disable-next-line no-console + console.warn('Browser does not support document.fonts API'); + setIsLoaded(true); + + return; + } + + Promise.all(fontNames.map((fontName) => document.fonts.load(`16px "${fontName}"`))).then(() => { + setIsLoaded(true); + }); + }, [fontNames]); + + return isLoaded; +}; diff --git a/yarn.lock b/yarn.lock index abf67dc..a44a5c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5347,11 +5347,6 @@ follow-redirects@^1.0.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== -fontfaceobserver@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fontfaceobserver/-/fontfaceobserver-2.1.0.tgz#e2705d293e2c585a6531c2a722905657317a2991" - integrity sha512-ReOsO2F66jUa0jmv2nlM/s1MiutJx/srhAe2+TE8dJCMi02ZZOcCTxTCQFr3Yet+uODUtnr4Mewg+tNQ+4V1Ng== - for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -8655,14 +8650,6 @@ react-scripts@5.0.1: optionalDependencies: fsevents "^2.3.2" -react@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -9971,14 +9958,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -use-font-face-observer@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/use-font-face-observer/-/use-font-face-observer-1.2.1.tgz#2b33a389b82b48e2744f439abc1d5d6201fc099d" - integrity sha512-5ieKTMvtUux0l7YoOEz842djfgMH3oVg+tO13E/kyS+gGRLDyfAMmRv0D3fzM7UdFag1kz+3AQIFLkkfEF3TUg== - dependencies: - fontfaceobserver "2.1.0" - react "17.0.2" - util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"