Pontarlier
Pontarlier

Reputation: 21

HTML5 Canvas thick lineWidth ellipse has strange blank

I'm making a drawing app with html5 canvas. User can draw ellipses and select both line color and fill color. (including transparent colors)
When selected color is not transparent, it works fine. But when transparent color is selected and border line width is thick, there are problems.(Q1 and Q2)

This is the image
http://tinypic.com/view.php?pic=28ry4z&s=9#.VoRs7U8jHSg

I'm using drawEllipse() method from below.
the relation of the bezier Curve and ellipse?

Does anyone solve this problems? Any help will be greatly appreciated.

[Q1] When the lineWidth is larger than the ellipse's width, there is a strange blank in the ellipse, and lineWidth is strangely thin. Internet Explorer works fine, but both Firefox and Safari web browsers have this problem. How can I change the blank area to be blue?

[Q2] I'm using transparent colors and I want to draw the ellipse with 2 colors.
(stroke is blue and fill is red) But the stroke color and the fill color are mixed and there is magenta area in the ellipse. How can I draw the ellipse with 2 colors?
(I want to change the magenta area to blue)
One time fill is preferred when possible.

Here is my code

// this method is from
// https://stackoverflow.com/questions/14169234/the-relation-of-the-bezier-curve-and-ellipse
function _drawEllipse(ctx, x, y, w, h) {
  var width_over_2 = w / 2;
  var width_two_thirds = w * 2 / 3;
  var height_over_2 = h / 2;

  ctx.beginPath();
  ctx.moveTo(x, y - height_over_2);
  ctx.bezierCurveTo(x + width_two_thirds, y - height_over_2, x + width_two_thirds, y + height_over_2, x, y + height_over_2);
  ctx.bezierCurveTo(x - width_two_thirds, y + height_over_2, x - width_two_thirds, y - height_over_2, x, y - height_over_2);
  ctx.closePath();
  ctx.stroke();
}

function ellipse_test() {
  var canvas = document.getElementById('sample1');
  var ctx = canvas.getContext('2d');

  var x = 100;
  var y = 100;
  var w = 40;
  var h = 100;
  ctx.lineWidth = 30;
  ctx.fillStyle = "rgba(255,0,0,0.5)";
  ctx.strokeStyle = "rgba(0,0,255,0.5)";
  ctx.globalCompositeOperation = "source-over";

  for (var r = 0; r < 50; r++) {
    _drawEllipse(ctx, x, y, r, r * 2);
    ctx.fill();
    x += 60;

    if (x > 1000) {
      x = 100;
      y += 200;
    }
  }
}

ellipse_test();
<canvas id="sample1" style="border:1px solid blue; background:black;" width="1200" height="800"></canvas>

this is the image on firefox

Upvotes: 2

Views: 901

Answers (1)

markE
markE

Reputation: 105015

Both problems are caused by the fact that multiple strokes/fills of semi-transparent colors over an area will cause that area to become a blend of colors (much like an artist blends multiple colors).

You can resolve both problems by converting semi-transparent colors into opaque colors:

function RGBAtoRGB(r, g, b, a, backgroundR,backgroundG,backgroundB){
  var r3 = Math.round(((1 - a) * backgroundR) + (a * r))
  var g3 = Math.round(((1 - a) * backgroundG) + (a * g))
  var b3 = Math.round(((1 - a) * backgroundB) + (a * b))
  return "rgb("+r3+","+g3+","+b3+")";
} 

// convert 50%-red foreground fill + 100% black background into opaque (=="red-brownish")
ctx.fillStyle = RGBAtoRGB(255,0,0,0.50, 0,0,0,1); // "rgba(255,0,0,0.5)";

// convert 50%-blue foreground stroke + 100% black background into opaque (=="blueish")
ctx.strokeStyle = RGBAtoRGB(0,0,255,0.50, 0,0,0,1); // "rgba(0,0,255,0.5)";

enter image description here

Example code refactored to use opaque fills & strokes:

ellipse_test();

// this method is from
// http://stackoverflow.com/questions/14169234/the-relation-of-the-bezier-curve-and-ellipse
function _drawEllipse(ctx, x, y, w, h) {
  var width_over_2 = w / 2;
  var width_two_thirds = w * 2 / 3;
  var height_over_2 = h / 2;

  ctx.beginPath();
  ctx.moveTo(x, y - height_over_2);
  ctx.bezierCurveTo(x + width_two_thirds, y - height_over_2, x + width_two_thirds, y + height_over_2, x, y + height_over_2);
  ctx.bezierCurveTo(x - width_two_thirds, y + height_over_2, x - width_two_thirds, y - height_over_2, x, y - height_over_2);
  ctx.closePath();
}

function ellipse_test() {
  var canvas = document.getElementById('sample1');
  var ctx = canvas.getContext('2d');

  var x = 100;
  var y = 100;
  var w = 40;
  var h = 100;
  ctx.lineWidth = 30;

  ctx.fillStyle = 'black';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  ctx.fillStyle = RGBAtoRGB(255, 0, 0, 0.50, 0, 0, 0, 1); // "rgba(255,0,0,0.5)";
  ctx.strokeStyle = RGBAtoRGB(0, 0, 255, 0.50, 0, 0, 0, 1); // "rgba(0,0,255,0.5)";


  ctx.globalCompositeOperation = "source-over";

  for (var r = 0; r < 50; r++) {
    _drawEllipse(ctx, x, y, r, r * 2);
    ctx.stroke();
    ctx.fill();
    x += 60;

    if (x > 1000) {
      x = 100;
      y += 200;
    }
  }
}


function RGBAtoRGB(r, g, b, a, backgroundR, backgroundG, backgroundB) {
  var r3 = Math.round(((1 - a) * backgroundR) + (a * r))
  var g3 = Math.round(((1 - a) * backgroundG) + (a * g))
  var b3 = Math.round(((1 - a) * backgroundB) + (a * b))
  return "rgb(" + r3 + "," + g3 + "," + b3 + ")";
}
body {
  background-color: ivory;
}
#canvas {
  border: 1px solid red;
  background-color=black;
}
<canvas id="sample1" width=1200 height=800></canvas>

Overlapping

...And obviously if you draw your ellipses very close together they will eventually overlap. That's what's causing your Q1-line thinning.

Upvotes: 1

Related Questions