UNPKG

react-magical-typewriter

Version:

A customizable typewriter effect component for React with GSAP animations

624 lines (623 loc) 23 kB
import se, { useState as K, useRef as ee, useMemo as ae, useEffect as W } from "react"; import { gsap as l } from "gsap"; var q = { exports: {} }, M = {}; /** * @license React * react-jsx-runtime.production.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var te; function ie() { if (te) return M; te = 1; var m = Symbol.for("react.transitional.element"), P = Symbol.for("react.fragment"); function x(_, R, r) { var g = null; if (r !== void 0 && (g = "" + r), R.key !== void 0 && (g = "" + R.key), "key" in R) { r = {}; for (var C in R) C !== "key" && (r[C] = R[C]); } else r = R; return R = r.ref, { $$typeof: m, type: _, key: g, ref: R !== void 0 ? R : null, props: r }; } return M.Fragment = P, M.jsx = x, M.jsxs = x, M; } var F = {}; /** * @license React * react-jsx-runtime.development.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var re; function le() { return re || (re = 1, process.env.NODE_ENV !== "production" && function() { function m(e) { if (e == null) return null; if (typeof e == "function") return e.$$typeof === t ? null : e.displayName || e.name || null; if (typeof e == "string") return e; switch (e) { case j: return "Fragment"; case k: return "Profiler"; case G: return "StrictMode"; case U: return "Suspense"; case V: return "SuspenseList"; case a: return "Activity"; } if (typeof e == "object") switch (typeof e.tag == "number" && console.error( "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue." ), e.$$typeof) { case A: return "Portal"; case B: return (e.displayName || "Context") + ".Provider"; case S: return (e._context.displayName || "Context") + ".Consumer"; case Y: var s = e.render; return e = e.displayName, e || (e = s.displayName || s.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e; case J: return s = e.displayName || null, s !== null ? s : m(e.type) || "Memo"; case L: s = e._payload, e = e._init; try { return m(e(s)); } catch { } } return null; } function P(e) { return "" + e; } function x(e) { try { P(e); var s = !1; } catch { s = !0; } if (s) { s = console; var c = s.error, w = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object"; return c.call( s, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", w ), P(e); } } function _(e) { if (e === j) return "<>"; if (typeof e == "object" && e !== null && e.$$typeof === L) return "<...>"; try { var s = m(e); return s ? "<" + s + ">" : "<...>"; } catch { return "<...>"; } } function R() { var e = p.A; return e === null ? null : e.getOwner(); } function r() { return Error("react-stack-top-frame"); } function g(e) { if (E.call(e, "key")) { var s = Object.getOwnPropertyDescriptor(e, "key").get; if (s && s.isReactWarning) return !1; } return e.key !== void 0; } function C(e, s) { function c() { o || (o = !0, console.error( "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", s )); } c.isReactWarning = !0, Object.defineProperty(e, "key", { get: c, configurable: !0 }); } function O() { var e = m(this.type); return i[e] || (i[e] = !0, console.error( "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release." )), e = this.props.ref, e !== void 0 ? e : null; } function X(e, s, c, w, T, v, H, Z) { return c = v.ref, e = { $$typeof: D, type: e, key: s, props: v, _owner: T }, (c !== void 0 ? c : null) !== null ? Object.defineProperty(e, "ref", { enumerable: !1, get: O }) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", { configurable: !1, enumerable: !1, writable: !0, value: 0 }), Object.defineProperty(e, "_debugInfo", { configurable: !1, enumerable: !1, writable: !0, value: null }), Object.defineProperty(e, "_debugStack", { configurable: !1, enumerable: !1, writable: !0, value: H }), Object.defineProperty(e, "_debugTask", { configurable: !1, enumerable: !1, writable: !0, value: Z }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e; } function I(e, s, c, w, T, v, H, Z) { var y = s.children; if (y !== void 0) if (w) if (u(y)) { for (w = 0; w < y.length; w++) b(y[w]); Object.freeze && Object.freeze(y); } else console.error( "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead." ); else b(y); if (E.call(s, "key")) { y = m(e); var N = Object.keys(s).filter(function(ne) { return ne !== "key"; }); w = 0 < N.length ? "{key: someKey, " + N.join(": ..., ") + ": ...}" : "{key: someKey}", h[y + w] || (N = 0 < N.length ? "{" + N.join(": ..., ") + ": ...}" : "{}", console.error( `A props object containing a "key" prop is being spread into JSX: let props = %s; <%s {...props} /> React keys must be passed directly to JSX without using spread: let props = %s; <%s key={someKey} {...props} />`, w, y, N, y ), h[y + w] = !0); } if (y = null, c !== void 0 && (x(c), y = "" + c), g(s) && (x(s.key), y = "" + s.key), "key" in s) { c = {}; for (var Q in s) Q !== "key" && (c[Q] = s[Q]); } else c = s; return y && C( c, typeof e == "function" ? e.displayName || e.name || "Unknown" : e ), X( e, y, v, T, R(), c, H, Z ); } function b(e) { typeof e == "object" && e !== null && e.$$typeof === D && e._store && (e._store.validated = 1); } var $ = se, D = Symbol.for("react.transitional.element"), A = Symbol.for("react.portal"), j = Symbol.for("react.fragment"), G = Symbol.for("react.strict_mode"), k = Symbol.for("react.profiler"), S = Symbol.for("react.consumer"), B = Symbol.for("react.context"), Y = Symbol.for("react.forward_ref"), U = Symbol.for("react.suspense"), V = Symbol.for("react.suspense_list"), J = Symbol.for("react.memo"), L = Symbol.for("react.lazy"), a = Symbol.for("react.activity"), t = Symbol.for("react.client.reference"), p = $.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, E = Object.prototype.hasOwnProperty, u = Array.isArray, n = console.createTask ? console.createTask : function() { return null; }; $ = { "react-stack-bottom-frame": function(e) { return e(); } }; var o, i = {}, d = $["react-stack-bottom-frame"].bind( $, r )(), f = n(_(r)), h = {}; F.Fragment = j, F.jsx = function(e, s, c, w, T) { var v = 1e4 > p.recentlyCreatedOwnerStacks++; return I( e, s, c, !1, w, T, v ? Error("react-stack-top-frame") : d, v ? n(_(e)) : f ); }, F.jsxs = function(e, s, c, w, T) { var v = 1e4 > p.recentlyCreatedOwnerStacks++; return I( e, s, c, !0, w, T, v ? Error("react-stack-top-frame") : d, v ? n(_(e)) : f ); }; }()), F; } var oe; function ce() { return oe || (oe = 1, process.env.NODE_ENV === "production" ? q.exports = ie() : q.exports = le()), q.exports; } var z = ce(); const ge = ({ text: m, typingSpeed: P = 500, delayAfterTyping: x = 1500, cursorCharacter: _ = "|", cursorBlinkSpeed: R = 500, charAnimationSpeed: r = 0.7, animationStyle: g = "Elegant", ghostTrailColor: C = "rgba(100, 149, 237, 0.7)", cursorInvert: O = "none", onComplete: X = () => { }, className: I = "" }) => { const [b, $] = K([]), [D, A] = K(!0), [j, G] = K(!1), k = ee(null), S = ee([]), B = ae(() => m.split(/(\s+)/).filter(Boolean), [m]); let Y = 0; const U = (a) => { a.querySelectorAll(".ray").forEach((u) => u.remove()); const p = [], E = 16; for (let u = 0; u < E; u++) { const n = document.createElement("div"); n.className = "ray", a.appendChild(n), p.push(n); } return p; }, V = (a, t, p) => { document.querySelectorAll(`.ghost-char-${p}`).forEach((d) => d.remove()); const u = []; if (!k.current) return u; const n = 5, o = a.getBoundingClientRect(), i = k.current.getBoundingClientRect(); for (let d = 0; d < n; d++) { const f = document.createElement("span"); f.textContent = t === " " ? " " : t, f.className = `react-magical-typewriter-ghost ghost-char-${p}`, k.current.appendChild(f), l.set(f, { position: "absolute", top: `${o.top - i.top}px`, left: `${o.left - i.left}px`, width: `${o.width}px`, height: `${o.height}px`, fontSize: window.getComputedStyle(a).fontSize, fontFamily: window.getComputedStyle(a).fontFamily, fontWeight: window.getComputedStyle(a).fontWeight, lineHeight: window.getComputedStyle(a).lineHeight, color: C, opacity: 0.7 - d * 0.15, zIndex: 1, // Should be behind characters in the same container pointerEvents: "none" }), u.push(f); } return u; }, J = (a, t, p) => { document.querySelectorAll(`.fragment-char-${p}`).forEach((f) => f.remove()); const u = []; if (!k.current) return u; const n = 6 + Math.floor(Math.random() * 3), o = a.getBoundingClientRect(), i = k.current.getBoundingClientRect(), d = () => { const f = 3 + Math.floor(Math.random() * 3); let h = "polygon("; for (let e = 0; e < f; e++) h += `${Math.random() * 100}% ${Math.random() * 100}%`, e < f - 1 && (h += ", "); return h += ")", h; }; for (let f = 0; f < n; f++) { const h = document.createElement("span"); h.textContent = t === " " ? " " : t, h.className = `react-magical-typewriter-fragment fragment-char-${p}`, k.current.appendChild(h); const e = (Math.random() - 0.5) * (o.width * 2), s = (Math.random() - 0.5) * (o.height * 2), c = (Math.random() - 0.5) * 360; l.set(h, { position: "absolute", top: `${o.top - i.top + s}px`, left: `${o.left - i.left + e}px`, width: `${o.width * (0.4 + Math.random() * 0.4)}px`, height: `${o.height * (0.4 + Math.random() * 0.4)}px`, fontSize: window.getComputedStyle(a).fontSize, fontFamily: window.getComputedStyle(a).fontFamily, fontWeight: window.getComputedStyle(a).fontWeight, lineHeight: window.getComputedStyle(a).lineHeight, color: window.getComputedStyle(a).color, opacity: 0, rotation: c, scale: 0.3 + Math.random() * 0.4, clipPath: d(), overflow: "hidden", pointerEvents: "none" }), u.push(h); } return u; }; W(() => { let a; return b.length < m.length && (a = setTimeout(() => { $((t) => [ ...t, { char: m[t.length], id: `${t.length}-${Date.now()}` } ]); }, P)), () => clearTimeout(a); }, [b, m, P]), W(() => { if (b.length === m.length && !j) { G(!0); const a = setTimeout(() => X(), x); return () => clearTimeout(a); } }, [b, m, j, x, X]), W(() => { const a = setInterval(() => A((t) => !t), R); return () => clearInterval(a); }, [R]); const L = (a, t) => { a && (S.current[t] = a); }; return W(() => { var u; const a = b.length - 1; if (a < 0 || !S.current[a]) return; const t = S.current[a], p = b[a]; l.killTweensOf(t); const E = typeof g == "object" && !Array.isArray(g); if (E) l.set(t, { display: "inline-block" }), g.from || l.set(t, { opacity: 0 }); else { const n = g, o = n === "Elegant" || n === "LiquidDrip" ? 0 : 1; l.set(t, { display: "inline-block", opacity: o, x: 0, y: 0, scale: 1, rotation: 0, skewX: 0, skewY: 0, clipPath: "none", filter: "none", transformOrigin: "center center" }); } if (E) { const n = g; n.from && n.to ? l.fromTo(t, { ...n.from }, { ...n.to, duration: n.duration || r, ease: n.ease || "power2.out" }) : n.to && l.to(t, { ...n.to, duration: n.duration || r, ease: n.ease || "power2.out" }); } else { const n = g; if (n === "Elegant") l.to(t, { opacity: 1, duration: r, ease: "power2.out" }); else if (n === "Whimsical") { const i = [{ x: -10, y: -10 }, { x: 10, y: -10 }, { x: 10, y: 10 }, { x: -10, y: 10 }][a % 4]; l.fromTo( t, { opacity: 0, x: i.x, y: i.y, rotation: a % 20 - 10 }, { opacity: 1, x: 0, y: 0, rotation: 0, duration: r, ease: "back.out(1.7)" } ); } else if (n === "Landing") l.fromTo(t, { y: "-200%", rotation: -3 }, { y: "0%", rotation: 0, duration: r, ease: "elastic.out(1, 0.7)" }); else if (n === "Arise") l.fromTo(t, { y: "200%", rotation: 3 }, { y: "0%", rotation: 0, duration: r, ease: "power4.out" }); else if (n === "Rift") l.fromTo(t, { scale: 0, rotation: 720 }, { scale: 1, rotation: 0, duration: r, ease: "elastic.out(1, 0.6)" }); else if (n === "Warp") l.fromTo(t, { scaleX: 3, scaleY: 0.1, x: "50%", opacity: 0 }, { scale: 1, x: "0%", opacity: 1, duration: r, ease: "elastic.out(1.1, 0.7)" }); else if (n === "RadialBurst") { const o = U(t); l.set(t, { scale: 0.5, opacity: 0, transformOrigin: "center center" }), l.set(o, { position: "absolute", width: "3px", height: "1px", backgroundColor: "rgba(255, 223, 0, 0.9)", top: "50%", left: "50%", yPercent: -100, transformOrigin: "bottom center", scale: 0, opacity: 0, zIndex: -1 }); const i = l.timeline(); o.forEach((d, f) => { const h = f / o.length * 360, e = 20 + Math.random() * 15; i.to(d, { rotation: h, height: `${e}px`, scale: 1, opacity: 0.4, duration: r * 0.5, ease: "power3.out" }, 0); }), i.to(t, { scale: 1, opacity: 1, duration: r * 0.7, ease: "elastic.out(1.2, 0.5)" }, r * 0.1), i.to(o, { opacity: 0, scale: 0, duration: r * 0.6, ease: "power2.in", // Rays fade a bit longer onComplete: () => o.forEach((d) => d.remove()) }, `-=${r * 0.2}`); } else if (n === "LiquidDrip") l.set(t, { opacity: 0, scaleY: 0.2, y: "-100%", transformOrigin: "center bottom" }), l.timeline().to(t, { y: "-15%", opacity: 1, scaleY: 1.3, duration: r * 0.4, ease: "power2.in" }).to(t, { y: "5%", scaleY: 0.85, scaleX: 1.15, duration: r * 0.25, ease: "power1.out" }).to(t, { y: "0%", scaleY: 1, scaleX: 1, duration: r * 0.35, ease: "elastic.out(1, 0.5)" }); else if (n === "LaserSketch") { l.set(t, { opacity: 1, display: "inline-block", verticalAlign: "middle" }); const o = l.timeline(); o.to(t, { clipPath: "polygon(0% 0%, 100% 0%, 0% 0%, 0% 100%)", duration: r * 0.1, ease: "power1.inOut" }), o.to(t, { clipPath: "polygon(0 0%, 100% 0%, 100% 100%, 0% 100%)", duration: r * 0.7, ease: "power1.inOut" }), o.to(t, { opacity: 0.5, duration: r * 0.9, ease: "power2.in" }), o.to(t, { opacity: 1, duration: r * 0.9, ease: "power2.out" }); } else if (n === "FlipReveal") t.parentElement && l.set(t.parentElement, { perspective: 800 }), l.set(t, { opacity: 0, rotationY: -90, transformOrigin: "center center" }), l.timeline().to(t, { rotationY: 0, opacity: 1, duration: r * 0.8, ease: "back.out(1.5)" }).to(t, { rotationY: 15, duration: r * 0.2, ease: "power1.inOut" }).to(t, { rotationY: 0, duration: r * 0.3, ease: "elastic.out(1, 0.7)" }); else if (n === "PixelGlitch") l.set(t, { opacity: 1, filter: "blur(6px) contrast(0.1) brightness(3)", skewX: "8deg", scale: 1.1 }), l.timeline().to(t, { filter: "blur(3px) contrast(2) brightness(0.8)", skewX: "-6deg", x: 5, duration: r * 0.25, ease: "steps(4)" }).to(t, { filter: "blur(1px) contrast(0.5) brightness(1.5)", skewX: "4deg", x: -3, duration: r * 0.2, ease: "steps(3)" }).to(t, { filter: "blur(0px) contrast(1) brightness(1)", skewX: "0deg", x: 0, scale: 1, duration: r * 0.3, ease: "power2.out" }); else if (n === "GhostTrail") { l.fromTo(t, { opacity: 0, x: -10 }, { opacity: 1, x: 0, duration: r * 0.4, ease: "power2.out" }); const o = V(t, p.char, a); o.length > 0 && l.to(o, { x: (i) => `-=${10 + i * 4}`, y: (i) => `+=${4 + i * 2}`, opacity: 0, duration: r * 1.8, stagger: 0.08, ease: "power1.out", onComplete: () => { o.forEach((i) => i.remove()); } }); } else if (n === "ShatterIn") { const o = J(t, p.char, a); if (l.set(t, { opacity: 0 }), o.length > 0) { const i = t.getBoundingClientRect(), d = ((u = k.current) == null ? void 0 : u.getBoundingClientRect()) || { top: 0, left: 0 }; l.to(o, { top: i.top - d.top, left: i.left - d.left, width: i.width, height: i.height, rotation: 0, scale: 1, opacity: 0.7, // You might want fragments to be fully opaque if they represent the final letter clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", duration: r, stagger: { amount: r * 0.3, from: "random" }, ease: "quad.out", onComplete: () => { l.set(t, { opacity: 1 }), o.forEach((f) => f.remove()); } }); } else l.set(t, { opacity: 1 }); } else if (n === "OrigamiReveal") { const o = [ { origin: "bottom center", scaleProp: "scaleY", skewSign: 1 }, { origin: "top center", scaleProp: "scaleY", skewSign: -1 }, { origin: "center left", scaleProp: "scaleX", skewSign: 1 }, { origin: "center right", scaleProp: "scaleX", skewSign: -1 }, { origin: "top left", scaleProp: "scaleY", skewSign: -1 }, { origin: "top left", scaleProp: "scaleX", skewSign: 1 }, { origin: "top right", scaleProp: "scaleY", skewSign: -1 }, { origin: "top right", scaleProp: "scaleX", skewSign: -1 }, { origin: "bottom left", scaleProp: "scaleY", skewSign: 1 }, { origin: "bottom left", scaleProp: "scaleX", skewSign: 1 }, { origin: "bottom right", scaleProp: "scaleY", skewSign: 1 }, { origin: "bottom right", scaleProp: "scaleX", skewSign: -1 } ], i = o[Math.floor(Math.random() * o.length)], d = i.skewSign * 35, f = i.skewSign * -15, h = i.skewSign * 5, e = { opacity: 1, transformOrigin: i.origin, skewX: `${d}deg`, [i.scaleProp]: 0 // Dynamically sets scaleX: 0 or scaleY: 0 }; l.set(t, e); const s = l.timeline(), c = { skewX: `${f}deg`, duration: r * 0.5, ease: "power3.out", [i.scaleProp]: 1 // Dynamically sets scaleX: 1 or scaleY: 1 }; s.to(t, c).to(t, { skewX: `${h}deg`, duration: r * 0.25, ease: "power1.inOut" }).to(t, { skewX: "0deg", duration: r * 0.25, ease: "elastic.out(1, 0.6)" }); } } }, [b, g, r, m.length]), /* @__PURE__ */ z.jsxs( "span", { ref: k, className: `react-magical-typewriter-container ${g === "Arise" || g === "Landing" || g === "PixelGlitch" || g === "LiquidDrip" ? "clip-path-overflow" : ""} ${I}`, children: [ B.map((a, t) => { const p = [], E = Y; for (let u = 0; u < a.length && Y < b.length; u++) p.push(b[Y]), Y++; return p.length === 0 ? null : /* @__PURE__ */ z.jsx( "span", { className: "typewriter-word-or-space-segment", children: p.map((u, n) => { const o = E + n; return /* @__PURE__ */ z.jsx( "span", { ref: (i) => L(i, o), className: `react-magical-typewriter-char ${typeof g == "string" && ["RadialBurst", "LaserSketch", "ShatterIn"].includes(g) ? `char-${g.toLowerCase()}` : ""} ${g === "GhostTrail" ? "char-ghosttrail-active" : ""}`, children: u.char === " " ? " " : u.char }, u.id ); }) }, `segment-${t}` ); }), /* @__PURE__ */ z.jsx( "span", { className: `react-magical-typewriter-cursor ${O === "horizontal" ? "cursor-invert-horizontal" : O === "vertical" ? "cursor-invert-vertical" : O === "both" ? "cursor-invert-both" : O === "none" ? "" : O}`, style: { opacity: D ? 1 : 0 }, children: _ } ) ] } ); }; export { ge as ReactMagicalTypewriter, ge as default };