Skip to content

Instantly share code, notes, and snippets.

@betafcc
Last active August 7, 2023 22:47
Show Gist options
  • Save betafcc/c5d7a5516c56f8b875584f302605d704 to your computer and use it in GitHub Desktop.
Save betafcc/c5d7a5516c56f8b875584f302605d704 to your computer and use it in GitHub Desktop.
export const ripple = (
event: { currentTarget: HTMLElement; clientX: number; clientY: number },
variant: "light" | "dark" = "light",
duration: number = 500
) => {
const element = event.currentTarget
element.style.position = "relative"
element.style.overflow = "hidden"
const rect = element.getBoundingClientRect()
const radius = Math.hypot(
(event.clientX - rect.left > element.offsetWidth / 2
? 0
: element.offsetWidth) -
(event.clientX - rect.left),
(event.clientY - rect.top > element.offsetHeight / 2
? 0
: element.offsetHeight) -
(event.clientY - rect.top)
)
const circle = document.createElement("span")
Object.assign(circle.style, {
backgroundColor: {
light: "rgba(255,255,255, 0.3)",
dark: "rgba(0,0,0, 0.2)",
}[variant],
borderRadius: "50%",
pointerEvents: "none",
position: "absolute",
left: event.clientX - rect.left - radius + "px",
top: event.clientY - rect.top - radius + "px",
width: radius * 2 + "px",
height: radius * 2 + "px",
})
circle.animate(
[
{ transform: "scale(0)", opacity: 1 },
{ transform: "scale(1.5)", opacity: 0 },
],
{ duration, easing: "linear" }
)
element.appendChild(circle)
setTimeout(() => circle.remove(), duration)
}
import { ComponentProps, ComponentRef, forwardRef } from "react"
import { Slot } from "@radix-ui/react-slot"
import { cn } from "@/lib/util"
const variants = {
light: "rgba(255,255,255, 0.3)",
dark: "rgba(0,0,0, 0.2)",
} as const
const handlers = {
mousedown: "onMouseDown",
mouseup: "onMouseUp",
} as const
export const Ripple = forwardRef<
ComponentRef<typeof Slot>,
ComponentProps<typeof Slot> & {
event?: keyof typeof handlers
variant?: keyof typeof variants
duration?: number
}
>(function Ripple(
{
event = "mousedown",
variant = "light",
duration = 500,
className,
...props
},
ref
) {
const handler = handlers[event]
return (
<Slot
ref={ref}
className={cn("relative overflow-hidden", className)}
{...{
...props,
[handler]: (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
ripple(event, variants[variant], duration)
return props[handler]?.(event)
},
}}
/>
)
})
export const ripple = (
event: { currentTarget: HTMLElement; clientX: number; clientY: number },
color: CSSStyleDeclaration["backgroundColor"],
duration: number
) => {
const element = event.currentTarget
// element.style.position = "relative"
// element.style.overflow = "hidden"
const rect = element.getBoundingClientRect()
const radius = Math.hypot(
(event.clientX - rect.left > element.offsetWidth / 2
? 0
: element.offsetWidth) -
(event.clientX - rect.left),
(event.clientY - rect.top > element.offsetHeight / 2
? 0
: element.offsetHeight) -
(event.clientY - rect.top)
)
const circle = document.createElement("span")
Object.assign(circle.style, {
backgroundColor: color,
borderRadius: "50%",
pointerEvents: "none",
position: "absolute",
left: event.clientX - rect.left - radius + "px",
top: event.clientY - rect.top - radius + "px",
width: radius * 2 + "px",
height: radius * 2 + "px",
})
circle.animate(
[
{ transform: "scale(0)", opacity: 1 },
{ transform: "scale(1.5)", opacity: 0 },
],
{ duration, easing: "linear" }
)
element.appendChild(circle)
setTimeout(() => circle.remove(), duration)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment