In my react project, I have an SVG
that contains horizontal lines (path), and when I hover the mouse on each path I want to start wave animation that starts from the hovered point and continuously ends until the end of the path line.
as default, I use a vibration CSS animation. and I want to replace vibration with wave
I don't have any idea about that. I'm not into the SVG.
I want something like this GIF.
my react component
"use client";
import { useCallback } from "react";
import "./styles.css";
const SongPlayer = () => {
const soundFontUrl = "/sounds/guitar/";
const playNote = useCallback((note: string) => {
const audio = new Audio(soundFontUrl + note.trim() + ".mp3");
audio.play();
}, []);
const handleMouseOver = (note: string, id: string) => {
playNote(note);
const el = document.getElementById(id);
el?.classList.add("vibrating");
setTimeout(() => {
el?.classList.remove("vibrating");
}, 1000);
};
return (
<div className="w-full mt-[44px]">
<div className="w-full flex justify-center items-center">
<svg
id="guitar"
className="guitar"
width="1700"
height="324"
viewBox="0 0 1441 324"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
id="g-one"
onMouseOver={() => handleMouseOver("D3", "g-one")}
d="M1075.88 242.167C1059.69 248.627 1044.41 255.881 1021.79 251.862C1020.83 251.69 1019.91 251.509 1019.03 251.319M1019.03 251.319C987.538 244.521 1010.27 226.472 1021.79 232.056C1028.69 235.4 1025.2 244.393 1019.03 251.319ZM1019.03 251.319C1014.6 256.283 1008.47 258.373 993.68 258.373C954.721 258.373 905.598 232.907 799 235.49L0 235.49"
stroke="#A39D92"
strokeWidth="1.5"
/>
<path
id="g-two"
onMouseOver={() => handleMouseOver("D5", "g-two")}
d="M0 275H1089.5"
stroke="#A39D92"
strokeWidth="1.5"
/>
<path
id="g-three"
onMouseOver={() => handleMouseOver("C4", "g-three")}
d="M0 164.5H1026"
stroke="#A39D92"
strokeWidth="1.5"
/>
<path
id="g-four"
onMouseOver={() => handleMouseOver("C2", "g-four")}
d="M0 125H1057.5"
stroke="#A39D92"
strokeWidth="1.5"
/>
<path
id="g-five"
onMouseOver={() => handleMouseOver("F4", "g-five")}
d="M0 54H1151"
stroke="#A39D92"
strokeWidth="1.5"
/>
<path
id="g-six"
onMouseOver={() => handleMouseOver("F2", "g-six")}
d="M0 14.5H1164.5"
stroke="#A39D92"
strokeWidth="1.5"
/>
</svg>
</div>
</div>
);
};
export default SongPlayer;
Here is an attempt (with D3 library):
const svg = d3.select('svg');
const totalWidth = 300;
const waveLength = 30;
const maxMagnitude = 40;
const delta = 5;
const maxIndex = 22;
const line = d3.line().curve(d3.curveMonotoneX);
const nextStep = (handle, points, y, index) => {
const pArray = points.map(p => [p.x, p.y]);
points.forEach(p => {
if (p.m > 0) {
p.m -= 2;
if (p.y > y)
p.y = y - p.m;
else
p.y = y + p.m;
}
});
if (index < maxIndex) {
const path = line(pArray);
d3.select(handle)
.transition()
.duration(250)
.attr('d', path);
setTimeout(() => nextStep(handle, points, y, index + 1), 250);
}
else {
d3.select(handle).attr('d', `M 0,${y} H 300`);
}
}
const onHover = (handle, e, lineIndex) => {
let x = e.offsetX;
let y = lineIndex * 30;
let m = maxMagnitude;
const start = - Math.ceil(x / waveLength);
const end = Math.ceil((totalWidth - x) / waveLength);
const points = [];
for (let i = 0; i >= start; i--) {
points.unshift({x, y, m: 0});
x -= waveLength / 4;
points.unshift({x, y: y - m, m});
m = Math.max(0, m - delta);
x -= waveLength / 4;
points.unshift({x, y: y, m: 0});
x -= waveLength / 4;
points.unshift({x, y: y + m, m});
m = Math.max(0, m - delta);
x -= waveLength / 4;
}
m = maxMagnitude;
x = e.offsetX;
for (let i = 0; i <= end; i++) {
if (i !== 0)
points.push({x, y, m: 0});
x += waveLength / 4;
points.push({x, y: y + m, m});
m = Math.max(0, m - delta);
x += waveLength / 4;
points.push({x, y: y, m: 0});
x += waveLength / 4;
points.push({x, y: y - m, m});
m = Math.max(0, m - delta);
x += waveLength / 4;
}
nextStep(handle, points, y, 0);
}
for (let i = 0; i <= 10; i++) {
svg.append('path')
.attr('d', `M 0,${i * 30} H 300`)
.attr('fill', 'none')
.attr('stroke', 'blue')
.on('mouseover', function(e) {
onHover(this, e, i);
});
}
svg {
border: 1px solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
<svg width='300' height='300'>
</svg>