react-magical-typewriter
Version:
A customizable typewriter effect component for React with GSAP animations
624 lines (623 loc) • 23 kB
JavaScript
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
};