v12-ui
Version:
A React component library with a focus on utility-first design and accessibility.
122 lines (121 loc) • 4.85 kB
JavaScript
import { jsx as v } from "react/jsx-runtime";
import { useRef as g, useEffect as u } from "react";
import { useDataTheme as m } from "../Hooks/useDataTheme.js";
class y {
canvas;
ctx;
particles = [];
mouse = null;
animationId = null;
dpr;
config;
constructor(t, i) {
this.canvas = t;
const e = t.getContext("2d");
if (!e) throw new Error("No se pudo obtener el contexto 2D del canvas");
this.ctx = e, this.dpr = window.devicePixelRatio || 1, this.config = i, this.setupCanvas(), this.generateParticles(), this.animate = this.animate.bind(this), this.animate();
}
setupCanvas() {
const t = this.canvas.getBoundingClientRect();
this.canvas.width = t.width * this.dpr, this.canvas.height = t.height * this.dpr, this.canvas.style.width = `${t.width}px`, this.canvas.style.height = `${t.height}px`, this.ctx.scale(this.dpr, this.dpr), this.ctx.imageSmoothingEnabled = !0, this.ctx.imageSmoothingQuality && (this.ctx.imageSmoothingQuality = "high");
}
createTextMask() {
return this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height), this.ctx.font = `bold ${this.config.fontSize || 80}px ${this.config.fontFamily || "sans-serif"}`, this.ctx.textAlign = "center", this.ctx.textBaseline = "middle", this.ctx.fillStyle = `${this.config.textColor || "#fff"}`, this.ctx.fillText(this.config.text, this.canvas.width / 2, this.canvas.height / 2), this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
}
extractPositions(t) {
const i = [], e = t.width * t.height, n = Math.max(1, Math.floor(Math.sqrt(e / this.config.particles)));
for (let h = 0; h < t.height; h += n)
for (let s = 0; s < t.width; s += n) {
const a = (h * t.width + s) * 4;
t.data[a + 3] > 128 && i.push({ x: s, y: h });
}
return i;
}
generateParticles() {
const t = this.createTextMask(), e = this.extractPositions(t).sort(() => Math.random() - 0.5);
this.particles = e.slice(0, Math.min(this.config.particles, e.length)).map((n) => ({
tx: n.x,
ty: n.y,
x: Math.random() * this.canvas.width,
y: Math.random() * this.canvas.height,
vx: 0,
vy: 0
}));
}
updateParticle(t) {
const i = t.tx - t.x, e = t.ty - t.y;
if (t.vx += i * this.config.returnSpeed, t.vy += e * this.config.returnSpeed, t.vx *= this.config.friction, t.vy *= this.config.friction, t.x += t.vx, t.y += t.vy, this.mouse) {
const n = this.mouse.x - t.x, h = this.mouse.y - t.y, s = Math.hypot(n, h);
if (s < this.config.repulsion && s > 0) {
const a = (this.config.repulsion - s) / this.config.repulsion, o = 10;
t.vx -= n / s * a * o, t.vy -= h / s * a * o;
}
t.vx += i * this.config.returnSpeed * 0.5, t.vy += e * this.config.returnSpeed * 0.5;
} else
t.vx += i * this.config.returnSpeed, t.vy += e * this.config.returnSpeed;
}
drawParticles() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
for (const t of this.particles)
this.updateParticle(t), this.ctx.fillStyle = `${this.config.textColor || "#fff"}`, this.ctx.beginPath(), this.ctx.arc(t.x, t.y, this.config.dotSize, 0, Math.PI * 2), this.ctx.fill();
}
handleMouseMove = (t) => {
const i = this.canvas.getBoundingClientRect();
this.mouse = {
x: (t.clientX - i.left) * (this.canvas.width / i.width),
y: (t.clientY - i.top) * (this.canvas.height / i.height)
};
};
handleMouseLeave = () => {
this.mouse = null;
};
animate() {
this.drawParticles(), this.canvas.addEventListener("mousemove", this.handleMouseMove), this.canvas.addEventListener("mouseleave", this.handleMouseLeave), this.animationId = requestAnimationFrame(this.animate);
}
destroy() {
this.canvas.removeEventListener("mousemove", this.handleMouseMove), this.canvas.removeEventListener("mouseleave", this.handleMouseLeave), this.animationId && cancelAnimationFrame(this.animationId);
}
}
function P({
text: c = "Magic Text",
particles: t = 1e5,
dotSize: i = 1.5,
repulsion: e = 100,
friction: n = 0.82,
returnSpeed: h = 0.01,
fontFamily: s = "sans-serif",
fontSize: a = 80,
textColor: o
}) {
const { theme: r } = m(), l = o || (r === "dark" ? "#fff" : "#000");
console.log("Theme:", r, "Color detected:", l);
const d = g(null);
return u(() => {
const f = d.current;
if (!f) return;
const x = new y(f, {
text: c,
particles: t,
dotSize: i,
repulsion: e,
friction: n,
returnSpeed: h,
fontFamily: s,
fontSize: a,
textColor: l
});
return () => {
x && x.destroy();
};
}, [c, t, i, e, n, h, s, a, r, o]), /* @__PURE__ */ v(
"canvas",
{
ref: d,
className: "block mx-auto",
style: { width: "100%", height: "100%" }
}
);
}
export {
P as MagicText
};