In this tutorial, we’ll create a reusable ripple effect component in React using SCSS. This effect is commonly used in CSS Animation to provide feedback when a user interacts with a button.
We’ll start by creating two components: Button
and RippleButton
. The RippleButton
component will handle the ripple effect, while Button
will serve as a simple wrapper that can be extended.
RippleButton.js
Fileimport React from "react";
import classnames from "classnames";
import Styles from "./RippleButton.module.scss";
export const Button = ({ className, children, onClick }) => {
return (
<div className={className} onClick={onClick}>
{children}
</div>
);
};
export const RippleButton = ({ className, children, onClick, ...rest }) => {
const mounted = React.useRef(false);
const [isRippling, setIsRippling] = React.useState(false);
const [coords, setCoords] = React.useState({ x: -1, y: -1 });
React.useEffect(() => {
mounted.current = true;
return () => {
mounted.current = false;
};
}, []);
React.useEffect(() => {
if (mounted) {
if (coords.x !== -1 && coords.y !== -1) {
setIsRippling(true);
setTimeout(() => setIsRippling(false), 500);
} else {
setIsRippling(false);
}
}
}, [coords, mounted]);
React.useEffect(() => {
if (mounted) {
if (!isRippling) setCoords({ x: -1, y: -1 });
}
}, [isRippling]);
return (
<Button
className={classnames(Styles["waves-effect"], {
[className]: className,
})}
onClick={(e) => {
const rect = e.target.getBoundingClientRect();
setCoords({ x: e.clientX - rect.left, y: e.clientY - rect.top });
if (onClick) {
onClick(e);
}
}}
{...rest}
>
{children}
{isRippling ? (
<span
className={Styles["waves-ripple"]}
style={{
left: coords.x,
top: coords.y,
}}
></span>
) : null}
</Button>
);
};
Button.Ripple = RippleButton;
useRef
and useEffect
HooksNext, we’ll create the SCSS file that defines the styles for the ripple effect.
RippleButton.module.scss
File.waves-effect {
position: relative;
overflow: hidden;
.waves-ripple {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
animation: ripple-effect 0.7s ease;
}
}
@keyframes ripple-effect {
0% {
opacity: 0;
}
25% {
opacity: 1;
}
100% {
width: 200%;
padding-bottom: 200%;
opacity: 0;
}
.waves-effect
: This class is applied to the button container to handle the positioning and overflow of the ripple effect.
.waves-ripple
: The ripple element starts small and expands while fading out. The transform: translate(-50%, -50%);
ensures that the ripple is centered at the click position.
@keyframes ripple-effect
: Defines the animation for the ripple effect, where the ripple grows in size and fades out as it expands.
App.js
import React from "react";
import { RippleButton } from "./RippleButton";
function App() {
return (
<div className="App">
<RippleButton className="my-button">Click Me</RippleButton>
</div>
);
}
export default App;