首页 > 解决方案 > 如何在以下音量旋钮组件中为我的 SVG 音量旋钮设置动画?

问题描述

我正在尝试在以下音量旋钮组件中实现 SVG 音量旋钮,以便 svg 旋钮将获得动画效果。这有可能吗?

这是我的旋钮组件:

import React, { useEffect } from 'react';

export function VolumeKnobComponent() {
    const [volumeLevel, setVolumeLevel] = React.useState(70);
      
    const interactionAreaRef = React.useRef();
    const scrollWheelRef = React.useRef();  
    const scrollWheelWrapperRef = React.useRef();
    
    let width = 100;
    let height = 100;
    let touchPosX = 0;
    let touchPosY = height / 2;
    let currentAngle = 0;
    
    const setDefaultVolumeLevel = () => {
      scrollWheelRef.current.style.transform = 'rotate('+ 45 +'deg)';
    }
    
    const setEventListeners = (event) => {
      interactionAreaRef.current.addEventListener("touchmove", calculateAngle(event));
      interactionAreaRef.current.addEventListener("touchstart", setTouchPos(event));
  
      interactionAreaRef.current.addEventListener("mousedown", setTouchPos(event));
      interactionAreaRef.current.addEventListener("mousemove", calculateAngle(event));
    }
      
    const setTouchPos = (event) => {
      touchPosX = event.layerX - (width / 2);
      touchPosY = (height / 2) - event.layerY;
    }
  
    const calculateAngle = (event) => {
      let adjustedLayerX = event.layerX - (width / 2);
      let adjustedLayerY = (height / 2) - event.layerY;
    
      let startAngle = Math.atan2(touchPosY, touchPosX) * 180 / Math.PI - 90;
      let deltaAngle = Math.atan2(adjustedLayerY, adjustedLayerX) * 180 / Math.PI - 90;
      
      currentAngle += (startAngle - deltaAngle);
    
      switch(true) {
        case currentAngle > 150 && currentAngle <= 210:
          setVolumeLevel(0);
          break;
        case currentAngle > 210 && currentAngle <= 240:
          setVolumeLevel(10);
          break;
        case (currentAngle > 240 && currentAngle <= 269) || currentAngle === -90:
          setVolumeLevel(20);
          break;
        case currentAngle > -90 && currentAngle <= -60:
          setVolumeLevel(30);
          break;
        case currentAngle > -60 && currentAngle <= -30:
          setVolumeLevel(40);
          break;
        case currentAngle > -30 && currentAngle < 0:
          setVolumeLevel(50);
          break;
        case currentAngle > 0 && currentAngle <= 30:
          setVolumeLevel(60);
          break;
        case currentAngle > 30 && currentAngle <= 60:
          setVolumeLevel(70);
          break;
        case currentAngle > 60 && currentAngle <= 90:
          setVolumeLevel(80);
          break;
        case currentAngle > 90 && currentAngle <= 120:
          setVolumeLevel(90);
          break;
        case currentAngle > 120 && currentAngle <= 150:
          setVolumeLevel(100);
          break;
      }
    
      scrollWheelRef.current.style.transform = 'rotate('+ currentAngle +'deg)';
      
      if (!volumeLevel) {
        scrollWheelRef.current.style.background = "rgba(0, 0, 0, 0.2)";
      }
      else if (volumeLevel === 90) {
        scrollWheelRef.current.style.background = "rgba(255,255,0, 1.0)";
      }
      else if (volumeLevel === 100) {
        scrollWheelRef.current.style.background = "rgba(360, 0, 0, 1.0)";
      }
      else {
        scrollWheelRef.current.style.background = "rgba(0, 191, 255, " + volumeLevel * 0.0125 + ")";
      }
      
      touchPosX = adjustedLayerX;
      touchPosY = adjustedLayerY;
    }
    
    useEffect(() => {
      setDefaultVolumeLevel();
      
      scrollWheelWrapperRef.current.addEventListener('touchmove', (event) => event.preventDefault());    
    });
    
    return(
      <div>
        <div ref={scrollWheelWrapperRef} id="scroll-wheel-wrapper">
          <div ref={scrollWheelRef} id="scroll-wheel">
            <div id="thumb">
            </div>
          </div>
            
          <div ref={interactionAreaRef} id="interaction-area" onMouseMove={() => setEventListeners()} onTouchMove={() => setEventListeners()}>
          </div>
        </div>
          
        <div id="volume-level">
          {volumeLevel}
        </div>
      </div>
    );
  }

这是我想用我的代码制作动画的 SVG 旋钮:

 <svg width="74" height="97" viewBox="0 0 74 97" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M21.6515 81.8144V89.7344H21.0755V83.8304L21.1475 82.3904H21.1355L18.2435 89.7344H17.6915L14.7995 82.3904H14.7875L14.8595 83.8304V89.7344H14.2835V81.8144H15.1835L17.5715 87.8144L17.9675 89.0264H17.9795L18.3635 87.8264L20.7515 81.8144H21.6515ZM23.1307 89.7344L26.2987 81.8144H27.0067L30.1747 89.7344H29.5387L28.5427 87.2504H24.7507L23.7667 89.7344H23.1307ZM26.3587 83.2184L24.9787 86.6984H28.3267L26.9467 83.2424L26.6587 82.4024H26.6467L26.3587 83.2184ZM34.005 81.6944C35.469 81.6944 36.465 82.2784 36.993 83.4464L36.489 83.8304C36.257 83.2704 35.929 82.8664 35.505 82.6184C35.089 82.3704 34.573 82.2464 33.957 82.2464C33.245 82.2464 32.701 82.4064 32.325 82.7264C31.949 83.0384 31.761 83.4224 31.761 83.8784C31.761 84.2464 31.877 84.5504 32.109 84.7904C32.349 85.0304 32.793 85.2224 33.441 85.3664L34.989 85.7024C35.757 85.8704 36.301 86.1144 36.621 86.4344C36.941 86.7464 37.101 87.1504 37.101 87.6464C37.101 88.0784 36.977 88.4624 36.729 88.7984C36.481 89.1264 36.133 89.3864 35.685 89.5784C35.245 89.7624 34.729 89.8544 34.137 89.8544C33.289 89.8544 32.577 89.6864 32.001 89.3504C31.433 89.0064 31.041 88.5344 30.825 87.9344L31.353 87.5264C31.521 88.0944 31.849 88.5344 32.337 88.8464C32.833 89.1504 33.441 89.3024 34.161 89.3024C34.833 89.3024 35.385 89.1664 35.817 88.8944C36.257 88.6144 36.477 88.2104 36.477 87.6824C36.477 87.3544 36.357 87.0744 36.117 86.8424C35.877 86.6104 35.461 86.4304 34.869 86.3024L33.237 85.9424C32.477 85.7744 31.933 85.5264 31.605 85.1984C31.285 84.8624 31.125 84.4464 31.125 83.9504C31.125 83.5424 31.241 83.1704 31.473 82.8344C31.705 82.4904 32.033 82.2144 32.457 82.0064C32.889 81.7984 33.405 81.6944 34.005 81.6944ZM44.0234 81.8144V82.3664H41.1314V89.7344H40.5314V82.3664H37.6394V81.8144H44.0234ZM45.5374 89.7344V81.8144H50.9494V82.3664H46.1374V85.4264H49.9414V85.9904H46.1374V89.1824H51.1414V89.7344H45.5374ZM55.8251 81.8144C56.6091 81.8144 57.2371 82.0144 57.7091 82.4144C58.1811 82.8144 58.4171 83.3624 58.4171 84.0584C58.4171 84.6424 58.2451 85.1224 57.9011 85.4984C57.5651 85.8664 57.1051 86.1024 56.5211 86.2064L58.8371 89.7344H58.1291L55.8371 86.2664H55.8131L55.7531 86.3024H53.4851V89.7344H52.8851V81.8144H55.8251ZM55.8131 85.7384C56.4531 85.7384 56.9411 85.5944 57.2771 85.3064C57.6211 85.0184 57.7931 84.6024 57.7931 84.0584C57.7931 83.5144 57.6211 83.0984 57.2771 82.8104C56.9411 82.5144 56.4531 82.3664 55.8131 82.3664H53.4851V85.7384H55.8131Z" fill="#383838"/>
    <path d="M13.888 67.7744V71.7344H13.6V68.7824L13.636 68.0624H13.63L12.184 71.7344H11.908L10.462 68.0624H10.456L10.492 68.7824V71.7344H10.204V67.7744H10.654L11.848 70.7744L12.046 71.3804H12.052L12.244 70.7804L13.438 67.7744H13.888ZM15.4376 67.7744V71.7344H15.1376V67.7744H15.4376ZM19.7265 67.7744V71.7344H19.3545L17.2545 68.6264L16.9605 68.1164H16.9545L16.9725 68.5964V71.7344H16.6845V67.7744H17.0625L19.1565 70.8764L19.4565 71.3924L19.4445 70.9124V67.7744H19.7265Z" fill="#383838"/>
    <path d="M55.7516 67.7744V71.7344H55.4636V68.7824L55.4996 68.0624H55.4936L54.0476 71.7344H53.7716L52.3256 68.0624H52.3196L52.3556 68.7824V71.7344H52.0676V67.7744H52.5176L53.7116 70.7744L53.9096 71.3804H53.9156L54.1076 70.7804L55.3016 67.7744H55.7516ZM56.4912 71.7344L58.0752 67.7744H58.4292L60.0132 71.7344H59.6952L59.1972 70.4924H57.3012L56.8092 71.7344H56.4912ZM58.1052 68.4764L57.4152 70.2164H59.0892L58.3992 68.4884L58.2552 68.0684H58.2492L58.1052 68.4764ZM63.3087 71.7344H62.9607L61.7547 69.9524L60.5307 71.7344H60.1767L61.5927 69.7004L60.2967 67.7744H60.6567L61.7787 69.4484L62.9187 67.7744H63.2607L61.9407 69.6944L63.3087 71.7344Z" fill="#383838"/>
    <circle cx="36.8245" cy="36.7344" r="32.5" stroke="#363636"/>
    <circle cx="36.8245" cy="36.7344" r="30" fill="#727272"/>
    <circle cx="12.3245" cy="38.2344" r="2.5" fill="white"/>
    <path d="M12.8245 63.7343C-4.67554 44.7344 -2.67561 19.5984 18.3245 6.23428C36.3955 -5.26582 59.8245 3.73438 67.3245 18.2343C71.5415 26.3872 78.8245 46.2344 59.8245 63.7343" stroke="#5B5B5B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="0.5 5"/>
</svg>

是否可以在 VolumeKnobComponent 内实现这个 svg 旋钮,以便 svg 旋钮获得动画效果?

如果是的话,如果有人可以向我解释它是如何完成的,我将非常感激。

标签: javascriptreactjsanimationsvg

解决方案


推荐阅读