Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw segment of a donut with HTML5 canvas?

Tags:

html

canvas

As the title states. Is this possible?

Edit: When i say doughnut I mean a top, 2D view

Is the only option to draw a segment of a circle, then draw a segment of a smaller circle with the same origin and smaller radius over the top, with the colour of the background? That would be crap if so :(

like image 587
Andrew Bullock Avatar asked Nov 06 '11 20:11

Andrew Bullock


People also ask

How do I draw a circle in canvas HTML?

The arc() method creates an arc/curve (used to create circles, or parts of circles). Tip: To create a circle with arc(): Set start angle to 0 and end angle to 2*Math. PI. Tip: Use the stroke() or the fill() method to actually draw the arc on the canvas.

How do you create canvas in HTML5 give example?

Here is a simple <canvas> element which has only two specific attributes width and height plus all the core HTML5 attributes like id, name and class, etc. var canvas = document. getElementById("mycanvas"); Let us see a simple example on using <canvas> element in HTML5 document.


5 Answers

You do it by making a single path with two arcs.

You draw one circle clockwise, then draw a second circle going counter-clockwise. I won't go into the detail of it, but the way paths are constructed knows to take this as a reason to un-fill that part of the path. For more detail of what its doing you can this wiki article.

The same would work if you were drawing a "framed" rectangle. You draw a box one way (clockwise), then draw the inner box the other way (counter-clockwise) to get the effect.

Here's the code for a doughnut:

var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');

// Pay attention to my last argument!
//ctx.arc(x,y,radius,startAngle,endAngle, anticlockwise);  

ctx.beginPath()
ctx.arc(100,100,100,0,Math.PI*2, false); // outer (filled)
ctx.arc(100,100,55,0,Math.PI*2, true); // inner (unfills it)
ctx.fill();

Example:

http://jsfiddle.net/Hnw6a/

Drawing only a "segment" of it can be done by making the path smaller (you might need to use beziers instead of arc), or by using a clipping region. It really depends on how exactly you want a "segment"

Here's one example: http://jsfiddle.net/Hnw6a/8/

// half doughnut
ctx.beginPath()
ctx.arc(100,100,100,0,Math.PI, false); // outer (filled)
ctx.arc(100,100,55,Math.PI,Math.PI*2, true); // outer (unfills it)
ctx.fill();
like image 71
Simon Sarris Avatar answered Sep 26 '22 10:09

Simon Sarris


You can make a 'top view doughnut' (circle with hollow center) by stroking an arc. You can see an example of this here: http://phrogz.net/tmp/connections.html

enter image description here

The circles (with nib) are drawn by lines 239-245:

ctx.lineWidth = half*0.2;          // set a nice fat line width
var r = half*0.65;                 // calculate the radius
ctx.arc(0,0,r,0,Math.PI*2,false);  // create the circle part of the path
// ... some commands for the nib
ctx.stroke();                      // actually draw the path
like image 27
Phrogz Avatar answered Sep 27 '22 10:09

Phrogz


Yes, I understand how old this question is :)

Here are my two cents:

(function(){
 var annulus = function(centerX, centerY,
                       innerRadius, outerRadius,
                       startAngle, endAngle,
                       anticlockwise) {
  var th1 = startAngle*Math.PI/180;
  var th2 = endAngle*Math.PI/180;
  var startOfOuterArcX = outerRadius*Math.cos(th2) + centerX;
  var startOfOuterArcY = outerRadius*Math.sin(th2) + centerY;

  this.beginPath();
  this.arc(centerX, centerY, innerRadius, th1, th2, anticlockwise);
  this.lineTo(startOfOuterArcX, startOfOuterArcY);
  this.arc(centerX, centerY, outerRadius, th2, th1, !anticlockwise);
  this.closePath();
 }
 CanvasRenderingContext2D.prototype.annulus = annulus;
})();

Which will add a function "annulus()" similar to "arc()" in the CanvasRenderingContext2D prototype. Making the closed path comes in handy if you want to check for point inclusion.

With this function, you could do something like:

var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");
ctx.annulus(0, 0, 100, 200, 15, 45);
ctx.fill();

Or check this out: https://jsfiddle.net/rj2r0k1z/10/

Thanks!

like image 22
RinRisson Avatar answered Sep 26 '22 10:09

RinRisson


With WebGL (one of the contexts of the HTML5 canvas) that is possible. There are even some JS libraries for browsers that don't support/implement it yet - check out these links:

  • http://sixrevisions.com/web-development/how-to-create-an-html5-3d-engine/
  • http://slides.html5rocks.com/#landing-slide
  • http://sebleedelisle.com/2009/09/simple-3d-in-html5-canvas/
  • http://www.khronos.org/webgl/
  • http://webdesign.about.com/od/html5tutorials/f/is-there-a-3d-context-for-html5-canvas.htm
  • http://code.google.com/p/html-gl/
like image 41
Yahia Avatar answered Sep 27 '22 10:09

Yahia


Given the requirements, what @SimonSarris says satisfies the problem. But lets say you're like me and you instead want to "clear" a part of a shape that may be partially outside the bounds of the shape you're clearing. If you have that requirement, his solution won't get you want you want. It'll look like the "xor" in the image below.

enter image description here

The solution is to use context.globalCompositeOperation = 'destination-out' The blue is the first shape and the red is the second shape. As you can see, destination-out removes the section from the first shape. Here's some example code:

  explosionCanvasCtx.fillStyle = "red"
  drawCircle(explosionCanvasCtx, projectile.radius, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()

  explosionCanvasCtx.globalCompositeOperation = 'destination-out' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
  drawCircle(explosionCanvasCtx, projectile.radius + 20, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()

Here's the potential problem with this: The second fill() will clear everything underneath it, including the background. Sometimes you'll want to only clear the first shape but you still want to see the layers that are underneath it.

The solution to that is to draw this on a temporary canvas and then drawImage to draw the temporary canvas onto your main canvas. The code will look like this:

  diameter = projectile.radius * 2
  console.log "<canvas width='" + diameter + "' height='" + diameter + "'></canvas>"
  explosionCanvas = $("<canvas width='" + diameter + "' height='" + diameter + "'></canvas>")
  explosionCanvasCtx = explosionCanvas[0].getContext("2d")

  explosionCanvasCtx.fillStyle = "red"
  drawCircle(explosionCanvasCtx, projectile.radius, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()

  explosionCanvasCtx.globalCompositeOperation = 'destination-out' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
  durationPercent = (projectile.startDuration - projectile.duration) / projectile.startDuration
  drawCircle(explosionCanvasCtx, projectile.radius + 20, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()
  explosionCanvasCtx.globalCompositeOperation = 'source-over' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html

  ctx.drawImage(explosionCanvas[0], projectile.pos.x - projectile.radius, projectile.pos.y - projectile.radius) #center
like image 27
Daniel Kaplan Avatar answered Sep 29 '22 10:09

Daniel Kaplan