画布多边形不正确接触。

4

我一直在尝试使用canvas元素,但似乎遇到了一个非常烦人的问题,然而我还没有找到解决方法。 假设在画布上绘制了两个多边形,它们应该互相接触。其中一个多边形的绘制方式如下:

ctx.beginPath();
ctx.moveTo(oX,oY);
ctx.lineTo(oX=oX+k,oY=oY-h);
ctx.lineTo(oX=oX+k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY-h);
ctx.fill();

这个链接实现了一个简单的版本。

如你所见,这些形状之间有一条细线。我该如何避免它?我已经尝试了这里的解决方案,但它们似乎并不适用于我的情况,因为我正在处理对角线。


可能是如何避免HTML5 Canvas中的多边形边缘拼接伪影?的重复问题。 - Jose Gómez
2个回答

4

一种解决方案

你可以使用stroke-line技巧,但这取决于你的目标:

如果是要在一起显示多个多边形,你可以将多边形看作简单的正方形。

  • 在屏幕外的画布上将它们绘制成相邻的正方形。这样可以产生没有间隙的结果。
  • 然后根据需要将主画布转换到你想要多边形出现的位置。添加旋转和/或倾斜。
  • 最后,将屏幕外画布作为图像绘制到主画布中。问题解决了。

这将为您提供准确的结果,没有额外的描边步骤,而且计算盒子的过程变得非常简单和快速(考虑2d网格)。

不过,您必须使用屏幕外画布。如果您转换主画布并在形状中进行绘制,则会遇到与已有问题相同的问题。这是因为每个点都被转换,如果需要插值,则会为每个路径形状单独计算。绘制图像将在整个表面上添加插值,并且仅在存在间隙(不透明alpha)的地方。由于我们已经“无缝”,所以这不再是问题。

这将需要额外的步骤来正确放置它们,但这是一个简单的步骤。

示例

步骤1-将框绘制到屏幕外画布中:

此代码在屏幕外画布上绘制,结果为两个没有间隙的框:

snap

(该示例使用屏幕显示结果,请参见下一步骤以了解如何使用屏幕外画布)

var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "red";

ctx.fillRect(10, 10, 50, 50);
ctx.fillRect(60, 10, 50, 50);
<canvas/>

步骤二 - 转换主画布并在离屏画布中绘制

当设置变换后,绘制在主画布上的结果将会是(为了展示伪随机变换):

snap2

var ctx = document.querySelector("canvas").getContext("2d");

// off-screen canvas
var octx = document.createElement("canvas").getContext("2d");
octx.fillStyle = "red";
octx.fillRect(10, 10, 50, 50);
octx.fillRect(60, 10, 50, 50);

// transform and draw to main
ctx.translate(80, 0);
ctx.rotate(0.5, Math.PI);
ctx.transform(1, 0, Math.tan(-0.5),1, 0,0); // skew
ctx.drawImage(octx.canvas, 0, 0);
<canvas />

第三步(可选) - 交互

如果您想与方块进行交互,只需应用相同的变换,然后添加一个方块路径并将其与鼠标位置进行碰撞检测。重新绘制单个状态,通过清除擦除并在顶部绘制离屏画布:

var ctx = document.querySelector("canvas").getContext("2d");

// off-screen canvas
var octx = document.createElement("canvas").getContext("2d");
octx.fillStyle = "red";
octx.fillRect(10, 10, 50, 50);
octx.fillRect(60, 10, 50, 50);

// allow us to reuse some of the steps:
function getTransforms() {
  ctx.setTransform(1,0,0,1,0,0);
  ctx.translate(80, 0);
  ctx.rotate(0.5, Math.PI);
  ctx.transform(1, 0, Math.tan(-0.5),1, 0,0); // skew
}

function clear() {
  ctx.setTransform(1,0,0,1,0,0);
  ctx.clearRect(0,0,300,150);
}

function redraw() {
  ctx.drawImage(octx.canvas, 0, 0);
}

getTransforms();
redraw();

ctx.canvas.onmousemove = function(e) {
  var r = this.getBoundingClientRect(),
      x = e.clientX - r.left, y = e.clientY - r.top;
  
  // box 1 (for many, use array)
  ctx.beginPath();
  ctx.rect(10, 10, 50, 50);

  clear();         // these can be optimized to use state-flags
  getTransforms(); //  so they aren't redraw for every move...
  redraw();

  // just one box check here
  if (ctx.isPointInPath(x, y)) {
      ctx.fill();
  }
  
};
<canvas />


感谢您提供深入的答案!这个解决方案似乎可以很好地满足我的需求。 - Kazimieras

1

是的,填充多边形出现小间隙很让人烦恼。尤其是在理论上应该相交的对角线上。

一个常见的解决方法是在多边形周围放置一个半像素、颜色相同的描边:

//Some basic setup ...
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var oX = 50;
var oY = 50;
var h = 33;
var k = 50;
ctx.fillStyle = 'red';
ctx.strokeStyle='red';
ctx.lineWidth=0.50;

//Draw one polygon
ctx.beginPath();
ctx.moveTo(oX,oY);
ctx.lineTo(oX=oX+k,oY=oY-h);
ctx.lineTo(oX=oX+k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY-h);
ctx.fill();
ctx.stroke();
//Draw another polygon
oX = oX+k;
oY = oY+h;
ctx.beginPath();
ctx.moveTo(oX,oY);
ctx.lineTo(oX=oX+k,oY=oY-h);
ctx.lineTo(oX=oX+k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY-h);
ctx.fill();
ctx.stroke();

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");



//Some basic setup ...
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var oX = 50;
var oY = 50;
var h = 33;
var k = 50;
ctx.fillStyle = 'red';
ctx.strokeStyle='red';
ctx.lineWidth=0.50;

//Draw one polygon
ctx.beginPath();
ctx.moveTo(oX,oY);
ctx.lineTo(oX=oX+k,oY=oY-h);
ctx.lineTo(oX=oX+k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY-h);
ctx.fill();
ctx.stroke();
//Draw another polygon
oX = oX+k;
oY = oY+h;
ctx.beginPath();
ctx.moveTo(oX,oY);
ctx.lineTo(oX=oX+k,oY=oY-h);
ctx.lineTo(oX=oX+k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY-h);
ctx.fill();
ctx.stroke();
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>


2
那个解决方法还不错,除非多边形有透明度,否则会使问题变得更加严重。 - Jose Gómez

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