Smoke Card
Smoke Card é um componente de efeito para React e Tailwind CSS, escrito para a NedDev e teu para usares. Copia e cola no teu projeto.
NedDev
effects
O que é
Smoke Card é um componente de efeito para React e Tailwind CSS, escrito para a NedDev e teu para usares. Copia e cola no teu projeto.
Como usar
Abre o componente no catálogo, carrega em copiar, e cola no teu projeto. O prompt copiado leva o código e as regras para o teu agente o reproduzir sem alterar nada.
Cinco cópias grátis por mês. Sem cartão.
Não precisa de pacotes além do que o projeto já tem
Licença
Este componente foi escrito para este catálogo. É teu para usares em trabalho de cliente e em produtos que vendas, nos termos de uso.
Smoke Card
"use client"
import React, { useEffect, useRef } from 'react';
class Particle {
x: number;
y: number;
size: number;
speedX: number;
speedY: number;
life: number;
initialSize: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
this.size = Math.random() * 5 + 2;
this.speedX = Math.random() * 2 - 1;
this.speedY = -Math.random() * 3 - 1;
this.life = 100;
this.initialSize = this.size;
}
update() {
this.x += this.speedX;
this.y += this.speedY;
this.life -= 1;
this.size = Math.max(0, this.initialSize * (this.life / 100));
}
}
const SmokeCard = () => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const particlesRef = useRef<Particle[]>([]);
const mousePosRef = useRef({ x: 0, y: 0 });
const animationFrameRef = useRef<number>();
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
const animate = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
particlesRef.current = particlesRef.current
.filter(particle => particle.life > 0 && particle.size > 0)
.map(particle => {
particle.update();
if (particle.size > 0) {
const opacity = particle.life / 100;
ctx.fillStyle = `rgba(128, 128, 128, ${opacity})`;
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
ctx.fill();
}
return particle;
});
if (mousePosRef.current.x !== 0 && mousePosRef.current.y !== 0) {
for (let i = 0; i < 2; i++) {
particlesRef.current.push(
new Particle(
mousePosRef.current.x + (Math.random() * 10 - 5),
mousePosRef.current.y + (Math.random() * 10 - 5)
)
);
}
}
animationFrameRef.current = requestAnimationFrame(animate);
};
const updateCanvasSize = () => {
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
};
updateCanvasSize();
window.addEventListener('resize', updateCanvasSize);
animate();
return () => {
window.removeEventListener('resize', updateCanvasSize);
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
};
}, []);
const handleMouseMove = (e: React.MouseEvent<HTMLCanvasElement>) => {
const rect = canvasRef.current?.getBoundingClientRect();
if (!rect) return;
mousePosRef.current = {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
};
const handleMouseLeave = () => {
mousePosRef.current = { x: 0, y: 0 };
};
return (
<div className="w-96 h-96 relative overflow-hidden bg-black rounded-lg border border-white/10">
<canvas
ref={canvasRef}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
className="absolute top-0 left-0 w-full h-full cursor-none"
/>
</div>
);
};
export { SmokeCard };