Sanjay Nakate
Sanjay Nakate

Reputation: 2098

How to draw a smooth oval in canvas

I am trying to draw a smooth oval using ctx.clip property in my canvas.I have done with drawing part i am facing problem with oval arc line clarity.Any one have any idea regarding this just let me know? Here is my code.

<canvas id="c" width="400" height="400"></canvas>


var canvas = new fabric.Canvas('c');
var ctx = canvas.getContext('2d');
var cx=180;
var cy=200;
var w=300;
var h=250;
    // Quadratric curves example
    ctx.beginPath();
    var lx = cx - w/2,
        rx = cx + w/2,
        ty = cy - h/2,
        by = cy + h/2;
    var magic = 0.551784;
    var xmagic = magic*w/2;
    var ymagic = h*magic/2;
    ctx.moveTo(cx,ty);
    ctx.bezierCurveTo(cx+xmagic,ty,rx,cy-ymagic,rx,cy);
    ctx.bezierCurveTo(rx,cy+ymagic,cx+xmagic,by,cx,by);
    ctx.bezierCurveTo(cx-xmagic,by,lx,cy+ymagic,lx,cy);
    ctx.bezierCurveTo(lx,cy-ymagic,cx-xmagic,ty,cx,ty);

    ctx.fill();
    ctx.stroke();
    ctx.clip();



var text;
text = new fabric.Text('Honey', {
  fontSize: 50,
  left: 150,
  top: 150,
  lineHeight: 1,
  originX: 'left',
  fontFamily: 'Helvetica',
  fontWeight: 'bold'
});
canvas.add(text);

Here is my fiddle link

You can see this output over here the line border of oval not much clear. Here is output of screen shot

Upvotes: 5

Views: 2492

Answers (5)

amit gupta
amit gupta

Reputation: 1330

try this it will help you //Script

var canvas = new fabric.Canvas('c');
 var w;
 var h;
 var ctx = canvas.getContext('2d');
 w=canvas.width / 4;
 h=canvas.height / 2.4;
canvas.clipTo = function(ctx) {
 ctx.save();
 ctx.scale(2, 1.2);
 ctx.arc(w, h, 90, 0, 2 * Math.PI, true);
 ctx.stroke();
 ctx.restore();
}

Fiddle Demo

Upvotes: 3

Xotic750
Xotic750

Reputation: 23482

Here is another alternative routine, but it looks visually the same as the other methods. This is primarily to do with the finite resolution of the display device, though you may be able to make some improvement using a thicker pencil, optical illusions or performing some anti-aliasing. Otherwise I think you will have to accept what you have.

Javascript

var canvas = new fabric.Canvas('c'),
    ctx = canvas.getContext("2d"),
    steps = 100,
    step = 2 * Math.PI / steps,
    h = 200,
    k = 180,
    r = 150,
    factor = 0.8,
    theta,
    x,
    y,
    text;

ctx.beginPath();

for (theta = 0; theta < 2 * Math.PI; theta += step) {
    x = h + r * Math.cos(theta);
    y = k - factor * r * Math.sin(theta);
    ctx.lineTo(x, y);
}

ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.clip();

text = new fabric.Text('Honey', {
    fontSize: 50,
    left: 150,
    top: 150,
    lineHeight: 1,
    originX: 'left',
    fontFamily: 'Helvetica',
    fontWeight: 'bold'
});

canvas.add(text);

jsfiddle

Note: by changing the number of steps and the factor, you can create other shapes: circles, squares, hexagons, other polygons ....

Upvotes: 1

markE
markE

Reputation: 105015

One problem is in the nature of your display screen...

Since pixels are rectangles and you're drawing a curve, your result will have "jaggies" as the curve tries to fit itself in rectangular spaces.

You can use an optical illusion to trick the eye into seeing a less jagged oval.

An optical trick:

Reduce the contrast between the background color and the oval color.

This is not a cure...the jaggies are still there.

But the eye recognizes less contrast and perceives the oval as more smooth.

So if your design accommodates this style change, this optical illusion might help.

Here's code and a Fiddle: http://jsfiddle.net/m1erickson/vDWR3/

var cx=180;
var cy=200;
var w=300;
var h=250;

// Start with a less-contrasting background
ctx.fillStyle="#ddd";
ctx.fillRect(0,0,canvas.width,canvas.height);

ctx.beginPath();
var lx = cx - w/2,
    rx = cx + w/2,
    ty = cy - h/2,
    by = cy + h/2;
var magic = 0.551784;
var xmagic = magic*w/2;
var ymagic = h*magic/2;
ctx.moveTo(cx,ty);
ctx.bezierCurveTo(cx+xmagic,ty,rx,cy-ymagic,rx,cy);
ctx.bezierCurveTo(rx,cy+ymagic,cx+xmagic,by,cx,by);
ctx.bezierCurveTo(cx-xmagic,by,lx,cy+ymagic,lx,cy);
ctx.bezierCurveTo(lx,cy-ymagic,cx-xmagic,ty,cx,ty);

ctx.fillStyle="#555";
ctx.strokeStyle=ctx.fillStyle;
ctx.lineWidth=1.5;
ctx.stroke();

Upvotes: 2

Ruan Mendes
Ruan Mendes

Reputation: 92274

A possibility is to use a thicker line, it's not great but it's better

ctx.lineWidth = 3;

http://jsfiddle.net/xadqg/7/

I also noticed that using quadraticCurveTo seems to anti alias the line http://jsfiddle.net/d4JJ8/298/ I didn't change your code to use it but the jsfiddle I posted shows that it's true. You should modify your code and try it

Upvotes: 0

OBV
OBV

Reputation: 1259

<canvas id="thecanvas" width="400" height="400"></canvas>

<script>
var canvas = document.getElementById('thecanvas');

if(canvas.getContext) 
{
  var ctx = canvas.getContext('2d');
  drawEllipse(ctx, 10, 10, 100, 60);
  drawEllipseByCenter(ctx, 60,40,20,10);
}

function drawEllipseByCenter(ctx, cx, cy, w, h) {
  drawEllipse(ctx, cx - w/2.0, cy - h/2.0, w, h);
}

function drawEllipse(ctx, x, y, w, h) {
  var kappa = .5522848,
      ox = (w / 2) * kappa, // control point offset horizontal
      oy = (h / 2) * kappa, // control point offset vertical
      xe = x + w,           // x-end
      ye = y + h,           // y-end
      xm = x + w / 2,       // x-middle
      ym = y + h / 2;       // y-middle

  ctx.beginPath();
  ctx.moveTo(x, ym);
  ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
  ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
  ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
  ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
  ctx.closePath();
  ctx.stroke();
}

</script>

http://jsbin.com/ovuret/2/edit

Upvotes: 0

Related Questions