如何使用JavaScript中的for循环绘制完整的多边形?

时间:2019-07-09 22:42:28

标签: javascript html html5-canvas

所以我正在尝试利用HTML的Canvas绘制凸多边形。

在下面的代码中,t2是一个点数组(我已经声明了该类,该代码可以正常工作,所有内容,getX()返回X,getY()返回Y。)

绘图功能起作用,直到到达for循环之后的代码为止。我想画一条连接第一个点和最后一个点的线,但是由于某种原因,它没有执行。

var i;
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
for(i = 0; i<t2.length-2; i++){
    var t3 = t2[i];
    var t4 = t2[i+1];


    ctx.moveTo(t3.getX(), t3.getY());
    ctx.lineTo(t4.getX(), t4.getY());
    ctx.stroke();
}
var t5 = t2[t2.length-1];
var t6 = t2[0];



ctx.moveTo(t5.getX(), t5.getY());
ctx.lineTo(t6.getX(), t6.getY());
ctx.stroke();

画布应该显示一条连接点t5和t6的线,但是什么也没发生。 for循环中的所有内容都可以使用,但是之后的所有内容都不能使用。

2 个答案:

答案 0 :(得分:2)

closePath()是您的朋友,在这里...请参阅:https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/closePath

在没有可用示例的情况下,以下是对您所需要的进行优化的粗略猜测。

let c = document.getElementById("myCanvas");
let ctx = c.getContext("2d");
//Start the path.
ctx.beginPath();
//Move to the initial vector.
ctx.moveTo(t2[0].getX(), t2[0].getY());
//Loop through the rest of the vectors skipping the first one.
for(let i = 1; i < t2.length; i++){
    //Add a line to the path. This always originates from the previous lineTo end vector.
    ctx.lineTo(t2[i].getX(), t2[i].getY());
}
//closePath() attempts to draw a line from the last vector to the first vector in the current path.
ctx.closePath();
//Stroke.
ctx.stroke();

答案 1 :(得分:1)

代码正在做一些奇怪的事情。

  1. 它正在循环内调用stroke。这意味着它将绘制第一条线,然后绘制第一条和第二条线,然后绘制第一条,第二条和第三条线,等等...

    如果要分别绘制每条线,则需要在每次调用beginPath之后调用stroke。如果只想绘制所有线条,则应该在末尾调用一次笔画。

  2. 每个段都使用moveTo / lineTo。这不是制作多边形,而是绘制N条线段。区别在于,如果您调用fill而不是笔触,它将失败,因为代码没有创建多边形,而是创建了单独的线。

  3. 您不需要第一个moveTo。呼叫lineTo之后,对beginPath的第一次呼叫会自动是moveTo(感谢@Kaiido)

  4. 最后一个点与第一个点没有连接。

    您可以使用closePath

这是工作示例

class Vec2 {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  getX() {
    return this.x;
  }
  getY() {
    return this.y;
  }
}
const t2 = [
  new Vec2(10, 10),
  new Vec2(100, 10),
  new Vec2(100, 100),
  new Vec2(10, 100),
];

const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
ctx.beginPath();
for(let i = 0; i < t2.length; ++i){
    const t3 = t2[i];
    ctx.lineTo(t3.getX(), t3.getY());
}
ctx.closePath();
ctx.stroke();
<canvas id="myCanvas"></canvas>

不过,如果代码更像JavaScript,有些事情可能会更简单。

例如,如果我们使Vec2将x和y保留在一个数组中,并用get返回该数组,则代替

ctx.lineTo(t3.getX(), t3.getY());

我们可以做到

ctx.lineTo(...t3.get());

现在,代码正在使用this.value存储2个值,如果我们仍然需要它们,我们可以为x和y添加getter。然后t3.getX()变成t3.x

另一个是您可以使用for ... of代替循环计数器

for (const point of t2) {
   ctx.lineTo(...point.get());
}

只是一些建议。

class Vec2 {
  constructor(x, y) {
    this.value = [x, y];
  }
  get() {
    return this.value;
  }
  get x() {
    return this.value[0];
  }
  set x(v) {
    this.value[0] = v;
  }
  get y() {
    return this.value[1];
  }
  set y(v) {
    this.value[1] = v;
  }
}
const t2 = [
  new Vec2(10, 10),
  new Vec2(100, 10),
  new Vec2(100, 100),
  new Vec2(10, 100),
];

const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
ctx.beginPath();
for (const point of t2) {
    ctx.lineTo(...point.get());
}
ctx.closePath();
ctx.stroke();
<canvas id="myCanvas"></canvas>

除了使用for ... of循环外,您还可以使用forEach循环

    t2.forEach(point => ctx.lineTo(...point.get()));

class Vec2 {
  constructor(x, y) {
    this.value = [x, y];
  }
  get() {
    return this.value;
  }
  get x() {
    return this.value[0];
  }
  set x(v) {
    this.value[0] = v;
  }
  get y() {
    return this.value[1];
  }
  set y(v) {
    this.value[1] = v;
  }
}
const t2 = [
  new Vec2(10, 10),
  new Vec2(100, 10),
  new Vec2(100, 100),
  new Vec2(10, 100),
];

const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
ctx.beginPath();
t2.forEach(point => ctx.lineTo(...point.get()));
ctx.closePath();
ctx.stroke();
<canvas id="myCanvas"></canvas>