HTML5 Canvas draw lines in a circle and move with music?

◇◆丶佛笑我妖孽 提交于 2021-01-27 21:13:00

问题


I am able to draw lines around a circle. I have the basic implementation of the AudioContext API setup.

The problem I am facing is when calling lineTo the line will only grow but not shrink. I am inspired by this https://www.kkhaydarov.com/audio-visualizer/. I am translating this code over into https://codesandbox.io/s/hungry-tereshkova-1pf0c?runonclick=1&file=/src/Visualizer.js:713-725 which is a React.js version.

If you run that code you will see the music play, then the bars will grow once, and then they stick. They refuse to shrink then grow to the beat.

I am not sure where I am going wrong or what I am missing in that code. It seems pretty similar to the other code in the example.

Here is the full code for the Visualizer component.

import React, { useEffect, useRef } from "react";

let frequencyArray = [];
let analyser;

const Visualizer = () => {
  const canvasRef = useRef(null);
  const requestRef = useRef(null);

  useEffect(() => {
    initAudio();
    requestRef.current = requestAnimationFrame(drawCanvas);
    return () => cancelAnimationFrame(requestRef.current);

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const initAudio = () => {
    const audio = new Audio();
    audio.src =
      "https://s3.us-west-2.amazonaws.com/storycreator.uploads/ck9kpb5ss0xf90132mgf8z893?client_id=d8976b195733c213f3ead34a2d95d1c1";
    audio.crossOrigin = "anonymous";
    audio.load();

    const context = new (window.AudioContext || window.webkitAudioContext)();
    analyser = context.createAnalyser();
    const source = context.createMediaElementSource(audio);

    source.connect(analyser);
    analyser.connect(context.destination);

    frequencyArray = new Uint8Array(analyser.frequencyBinCount);
    audio.play();
  };

  // draw the whole thing
  const drawCanvas = () => {
    if (canvasRef.current) {
      const canvas = canvasRef.current;
      const ctx = canvas.getContext("2d");
      const radius = 200;
      const bars = 100;

      drawCircle(canvas, ctx, radius);

      analyser.getByteFrequencyData(frequencyArray);

      for (var i = 0; i < bars; i++) {
        const height = frequencyArray[i] * 0.3;

        drawLine(
          {
            i,
            bars,
            height,
            radius
          },
          canvas,
          ctx
        );
      }

      requestRef.current = requestAnimationFrame(drawCanvas);
    }
  };

  // draw the main circle
  const drawCircle = (canvas, ctx, radius) => {
    const centerX = canvas.width / 2;
    const centerY = canvas.height / 2;

    ctx.save();
    ctx.beginPath();
    ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
    ctx.fillStyle = "white";
    ctx.fill();
    ctx.strokeStyle = "#dddddd";
    ctx.lineWidth = 5;
    ctx.stroke();
    ctx.restore();
  };

  // dray lines around the circle
  const drawLine = (opts, canvas, ctx) => {
    const { i, radius, bars, height } = opts;
    const centerX = canvas.width / 2;
    const centerY = canvas.height / 2;
    const lineWidth = 10;
    const rads = (Math.PI * 2) / bars;

    const x = centerX + Math.cos(rads * i) * (radius + lineWidth);
    const y = centerY + Math.sin(rads * i) * (radius + lineWidth);
    const endX = centerX + Math.cos(rads * i) * (radius + height);
    const endY = centerY + Math.sin(rads * i) * (radius + height);

    // draw the bar
    ctx.strokeStyle = "#ddd";
    ctx.lineWidth = lineWidth;
    ctx.lineCap = "round";
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(endX, endY);
    ctx.stroke();
  };

  return (
    <canvas
      ref={canvasRef}
      style={{ background: "#f5f5f5" }}
      width={window.innerWidth}
      height={window.innerHeight}
    />
  );
};

export default Visualizer;

回答1:


You just missed a clearRect in your code...
Without that we see the lines grow only because any following shorter line does not overwrite the previous one, they are still getting drawn just we do not see it.

here is the working code:

https://codesandbox.io/s/dry-cdn-ghu4m?file=/src/Visualizer.js:1247-1276

I hardcoded a ctx.clearRect(0,0, 1000,1000) just to show you that it works, but you should use the canvas dimensions there, everything else looks good.

Only recommendation will be to somehow move:

const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");

somewhere global outside the drawCanvas function,
those do not change on every run, will be nice to set them just once.



来源:https://stackoverflow.com/questions/61903270/html5-canvas-draw-lines-in-a-circle-and-move-with-music

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!