How to draw segment of a donut with HTML5 canvas?

人盡茶涼 提交于 2021-02-06 08:23:13

问题


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 :(


回答1:


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



回答2:


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



回答3:


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!




回答4:


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/



回答5:


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.

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



回答6:


Adapting/simplifying @Simon Sarris's answer to easily work with any angle gives the below:

To create an arc segment you draw an outer arc (of n radians) in one direction and then an opposite arc (of the same number of radians) at a smaller radius and fill in the resulting area.

var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
 
var angle = (Math.PI*2)/8;

var outer_arc_radius = 100;
var inner_arc_radius = 50.;

ctx.beginPath()

//ctx.arc(x,y,radius,startAngle,endAngle, anticlockwise); 
ctx.arc(100,100,outer_arc_radius,0,angle, false); // outer (filled)
// the tip of the "pen is now at 0,100
ctx.arc(100,100,inner_arc_radius,angle,0, true); // outer (unfills it)
ctx.fill();
<canvas id="canvas1" width="200" height="200"></canvas>


来源:https://stackoverflow.com/questions/8030069/how-to-draw-segment-of-a-donut-with-html5-canvas

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!