mooseymanson
mooseymanson

Reputation: 35

Rotating Rectangles Around Circle Perimeter on Canvas

I'm trying to create a little circular "equalizer" effect using JavaScript and HTML canvas for a little project I'm working on, and it works great, except one little thing. It's just a series of rectangular bars moving in time to an mp3 - nothing overly fancy, but at the moment all the bars point in one direction (i.e. 0 radians, or 90 degrees).

I want each respective rectangle around the edge of the circle to point directly away from the center point, rather than to the right. I have 360 bars, so naturally, each one should be 1 degree more rotated than the previous.

I thought that doing angle = i*Math.PI/180 would fix that, but it doesn't seem to matter what I do with the rotate function - they always end up pointing in weird and wonderful directions, and being translated a million miles from where they were. And I can't see why. Can anyone see where I'm going wrong?

My frame code, for reference, is as follows:

function frames() {

  // Clear the canvas and get the mp3 array
  window.webkitRequestAnimationFrame(frames);
  musicArray = new Uint8Array(analyser.frequencyBinCount);
  analyser.getByteFrequencyData(musicArray);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  bars = 360;


  for (var i = 0; i < bars; i++) {

    // Find the rectangle's position on circle edge
    distance = 100;
    var angle = i * ((Math.PI * 2) / bars);
    var x = Math.cos(angle) * distance + (canvas.width / 2);
    var y = Math.sin(angle) * distance + (canvas.height / 2);
    barWidth = 5;
    barHeight = (musicArray[i] / 4);

    // Fill with a blue-green gradient
    var grd = ctx.createLinearGradient(x, 0, x + 40, 0);
    grd.addColorStop(0, "#00CCFF");
    grd.addColorStop(1, "#00FF7F");
    ctx.fillStyle = grd;

    // Rotate the rectangle according to position
    // ctx.rotate(i*Math.PI/180); - DOESN'T WORK

    // Draw the rectangle
    ctx.fillRect(x, y, barHeight, barWidth);

  }

Upvotes: 2

Views: 905

Answers (2)

Helder Sepulveda
Helder Sepulveda

Reputation: 17594

Here is another solution, I'm preserving your initial trigonometry approach.
But instead of rectangles I used lines, I don't think it makes a difference for you, if what you need is bars moving in time to an mp3 all you need to do is change the var v = Math.random() + 1; to a reading from the Amplitude, and those bars will be dancing.

const canvas = document.getElementById("c");
canvas.width = canvas.height = 170;
const ctx = canvas.getContext("2d");
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.lineWidth = 2;

let r = 40;
let bars = 180;

function draw() {
  ctx.clearRect(-100, -100, 200, 200)

  for (var i = 0; i < 360; i += (360 / bars)) {
    var angle = i * ((Math.PI * 2) / bars);
    var x = Math.cos(angle) * r;
    var y = Math.sin(angle) * r;

    ctx.beginPath();
    var v = Math.random() + 1;
    ctx.moveTo(x, y);
    ctx.lineTo(x * v, y * v)

    grd = ctx.createLinearGradient(x, y, x*2, y*2);
    grd.addColorStop(0, "blue");
    grd.addColorStop(1, "red");
    ctx.strokeStyle = grd;
    ctx.stroke();
  }
}

setInterval(draw, 100)
<canvas id="c"></canvas>

Upvotes: 0

enxaneta
enxaneta

Reputation: 33044

For clarity I've removed part of your code. I'm using rotate as you intended. Also I'm using barHeight = (Math.random()* 50); instead your (musicArray[i]/4); because I wanted to have something to show.

Also I've changed your bars to 180. It's very probable that you won't have 360 bars but 32 or 64 or 128 or 256 . . . Now you can change the numbers of bare to one of these numbers to see the result.

I'm drawing everything around the origin of the canvas and translating the context in the center.

I hope it helps.

const canvas = document.getElementById("c");
const ctx = canvas.getContext("2d");
let cw = canvas.width = 400;
let ch = canvas.height = 400;


let bars = 180;
let r = 100;

ctx.translate(cw / 2, ch / 2)

for (var i = 0; i < 360; i += (360 / bars)) {

  // Find the rectangle's position on circle edge

  var angle = i * ((Math.PI * 2) / bars);
  //var x = Math.cos(angle)*r+(canvas.width/2);
  //var y = Math.sin(angle)*r+(canvas.height/2);
  barWidth = 2 * Math.PI * r / bars;
  barHeight = (Math.random() * 50);


  ctx.fillStyle = "green";

  // Rotate the rectangle according to position
  // ctx.rotate(i*Math.PI/180); - DOESN'T WORK

  // Draw the rectangle
  ctx.save();
  ctx.rotate(i * Math.PI / 180);

  ctx.fillRect(r, -barWidth / 2, barHeight, barWidth);

  //ctx.fillRect(r ,0, barHeight, barWidth);

  ctx.restore();
}
canvas {
  border: 1px solid
}
<canvas id="c"></canvas>

Upvotes: 2

Related Questions