I've always wanted to make that nice, satisfying ripple effect that you can see on google's apps and stuff. It is just so satisfying:
Here's what I wanted out of my ripple effect:
I also wanted to contain the ripple effect in the button element (or any other element that I wanted to have the effect), so that means that the element has to have overflow: hidden
in its CSS. Also meaning that the ripple element has to be a child of the element that was clicked.
This was a BIG headache. I needed to get the position for the ripple but I wanted it to scroll with the page. I also needed to take into account the mouse position when clicking. To do this I used getBoundingClientRect()
. That seemed to work pretty well, but there were still a lot of bugs.
I'm just gonna skim over the bug fixing part, basically the ripple's weren't positioned right, they didn't transition, you couldn't click multiple times and have multiple ripples, and the ripple elements didn't go away. Big problems.
function ripple(el, opts = {}) {
const time = opts.time || (+el.getAttribute("data-time") || 1000) * 3;
const color = opts.color || el.getAttribute("data-color") || "currentColor";
const opacity = opts.opacity || el.getAttribute("data-opacity") || ".3";
const event = opts.event || el.getAttribute("data-event") || "click";
el.style.overflow = "hidden";
el.style.position = "relative";
el.addEventListener(event, (e) => {
var ripple_div = document.createElement("DIV");
ripple_div.style.position = "absolute";
ripple_div.style.background = `${color}`;
ripple_div.style.borderRadius = "50%";
var bx = el.getBoundingClientRect();
var largestdemensions;
if (bx.width > bx.height) {
largestdemensions = bx.width * 3;
} else {
largestdemensions = bx.height * 3;
}
ripple_div.style.pointerEvents = "none";
ripple_div.style.height = `${largestdemensions}px`;
ripple_div.style.width = `${largestdemensions}px`;
ripple_div.style.transform = `translate(-50%, -50%) scale(0)`;
ripple_div.style.top = `${e.pageY - (bx.top + window.scrollY)}px`;
ripple_div.style.left = `${e.pageX - (bx.left + window.scrollX)}px`;
ripple_div.style.transition = `opacity ${time}ms ease, transform ${time}ms ease`;
ripple_div.removeAttribute("data-ripple");
ripple_div.style.opacity = opacity;
el.appendChild(ripple_div);
setTimeout(() => {
ripple_div.style.transform = `translate(-50%, -50%) scale(1)`;
ripple_div.style.opacity = "0";
setTimeout(() => {
ripple_div.remove();
}, time);
}, 1);
});
}
I hosted the code on GitHub so that I could keep track of versions and use a CDN to import it, but it's pretty simple to use the code! Just call ripple(element)
when you have an element in the DOM that's similar to this:
<button onload="ripple(this)">Ripple button</button>
Nowww we get this:
Like I said earlier, we want it to be customizeable, where is the customization?! Turns out I added attributes to control the ripples, here they are!
Attribute | What it does | Default |
---|---|---|
data-ripple | Turns on the ripple effect | - |
data-time | The time in milliseconds that the ripple effect takes. | 500 |
data-color | The color of the ripple. | currentColor |
data-opacity | The opacity of the ripple effect. | .3 |
data-event | The event to ripple on. | mousedown |
So yeah, have fun! I hope that this blog post has been useful, or at least somewhat interesting! Feel free to use the ripple effect (with credit ofc) and keep coding!