【问题标题】:Canvas polygons not touching properly画布多边形未正确接触
【发布时间】:2015-06-04 01:00:00
【问题描述】:

所以我一直在摆弄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();

在这个fiddle中实现了一个简单的版本。

您可能会看到这些形状之间有一条细线。我怎样才能避免它?我已经尝试了here 的解决方案,但它们似乎并没有真正提到这种情况,因为我正在处理对角线。

【问题讨论】:

标签: javascript html canvas


【解决方案1】:

一个解决方案

您总是可以使用笔划线技巧,但取决于您的目标:

如果要显示多个相邻的多边形,您可以将多边形视为简单的正方形。

  • 在屏幕外画布中将它们画在彼此相邻的位置。这将产生一个没有间隙的结果。
  • 然后将主画布变换到您希望这些多边形出现的位置。根据目标添加旋转和/或倾斜。
  • 最后,将屏幕外画布作为图像绘制到主画布上。问题消失了。

这将为您提供准确的结果,无需额外的描边步骤,并且框的计算变得非常简单和快速(想想 2d 网格)。

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

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

示例

第 1 步 - 在屏幕外画布中绘制框:

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

(该示例使用屏幕显示结果,请参阅下一步了解屏幕外画布的使用)

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

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

第 2 步 - 转换主画布并在屏幕外画布中绘制

当绘制到带有变换集的主画布时,结果将是(伪随机变换只是为了显示):

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 />

第 3 步(可选)- 交互

如果您想与盒子交互,您只需应用相同的变换,然后为盒子添加路径并针对鼠标位置对其进行点击测试。重绘单个状态,通过清除擦除并拉回顶部的离屏画布:

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 />

【讨论】:

  • 感谢您的深入解答!这个解决方案似乎适用于我想要实现的目标。
【解决方案2】:

是的,当填充的多边形导致那个微小的间隙时,这很烦人。在理论上应该相交的对角线上尤其常见。

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

//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>

【讨论】:

  • 这种解决方法很好,除非多边形具有透明度,在这种情况下它会使问题变得更糟。
猜你喜欢
  • 1970-01-01
  • 2020-02-09
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 2012-11-28
  • 2016-11-22
  • 2013-05-29
  • 2023-03-09
相关资源
最近更新 更多