Javascript canvas-在矩形中相交的圆孔或如何合并多个弧形路径 [英] Javascript canvas - intersecting circle holes in rectangle or how to merge multiple arc paths

查看:91
本文介绍了Javascript canvas-在矩形中相交的圆孔或如何合并多个弧形路径的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到的问题非常简单.这是如何在形状中绘制孔?"的一种变体.这个问题的经典答案是在同一路径上简单绘制两个形状,但顺时针绘制实心而逆时针绘制孔".很好,但是我需要的孔"通常是一个复合形状,由多个圆圈组成.

The issue I have is very straightforward. This is a variation of the "How can I draw a hole in a shape?" question, to which the classic answer is "Simply draw both shapes in the same path, but draw the solid clockwise and the "hole" counterclockwise." That's great but the "hole" I need is often a compound shape, consisting of multiple circles.

视觉描述: http://i.imgur.com/9SuMSWT.png .

jsfiddle: http://jsfiddle.net/d_panayotov/44d7qekw/1/

jsfiddle: http://jsfiddle.net/d_panayotov/44d7qekw/1/

context = document.getElementsByTagName('canvas')[0].getContext('2d');
// green background
context.fillStyle = "#00FF00";
context.fillRect(0,0,context.canvas.width, context.canvas.height);
context.fillStyle = "#000000";
context.globalAlpha = 0.5;
//rectangle
context.beginPath();
context.moveTo(0, 0);
context.lineTo(context.canvas.width, 0);
context.lineTo(context.canvas.width, context.canvas.height);
context.lineTo(0, context.canvas.height);
//first circle
context.moveTo(context.canvas.width / 2 + 20, context.canvas.height / 2);
context.arc(context.canvas.width / 2 + 20, context.canvas.height / 2, 50, 0, Math.PI*2, true);
//second circle
context.moveTo(context.canvas.width / 2 - 20, context.canvas.height / 2);
context.arc(context.canvas.width / 2 - 20, context.canvas.height / 2, 50, 0, Math.PI*2, true);
context.closePath();
context.fill();

已经提出了多种解决方案,我感到我的问题一直是误导性的.因此,这里有更多信息: 我需要矩形区域充当阴影.这是我正在制作的游戏的屏幕截图(希望这不违反规则): http://i .imgur.com/tJRjMXC.png .

Multiple solutions have been proposed and I feel that my question has been misleading. So here's more info: I need the rectangle area to act as a shade. Here's a screenshot from the game I'm making (hope this is not against the rules): http://i.imgur.com/tJRjMXC.png.

  • 该矩形的alpha值应小于1.0.
  • 孔"中显示的内容是应用阴影之前在画布上绘制的内容.

@markE:

  • 或者...淘汰"(擦除)双圆圈...- "destination-out"将画布内容替换为设置的背景. http://jsfiddle.net/d_panayotov/ab21yfgd/-孔是蓝色而不是绿色.
  • 另一方面...- "source-atop"要求在定义剪贴蒙版之后绘制内容.在我的情况下,这是低效的(将光绘制为同心圆,阴影区域仍然可见).
  • Alternatively...to "knockout" (erase) the double-circles... - "destination-out" replaces the canvas content with the set background. http://jsfiddle.net/d_panayotov/ab21yfgd/ - The holes are blue instead of green.
  • On the other hand... - "source-atop" requires content to be drawn after defining the clipping mask. This in my case would be inefficient (Light is drawn as concentric circles, shaded area still visible).

@hobberwickey: 这是静态背景,而不是实际的画布内容.但是,我可以像使用"source-atop"一样使用clip(),但是效率低下.

@hobberwickey: That's a static background, not actual canvas content. I can however use clip() the same way I would use "source-atop" but that would be inefficient.

我目前实施的解决方案: http://jsfiddle.net/d_panayotov/ewdyfnj5/.我只是在主画布内容上绘制了裁剪后的矩形(在内存中的画布中).有更快/更好的解决方案吗?

The solution that I have implemented right now: http://jsfiddle.net/d_panayotov/ewdyfnj5/. I'm simply drawing the clipped rectangle (in an in-memory canvas) over the main canvas content. Is there a faster/better solution?

推荐答案

由于它的简单性,我几乎不敢发布此答案的第一部分,但为什么不只在坚实的背景上填充两个圆圈呢?

I almost dread posting the first part of this answer because of its simplicity, but why not just fill 2 circles on a solid background?

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var r=50;

ctx.fillStyle='rgb(0,174,239)';
ctx.fillRect(0,0,cw,ch);

ctx.fillStyle='white'
ctx.beginPath();
ctx.arc(cw/2-r/2,ch/2,r,0,Math.PI*2);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(cw/2+r/2,ch/2,r,0,Math.PI*2);
ctx.closePath();
ctx.fill();

body{ background-color: ivory; }
#canvas{border:1px solid red;}

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

或者...淘汰"(删除)双圆圈...

如果您想让2个圆圈向下淘汰"蓝色像素,以使双圆圈透明和&揭示下面的网页背景,然后您可以使用合成敲除"圈子:context.globalCompositeOperation='destination-out

If you want the 2 circles to "knockout" the blue pixels down so the double-circles are transparent & reveal the webpage background underneath, then you can use compositing to "knockout" the circles: context.globalCompositeOperation='destination-out

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var r=50;


// draw the blue background
// The background will be visible only outside the double-circles
ctx.fillStyle='rgb(0,174,239)';
ctx.fillRect(0,0,cw,ch);


// use destination-out compositing to "knockout" 
// the double-circles and thereby revealing the
// ivory webpage background below
ctx.globalCompositeOperation='destination-out';

// draw the double-circles
// and effectively "erase" the blue background
ctx.fillStyle='white'
ctx.beginPath();
ctx.arc(cw/2-r/2,ch/2,r,0,Math.PI*2);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(cw/2+r/2,ch/2,r,0,Math.PI*2);
ctx.closePath();
ctx.fill();

// always clean up! Set compositing back to its default
ctx.globalCompositeOperation='source-over';

body{ background-color: ivory; }
#canvas{border:1px solid red;}

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

另一方面...

如果需要将这些双圆形像素隔离为包含路径,则可以使用合成绘制双圆形而不绘制蓝色背景.

If you need to isolate those double-circle pixels as a containing path, then you can use compositing to draw into the double-circles without drawing into the blue background.

这是另一个例子:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var r=50;

var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/mm.jpg";
function start(){

  // fill the double-circles with any color
  ctx.fillStyle='white'
  ctx.beginPath();
  ctx.arc(cw/2-r/2,ch/2,r,0,Math.PI*2);
  ctx.closePath();
  ctx.fill();
  ctx.beginPath();
  ctx.arc(cw/2+r/2,ch/2,r,0,Math.PI*2);
  ctx.closePath();
  ctx.fill();

  // set compositing to source-atop
  // New drawings are only drawn where they
  //    overlap existing (non-transparent) pixels
  ctx.globalCompositeOperation='source-atop';


  // draw your new content
  // The new content will be visible only inside the double-circles
  ctx.drawImage(img,0,0);

  // set compositing to destination-over
  // New drawings will be drawn "behind" 
  //    existing (non-transparent) pixels
  ctx.globalCompositeOperation='destination-over';

  // draw the blue background
  // The background will be visible only outside the double-circles
  ctx.fillStyle='rgb(0,174,239)';
  ctx.fillRect(0,0,cw,ch);

  // always clean up! Set compositing back to its default
  ctx.globalCompositeOperation='source-over';

}

body{ background-color: ivory; }
#canvas{border:1px solid red;}

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

{回答中还添加了其他想法}

技术要点: xor合成仅通过翻转像素上的alpha值而不会将像素的r,g,b部分归零.在某些情况下,异或像素的Alpha值将不为零,并且rgb将再次显示.最好使用终点输出"合成,将像素值(r,g,b,a)的所有部分都清零,以免它们意外返回困扰您.

A technical point: xor compositing works by flipping just the alpha values on pixels but does not also zero-out the r,g,b portion of the pixel. In some cases, the alphas of the xored pixels will be un-zeroed and the rgb will again display. It's better to use 'destination-out' compositing where all parts of the pixel value (r,g,b,a) are zeroed out so they don't accidentally return to haunt you.

请确保... 即使在您的示例中并不重要,您也应始终从maskCtx.beginPath()开始路径绘制命令.这表示任何先前图形的结束和新路径的开始.

Be sure... Even though it's not critical in your example, you should always begin your path drawing commands with maskCtx.beginPath(). This signals the end of any previous drawing and the beginning of a new path.

一个选项:我看到您使用同心圆在圆心处引起更大的露底".如果您想要更渐进的显示,则可以使用修剪的阴影(或径向渐变)而不是同心圆来剔除内存中的圆.

One option: I see you're using concentric circles to cause greater "reveal" at the center of your circles. If you want a more gradual reveal, then you could knockout your in-memory circles with a clipped-shadow (or radial gradient) instead of concentric circles.

除此之外,您覆盖内存画布的解决方案应该可以很好地工作(以内存画布使用的内存为代价).

Other than that, you solution of overlaying an in-memory canvas should work well (at the cost of the memory used for the in-memory canvas).

祝你游戏好运!

这篇关于Javascript canvas-在矩形中相交的圆孔或如何合并多个弧形路径的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆