0

I'm finishing a project, but I have one more step to finish. I want to visualize microphone input by a canvas. Getting the data from the microphone isn't a problem. But I want to visualize it in a special way. (see image)

Wave

I want to animate each element from the wave.

My problem isn't the animation. My problem is to create those shapes in the CANVAS. This is an example of one shape:

One shape

I can create a rounded corner shape with the canvas

    const draw = () => {
        fillRoundedRect(20, 20, 100, 100, 20);
        ctx.fillStyle = "red";
        ctx.fill();
    };

    const fillRoundedRect = (x, y, w, h, r) => {
        ctx.beginPath();
        ctx.moveTo(x+r, y);
        ctx.lineTo(x+w-r, y);
        ctx.quadraticCurveTo(x+w, y, x+w, y+r);
        ctx.lineTo(x+w, y+h-r);
        ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
        ctx.lineTo(x+r, y+h);
        ctx.quadraticCurveTo(x, y+h, x, y+h-r);
        ctx.lineTo(x, y+r);
        ctx.quadraticCurveTo(x, y, x+r, y);
        ctx.fill();
    };

Can someone help me with creating a shape like in the second image?

Thanks in advance guys!

gus27
  • 2,616
  • 1
  • 21
  • 25
Jesse
  • 23
  • 5

1 Answers1

1

Instead of trying to make a single shape with dependency on surrounding shapes and a high risk of headache math-wise, use instead two shapes which you merge using composition. My suggestion anyways.

  • Draw all the bars in full height using composition mode source-over (default)
  • Define a single shape on top using some sort of spline (I would suggest a cardinal spline).
  • Set composition mode to destination-out and render an enclosed shape using the spline as top "line".

Example

This should work in a loop (remember to clear canvas for each frame) but shows only the building stones needed here -

var ctx = c.getContext("2d");
var points = [];
var skippy = 0;

// render all bars
ctx.globalCompositeOperation = "source-over"; // not needed here, but in a loop yes!

// produce bars
ctx.beginPath();                             // not needed here, but in a loop yes!
for(var x = 0; x < c.width; x += 30) {
  ctx.rect(x, 0, 16, c.height)

  // OKIDOKI, lets produce the spline using random points (y) as well
  // but not for all, only every second for prettyness... modify to taste
  if (skippy++ % 2 === 0) points.push(x, c.height * Math.random());
}
points.push(c.width, c.height * Math.random());  // one last
ctx.fillStyle = "rgb(198, 198, 198)";
ctx.fill();

// render spline
ctx.beginPath();
ctx.moveTo(0, c.height);                     // bottom left corner
curve(ctx, points);                          // spline
ctx.lineTo(c.width, c.height);               // bottom right corner
ctx.closePath();
ctx.globalCompositeOperation = "destination-out";
ctx.fill();
Community
  • 1
  • 1
  • Thank you! It's a good solution but technically it's not possible for me. I should have the opportunity to animate each bar separately. In your case, I will have to animate the curve, which doesn't allow me to animate each bar separately. Or am I wrong? Thanks! – Jesse Feb 07 '17 at 08:44
  • @Jesse sure it's possible. The code above simply generates some random points but you can replace those with actual points intended for the bars. You can also add (or reduce) points for finer control. I'll add a couple of controls to demo. –  Feb 07 '17 at 10:39
  • 1
    Wow great, I think i can get started with that :) Thank you! – Jesse Feb 07 '17 at 12:34