Superficie de agua con ondas
Cada clic lanza anillos concéntricos que se expanden y se desvanecen; Enter o Espacio la activan desde el teclado.
Toca el aguaHaz clic donde quieras
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
import { useRef, useState, type CSSProperties, type KeyboardEvent, type PointerEvent } from "react";
/** Superficie de agua: cada clic o toque lanza anillos concéntricos que se expanden y se desvanecen, y el cuerpo ondula. Con Enter o Espacio se lanza una onda desde el centro. Solo SVG y CSS. React + TypeScript, sin dependencias. */
export type EfectoOndasProps = {
className?: string;
style?: CSSProperties;
};
const CSS = `
.gceon-root { box-sizing: border-box; position: relative; width: 100%; max-width: 640px; height: 320px; overflow: hidden; border-radius: 20px; background: radial-gradient(120% 120% at 30% 20%, #0f5f8f, #072a4a 70%); color: #d7f0ff; font: 500 15px/1.4 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; display: grid; place-items: center; cursor: pointer; user-select: none; outline: 0; touch-action: manipulation; }
.gceon-root *, .gceon-root *::before, .gceon-root *::after { box-sizing: border-box; }
.gceon-root:focus-visible { box-shadow: inset 0 0 0 3px #7fd0ff; }
.gceon-ring { position: absolute; width: 20px; height: 20px; margin: -10px 0 0 -10px; border: 2px solid rgba(190, 235, 255, .9); border-radius: 50%; pointer-events: none; animation: gceon-grow 1.8s cubic-bezier(.16, .6, .3, 1) forwards; animation-delay: var(--d, 0s); opacity: 0; }
.gceon-hint { position: relative; pointer-events: none; text-align: center; opacity: .8; }
.gceon-hint b { display: block; font: 600 20px/1.2 ui-sans-serif, sans-serif; color: #fff; }
.gceon-shine { position: absolute; inset: 0; pointer-events: none; background: repeating-radial-gradient(circle at 70% 120%, transparent 0 14px, rgba(255, 255, 255, .03) 14px 16px); }
@keyframes gceon-grow { 0% { transform: scale(.4); opacity: 0; } 10% { opacity: 1; } 100% { transform: scale(26); opacity: 0; border-width: .4px; } }
@media (prefers-reduced-motion: reduce) { .gceon-ring { animation: gceon-fade 1.2s ease-out forwards; } @keyframes gceon-fade { 0% { opacity: .9; transform: scale(3); } 100% { opacity: 0; transform: scale(3); } } }`;
type Ring = { id: number; x: number; y: number };
export function EfectoOndas({ className = "", style }: EfectoOndasProps) {
const [rings, setRings] = useState<Ring[]>([]);
const id = useRef(0);
const root = useRef<HTMLDivElement>(null);
function drop(x: number, y: number) {
const batch: Ring[] = [0, 1, 2].map(() => ({ id: ++id.current, x, y }));
setRings((list) => [...list.slice(-12), ...batch]);
window.setTimeout(() => setRings((list) => list.filter((r) => !batch.some((b) => b.id === r.id))), 2400);
}
function click(event: PointerEvent<HTMLDivElement>) {
const rect = event.currentTarget.getBoundingClientRect();
drop(event.clientX - rect.left, event.clientY - rect.top);
}
function key(event: KeyboardEvent) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
const rect = root.current!.getBoundingClientRect();
drop(rect.width / 2, rect.height / 2);
}
}
return (
<div ref={root} className={`gceon-root ${className}`} style={style} role="button" tabIndex={0} aria-label="Superficie de agua. Pulsa para crear ondas" onPointerDown={click} onKeyDown={key}>
<style>{CSS}</style>
<div className="gceon-shine" aria-hidden="true" />
{rings.map((ring, i) => (
<span key={ring.id} className="gceon-ring" aria-hidden="true" style={{ left: ring.x, top: ring.y, "--d": `${(i % 3) * 0.16}s` } as CSSProperties} />
))}
<p className="gceon-hint">
<b>Toca el agua</b>
Haz clic donde quieras
</p>
</div>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/efecto-ondasEjemplo de uso de Superficie de agua con ondas
Después de copiar efecto-ondas.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { EfectoOndas } from "./efecto-ondas";
export default function Ejemplo() {
return <EfectoOndas />;
}Propiedades de EfectoOndas
EfectoOndas acepta 2 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Personaliza Superficie de agua con ondas con variables CSS
Define estas variables en el contenedor o en tu hoja de estilos para cambiar los colores sin tocar el componente. Si no las defines, se usa el valor por defecto.
| Variable | Valor por defecto |
|---|---|
--d | 0s |
Accesibilidad y rendimiento
El archivo tiene 60 líneas (4 KB) y define 2 animaciones con @keyframes. Esto es lo que hace Superficie de agua con ondas por quien lo usa:
- Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
- Muestra un anillo de foco visible al navegar con teclado (:focus-visible).
- Declara roles ARIA para lectores de pantalla: button.
- Responde al teclado, no solo al ratón.
- Es un componente de cliente de Next.js: incluye la directiva "use client".