如何使用HTML5画布绘制甜甜圈的部分?

20

正如标题所述。这是否可能?

编辑:当我说甜甜圈时,我的意思是顶部的2D视图。

唯一的选择是绘制一个圆的部分,然后在其上方绘制一个半径相同但更小的圆的部分,背景色相同吗? 如果是这样的话,那就很糟糕了:(

6个回答

33

通过使用两个弧线创建单个路径来实现。

首先顺时针绘制一个圆,然后逆时针绘制第二个圆。我不会详细解释它如何构建路径,但路径构造的方式知道将其作为取消路径一部分的原因。如需更多详细信息,请参阅此维基百科文章

如果您正在绘制“框架”矩形,则同样有效。您可以以一个方向(顺时针)绘制一个框,然后以另一个方向(逆时针)绘制内框以获得效果。

下面是甜甜圈的代码:

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

示例:

http://jsfiddle.net/Hnw6a/

只绘制“线段”可以通过缩小路径(可能需要使用贝塞尔曲线而不是圆弧),或使用裁剪区域来实现。这真的取决于您想要一个“线段”的确切方式。

下面是一个示例: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();

1
为什么不用粗线条描绘,而不是填充和取消填充呢? - Phrogz
@Phrogz 你可能希望边框和填充颜色不同。 - user879121
1
@JustinY 你可以分别设置 strokeStylefillStyle - Phrogz

3
您可以通过绘制弧线来制作“顶部视图甜甜圈”(中空圆)。您可以在此处查看示例:http://phrogz.net/tmp/connections.html enter image description here 圆形(带有尖端)由第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

2

是的,我知道这个问题有多久了 :)

以下是我的建议:

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

这将在CanvasRenderingContext2D原型中添加一个类似于"arc()"的函数"annulus()"。如果您想检查点是否包含在内,则闭合路径非常方便。

使用此函数,您可以执行以下操作:

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

或者看一下这个链接:https://jsfiddle.net/rj2r0k1z/10/

谢谢!


谢谢。我今天会经常使用它的:) - T4NK3R

2

根据要求,@SimonSarris所说的满足了问题。但是假设您像我一样,而您希望“清除”部分形状,该部分可能部分超出要清除的形状范围。如果您有这个要求,他的解决方案将无法得到您想要的结果。它看起来像下面图像中的“异或”。

enter image description here

解决方案是使用 context.globalCompositeOperation = 'destination-out'。蓝色是第一个形状,红色是第二个形状。如您所见,destination-out 从第一个形状中移除了部分内容。以下是示例代码:
  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()

这里可能会出现的问题是:第二个fill()将清除其下方的所有内容,包括背景。有时您只想清除第一个形状,但仍要看到其下方的图层。
解决方法是在临时画布上绘制此内容,然后使用drawImage将临时画布绘制到主画布上。代码如下:
  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

1

澄清问题,我的意思是在二维平面上,抱歉。你的答案仍然适用吗? - Andrew Bullock
@AndrewBullock 它有一点作用 - 您可以在2D中使用WebGL,这应该为您想要绘制的内容提供更多选项... 对于WebGL中的简单2D示例,请参见https://developer.mozilla.org/en/WebGL/Adding_2D_content_to_a_WebGL_context - Yahia

0

将@Simon Sarris的答案进行改编/简化,以便轻松适用于任何角度,具体如下:

要创建一个弧段,您需要在一个方向上绘制一个外部弧(n弧度),然后在较小的半径处绘制一个相同弧度数的相反弧,并填充所得到的区域。

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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接