UNPKG

@frontplus-ui/use-ripple

Version:

This hook is used to add ripple effect in all elements except void elements

69 lines (67 loc) 2.14 kB
"use client"; // src/index.ts import { useCallback, useRef } from "react"; function useRipple(props = { duration: 500, timingFunction: "cubic-bezier(.42,.36,.28,.88)", disabled: false, completedFactor: 0.5 }) { const ref = useRef(null); const { disabled, duration, completedFactor } = props; const event = useCallback((event2) => { if (!ref.current || disabled || event2.button !== 0) return; const target = ref.current; requestAnimationFrame(() => { const begun = Date.now(); const ripple = createRipple(target, event2, props); target.appendChild(ripple); const removeRipple = () => { const now = Date.now(); const diff = now - begun; setTimeout( () => { ripple.style.opacity = "0"; ripple.addEventListener("transitionend", (e) => { if (e.propertyName === "opacity") ripple.remove(); }); }, diff > completedFactor * duration ? 0 : completedFactor * duration - diff ); }; document.addEventListener("pointerup", removeRipple, { once: true }); }); }, []); return [ref, event]; } var createRipple = (target, event, options) => { const { clientX, clientY } = event; const { height, width, top, left } = target.getBoundingClientRect(); const maxHeight = Math.max(clientY - top, height - clientY + top); const maxWidth = Math.max(clientX - left, width - clientX + left); const size = Math.hypot(maxHeight, maxWidth) * 2; const element = document.createElement("span"); const { duration, timingFunction } = options; element.style.cssText = ` position: absolute; top: ${event.clientY - top}px; left: ${event.clientX - left}px; height: ${size}px; width: ${size}px; translate: -50% -50%; pointer-events: none; border-radius: 50%; background-color: var(--rippleBg); scale: 0; transition: scale ${duration}ms ${timingFunction}, opacity ${duration * 0.7}ms ease-in-out; `; requestAnimationFrame(() => { element.style.scale = "1"; }); return element; }; export { useRipple };