svg 的每个路径上的波浪动画

问题描述 投票:0回答:2

在我的反应项目中,我有一个包含水平线(路径)的

SVG
,当我将鼠标悬停在每个路径上时,我想启动从悬停点开始并连续结束直到路径末尾的波浪动画线。

默认情况下,我使用振动 CSS 动画。我想用波代替振动

我对此一无所知。我不喜欢 SVG。

我想要这样的 GIF。

我的反应组件

"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;
reactjs svg svg-animate
2个回答
2
投票

这是一个尝试(使用 D3 库):

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>


2
投票

不知道您觉得这是否有用。我使用

d
函数更新路径元素上的
setInterval()
属性。

在理想的设置中,您可以将

setInterval()
切换为
requestAnimationFrame()
,但那又是另一天了。

const path = document.querySelector('path');
let dist = 0;
let freq = 20;
let amp = 0;
let ampdelta = .4;
let length = 140;
let arr = [...new Array(length / freq).keys()];
let interval = 0;

path.addEventListener('mouseover', () => {
  if (interval == 0) {
    amp = 0;
    ampdelta = .1;
    interval = setInterval(animate, 10);
  }
});

function animate() {
  path.setAttribute('d', arr.map(i => {
    let prestr = (i == 0) ? `m ${dist} 0 ` : '';
    let localamp = (i % 2) ? amp * -1 : amp;
    return prestr + `q ${freq/2} ${localamp} ${freq} 0`;
  }).join(' '));

  dist = (dist >= -1) ? freq * -2 : dist + 1;
  amp = amp + ampdelta;
  if (amp > 10) ampdelta = ampdelta * -1;
  if (amp < 0) {
    clearInterval(interval);
    interval = 0;
  }
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 40">
  <path transform="translate(0 20)" d="m 0 0 h 100" stroke="black" fill="none" />
</svg>

© www.soinside.com 2019 - 2024. All rights reserved.