Files
rio/frontend/code/rippleEffect.ts
2024-09-26 19:37:51 +02:00

103 lines
3.0 KiB
TypeScript

import { commitCss } from "./utils";
class RippleEffectOptions {
rippleDuration?: number;
rippleCssColor?: string;
triggerOnPress?: boolean;
customCss?: { [attr: string]: string };
}
export class RippleEffect {
public customCss: { [attr: string]: string };
private element: HTMLElement;
private rippleDuration: number;
private rippleCssColor: string;
private boundEventHandler: (event: MouseEvent) => void;
constructor(
element: HTMLElement,
{
rippleDuration = 0.9,
rippleCssColor = "var(--rio-local-text-color)",
triggerOnPress = true,
customCss = {},
}: RippleEffectOptions = {}
) {
this.element = element;
this.rippleDuration = rippleDuration;
this.rippleCssColor = rippleCssColor;
this.customCss = customCss;
// Subscribe to events
if (triggerOnPress) {
this.boundEventHandler = this.trigger.bind(this);
this.element.addEventListener("click", this.boundEventHandler);
}
}
destroy() {
if (this.boundEventHandler !== undefined) {
this.element.removeEventListener("click", this.boundEventHandler);
}
}
trigger(event: MouseEvent) {
// Find the ripple's origin
const rect = this.element.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
// Spawn two elements: one for the animation, and one with `overflow:
// hidden`
let rippleContainer = document.createElement("div");
rippleContainer.classList.add(
"rio-ripple-container",
"rio-not-a-child-component"
);
rippleContainer.style.setProperty(
"--rio-ripple-color",
this.rippleCssColor
);
rippleContainer.style.setProperty(
"--rio-ripple-duration",
`${this.rippleDuration}s`
);
Object.assign(rippleContainer.style, this.customCss);
this.element.appendChild(rippleContainer);
let rippleElement = document.createElement("div");
rippleElement.classList.add("rio-ripple-effect");
rippleContainer.appendChild(rippleElement);
// Position it
rippleElement.style.top = `${y}px`;
rippleElement.style.left = `${x}px`;
rippleElement.style.width = "0px";
rippleElement.style.height = "0px";
rippleElement.style.opacity = "0.1";
// Commit CSS
commitCss(rippleElement);
// Animate it
rippleElement.style.top = `${rect.height / 2}px`;
rippleElement.style.left = `${rect.width / 2}px`;
let size = Math.max(rect.width, rect.height) * 2;
rippleElement.style.width = `${size}px`;
rippleElement.style.height = `${size}px`;
rippleElement.style.opacity = "0";
// Remove the ripple element after the animation
setTimeout(() => {
rippleContainer.remove();
}, this.rippleDuration * 1000);
}
}