简体   繁体   English

HTML5 Canvas 画一个圆圈,跟着音乐移动?

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

I am able to draw lines around a circle.我能够在圆圈周围画线。 I have the basic implementation of the AudioContext API setup.我有 AudioContext API 设置的基本实现。

The problem I am facing is when calling lineTo the line will only grow but not shrink.我面临的问题是当调用lineTo时,这条线只会增长但不会缩小。 I am inspired by this https://www.kkhaydarov.com/audio-visualizer/ .我受到这个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.我正在将此代码翻译成https://codesandbox.io/s/hungry-tereshkova-1pf0c?runonclick=1&file=/src/Visualizer.js:713-725 ,这是一个 React.js 版本。

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.这是 Visualizer 组件的完整代码。

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;

You just missed a clearRect in your code...您只是错过了代码中的clearRect ......
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 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.我硬编码了一个ctx.clearRect(0,0, 1000,1000)只是为了向您展示它可以工作,但是您应该在那里使用 canvas 尺寸,其他一切看起来都不错。

Only recommendation will be to somehow move:唯一的建议是以某种方式移动:

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

somewhere global outside the drawCanvas function,在drawCanvas function之外的某个地方,
those do not change on every run, will be nice to set them just once.这些在每次运行时都不会改变,只需设置一次就可以了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM