@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
JavaScript
"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
};