How to Draw an Oval in Html5 Canvas

How to draw an oval in html5 canvas?

updates:

  • scaling method can affect stroke width appearance
  • scaling method done right can keep stroke width intact
  • canvas has ellipse method that Chrome now supports
  • added updated tests to JSBin

JSBin Testing Example (updated to test other's answers for comparison)

  • Bezier - draw based on top left containing rect and width/height
  • Bezier with Center - draw based on center and width/height
  • Arcs and Scaling - draw based on drawing circle and scaling

    • see Deven Kalra's answer
  • Quadratic Curves - draw with quadratics

    • test appears to not draw quite the same, may be implementation
    • see oyophant's answer
  • Canvas Ellipse - using W3C standard ellipse() method

    • test appears to not draw quite the same, may be implementation
    • see Loktar's answer

Original:

If you want a symmetrical oval you could always create a circle of radius width, and then scale it to the height you want (edit: notice this will affect stroke width appearance - see acdameli's answer), but if you want full control of the ellipse here's one way using bezier curves.

<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(); // not used correctly, see comments (use to close off open path)
ctx.stroke();
}

</script>

Canvas: arc(75,75,50,0,3.1415,true) draws oval instead of circle

If you change this line:

<canvas id=c1 style="width:400;height:400">

to:

<canvas id=c1 width=400 height=400></canvas>

it should work. Don't use CSS to set Canvas sizes as this only affects the element but not the bitmap itself. For canvas you need to use it's dedicated properties (width/height) to also set the bitmap size or the bitmap is just stretched/scaled to match the size of the element.

The default size of canvas if not specified is 300x150 pixels. In this case those pixels are stretched (as an image) to 400x400 which is why you get an oval instead.

How to draw a circle in HTML5 Canvas using JavaScript?

Here is how to draw a circle using JavaScript in HTML5:

const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 70;

context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
body {
margin: 0px;
padding: 0px;
}
<canvas id="myCanvas" width="578" height="200"></canvas>

Drawing multiple oval shapes on HTML canvas using JavaScript having an extra line

You are missing the context.beginPath. Please see this JSFIddle: https://jsfiddle.net/zwcd7hcw/

function drawOvalShape(context, center_x, center_y, width, height){
context.beginPath()
context.ellipse(center_x, center_y, width, height, 90 * Math.PI/180, 0, 2 * Math.PI);
context.stroke();
}

 var canvas = document.getElementById("myCanvas");    var context = canvas.getContext("2d");    var center_x = 200;    var center_y = 100;    var width = 100;    var height = 200;    drawOvalShape(context, 200, 100, 100, 200);    drawOvalShape(context, 200, 100, 80, 180);    drawOvalShape(context, 200, 100, 60, 160);    drawOvalShape(context, 200, 100, 40, 140);    drawOvalShape(context, 200, 100, 20, 120);
function drawOvalShape(context, center_x, center_y, width, height){ context.beginPath() context.ellipse(center_x, center_y, width, height, 90 * Math.PI/180, 0, 2 * Math.PI);
context.stroke(); }
<canvas id="myCanvas" width="400" height="200" style="border:1px solid #000000;"></canvas>

Examples of the 3 ways to draw a circle on HTML5 canvas

It's all based on the same principle: You can draw a circle or ellipse by taking their parametric formula (x²+y² = some constant, and x²/a + y²/b = some constant, respectively), and joining up valid points for those formulae. Somehow. You can simply pick points that are so close that there's nothing "to join" since subsequent points are literally "the next pixel" (which is what arcTo does), but you can also space them further apart, and then you can join things up using:

  • lines
  • quadratic bezier curves
  • cubic bezier curve
  • literally any kind of curve, since you know all the points it needs to pass through

The challenge is not in joining up the points, it's knowing how far apart the points can be before things start to look pretty shitty.

For instance, for quadratic Bezier curves, you need 8 or more curves for things to look decent. Fewer than that, and it starts to look bad. For cubic Bezier curves, 4 is usually enough. For other curve types it really depends on the maths, which either you, or someone else, will have to work out to determine how many points you're going to need for things to look right.

But really, use arcTo, or even circle() and ellipse() if the programming language you're using has those. Don't approximate if you can just directly draw what you needed to draw.

How to draw a smooth oval in canvas

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();


Related Topics



Leave a reply



Submit