Reputation: 31
I’m trying to create a water tank simulation in React. The idea is to use clip-path
to restrict an animated wave to the shape of the tank icon (defined as an SVG path).
The SVG path renders correctly, but when I use it as a clip-path
, the size of the clip-path
doesn’t match the original SVG path. Instead, the wave appears misaligned or not clipped at all.
I tried adding clipPathUnits="objectBoundingBox"
, but this caused the clip-path
to disappear entirely.
Here is my React component:
import styles from "./WaterTankSmall.module.css";
const waterLevel = 50;
const WaterTankSmall = () => {
return (
<div className={styles.container}>
{/* SVG */}
<svg
className={styles.svgIcon}
xmlns="http://www.w3.org/2000/svg"
xmlSpace="preserve"
viewBox="0 0 66 82.5"
preserveAspectRatio="xMidYMid meet"
>
<defs>
<clipPath id="waterTankClip">
<path d="M52.6 47V25H13.4v22.4h39.2V47zM13.5 23h39c-.6-5.5-4.8-9.9-10.1-10.9V3h1.9c.6 0 1-.4 1-1s-.4-1-1-1H21.7c-.6 0-1 .4-1 1s.4 1 1 1h1.7v9.2c-5.2 1.1-9.3 5.4-9.9 10.8zM25.4 3h14.9v9h-6.4V8.3h2.2c.6 0 1-.4 1-1s-.4-1-1-1h-6.5c-.6 0-1 .4-1 1s.4 1 1 1H32V12h-6.6V3zM52.4 49.4H13.6c1 5.1 5.2 9.1 10.4 9.9l-2.5 4.1c-.2.3-.2.7 0 1 .2.3.5.5.9.5h21.2c.4 0 .7-.2.9-.5.2-.3.2-.7 0-1l-2.5-4c5.2-.8 9.3-4.8 10.4-10z" />
</clipPath>
</defs>
<path
maskContentUnits="objectBoundingBox"
d="M52.6 47V25H13.4v22.4h39.2V47zM13.5 23h39c-.6-5.5-4.8-9.9-10.1-10.9V3h1.9c.6 0 1-.4 1-1s-.4-1-1-1H21.7c-.6 0-1 .4-1 1s.4 1 1 1h1.7v9.2c-5.2 1.1-9.3 5.4-9.9 10.8zM25.4 3h14.9v9h-6.4V8.3h2.2c.6 0 1-.4 1-1s-.4-1-1-1h-6.5c-.6 0-1 .4-1 1s.4 1 1 1H32V12h-6.6V3zM52.4 49.4H13.6c1 5.1 5.2 9.1 10.4 9.9l-2.5 4.1c-.2.3-.2.7 0 1 .2.3.5.5.9.5h21.2c.4 0 .7-.2.9-.5.2-.3.2-.7 0-1l-2.5-4c5.2-.8 9.3-4.8 10.4-10z"
className={styles.tankOutline}
/>
</svg>
{/* wave container*/}
<div
className={styles.waveContainer}
style={{
clipPath: "url(#waterTankClip)",
WebkitClipPath: "url(#waterTankClip)",
}}
>
{[...Array(4)].map((_, i) => (
<div
key={i}
className={`${styles.wave} ${styles[`wave${i + 1}`]}`}
style={{
height: `${waterLevel * 2}%`,
}}
></div>
))}
<div className={styles.waterLevelText}>{`${waterLevel}%`}</div>
</div>
</div>
);
};
export default WaterTankSmall;
module.css below
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
background: #f0f8ff;
}
#waterTankClip {
width: 100px;
height: 100px;
}
.svgIcon {
width: 500px;
height: 500px;
fill: none;
stroke-width: 1;
stroke: #1e90ff;
}
.waveContainer {
background-color: pink;
width: 100%;
}
.wave {
position: absolute;
bottom: 0;
/* left: -100%;
width: 5000px;
height: 2500px;
background: linear-gradient(to bottom, #87ceeb, #1e90ff);
clip-path: polygon(
0% 50%,
10% 45%,
20% 50%,
30% 55%,
40% 50%,
50% 45%,
60% 50%,
70% 55%,
80% 50%,
90% 45%,
100% 50%,
100% 100%,
0% 100%
);
z-index: 0;
}
.wave1 {
background: linear-gradient(to bottom, #87ceeb, #1e90ff);
opacity: 0.7;
animation: wave1-animation 6s infinite ease-in-out;
}
.wave2 {
background: linear-gradient(
to bottom,
rgba(135, 206, 235, 0.5),
rgba(30, 144, 255, 0.5)
);
opacity: 0.5;
animation: wave2-animation 8s infinite ease-in-out;
}
.wave3 {
background: linear-gradient(
to bottom,
rgba(135, 206, 235, 0.4),
rgba(30, 144, 255, 0.4)
);
opacity: 0.3;
animation: wave3-animation 10s infinite ease-in-out;
}
.wave4 {
background: linear-gradient(
to bottom,
rgba(135, 206, 235, 0.6),
rgba(30, 144, 255, 0.6)
);
opacity: 0.4;
animation: wave4-animation 12s infinite ease-in-out;
}
/* Wave animation*/
@keyframes wave1-animation {
0% {
transform: translateX(0);
}
50% {
transform: translateX(-30%);
}
100% {
transform: translateX(0);
}
}
@keyframes wave2-animation {
0% {
transform: translateX(-25%);
}
50% {
transform: translateX(25%);
}
100% {
transform: translateX(-25%);
}
}
@keyframes wave3-animation {
0% {
transform: translateX(-15%);
}
50% {
transform: translateX(15%);
}
100% {
transform: translateX(-15%);
}
}
@keyframes wave4-animation {
0% {
transform: translateX(0);
}
50% {
transform: translateX(-20%);
}
100% {
transform: translateX(0);
}
}
What I’ve Tried:
Adjusting the viewBox
and preserveAspectRatio
of the SVG.
Adding/removing clipPathUnits="objectBoundingBox"
or userSpaceOnUse
.
Ensuring the SVG dimensions and CSS dimensions match.
Upvotes: 3
Views: 50