试图用圆圈创建一个模式?

时间:2016-09-12 21:11:42

标签: javascript canvas

我试图模仿a pattern我在互联网上找到了,但是我在中间得到了奇怪的线条,并试图连接另一组圆圈。

另外,当我尝试填充时,它会变成完全黑色。



console.log("grid");

var canvas = document.getElementById("canvas");
var image_b = document.getElementById("brown");
var image_g = document.getElementById("grey");

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;



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

var side = 160;
var side2 = 150;



ctx.strokeStyle = 'black';
ctx.fillStyle = 'white';

function draw() {

  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;


  ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
  var widthNbr = Math.ceil(window.innerWidth / side) + 1;
  var heightNbr = Math.ceil(window.innerHeight / side) + 1;

  var counter = 0;
  for (var i = 0; i < widthNbr; i++) {
    for (var j = 0; j < heightNbr; j++) {



      ctx.beginPath();

      var x = side * i + side / 2;
      var y = side * j + side / 2;

      var a = side * i + side / 2;
      var s = side * j + side / 2;

      var d = side * i + side / 2;
      var f = side * j + side / 2;

      var g = side * i + side / 2;
      var h = side * j + side / 2;

      var q = side * i + side / 2;
      var w = side * j + side / 2;

      var o = side * i + side / 2;
      var p = side * j + side / 2;


      var x1 = side2 * i + side2;
      var y1 = side2 * j + side2;

      var a1 = side2 * i + side2;
      var s1 = side2 * j + side2;

      var d1 = side2 * i + side2;
      var f1 = side2 * j + side2;

      var g1 = side2 * i + side2;
      var h1 = side2 * j + side2;

      var q1 = side2 * i + side2;
      var w1 = side2 * j + side2;

      var o1 = side2 * i + side2;
      var p1 = side2 * j + side2;


      ctx.arc(x, y, side / 2, 0, Math.PI * 2);
      ctx.arc(a, s, side / 2.5, 0, Math.PI * 2);
      ctx.arc(d, f, side / 3.5, 0, Math.PI * 2);
      ctx.arc(g, h, side / 5.3, 0, Math.PI * 2);
      ctx.arc(q, w, side / 9, 0, Math.PI * 2);
      ctx.arc(o, p, side / 18, 0, Math.PI * 2);
      ctx.lineWidth = 5;

      ctx.arc(x1, y1, side2 / 2, 0, Math.PI * 2);
      ctx.arc(a1, s1, side2 / 2.5, 0, Math.PI * 2);
      ctx.arc(d1, f1, side2 / 3.5, 0, Math.PI * 2);
      ctx.arc(g1, h1, side2 / 5.3, 0, Math.PI * 2);
      ctx.arc(q1, w1, side2 / 9, 0, Math.PI * 2);
      ctx.arc(o1, p1, side2 / 18, 0, Math.PI * 2);




      ctx.stroke();
      // ctx.fill();

      ctx.closePath();
      counter++;


    }
  }

}

draw();
&#13;
<canvas id="canvas"></canvas>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:1)

您必须将画布路径图纸视为纸上的铅笔画:

在路径声明(beginPath)之后,当您说ctx.arc(x, y, rad, 0, Math.PI*2)时,您的笔会转到坐标(xy),因为x并且yarc的中心位置,它将被放置在距离此中心rad的距离处以绘制圆圈。您的0告诉它从3点开始,所以在这种情况下,我们只需要将rad添加到x值。

此时,你的笔在纸上。

它绘制弧线,当你告诉它arc(x1, y1, rad, ...)时,它会直接转到坐标(x1+rady1)并绘制新弧。

这里的问题是你从未告诉它从纸上抬起铅笔,所以你可以看到从第一个弧上的最后一个点到下一个弧上的第一个点的线。

幸运的是,Canvas API附带了一组便捷的操作,“ Raise_the_pen_and_move_to_coordinates_x,y_without_ruining_my_paper ”简称为moveTo

通过告诉上下文轻轻抬起铅笔并移动到下一个第一个绘图点,在实际绘制弧线之前,你将避免所有这些尾随线。

所以基本上,对于三个弧,它将是:

// initialize a new drawing
ctx.beginPath();
// here we can set it directly because the pen is not on the paper yet
ctx.arc(x, y, rad, 0, Math.PI*2);
// tell it to raise the pen off the paper
//  and to go to the next starting point (3 o'clock in our case)
ctx.moveTo(x1 + rad, y1);
ctx.arc(x1, y1, rad, 0, Math.PI*2);
// once again
ctx.moveTo(x2 + rad, y2);
ctx.arc(x2, y2, rad, 0, Math.PI*2);
// now we've got clear independents arcs
ctx.stroke();

使用你的代码(你可以通过使用数组btw来清理很多)

var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

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

var side = 160;
var side2 = 150;

ctx.strokeStyle = 'black';
ctx.fillStyle = 'white';

function draw() {

  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;

  ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
  var widthNbr = Math.ceil(window.innerWidth / side) + 1;
  var heightNbr = Math.ceil(window.innerHeight / side) + 1;

  var counter = 0;
  for (var i = 0; i < widthNbr; i++) {
    for (var j = 0; j < heightNbr; j++) {

      ctx.beginPath();

      var x = side * i + side / 2;
      var y = side * j + side / 2;

      var a = side * i + side / 2;
      var s = side * j + side / 2;

      var d = side * i + side / 2;
      var f = side * j + side / 2;

      var g = side * i + side / 2;
      var h = side * j + side / 2;

      var q = side * i + side / 2;
      var w = side * j + side / 2;

      var o = side * i + side / 2;
      var p = side * j + side / 2;


      var x1 = side2 * i + side2;
      var y1 = side2 * j + side2;

      var a1 = side2 * i + side2;
      var s1 = side2 * j + side2;

      var d1 = side2 * i + side2;
      var f1 = side2 * j + side2;

      var g1 = side2 * i + side2;
      var h1 = side2 * j + side2;

      var q1 = side2 * i + side2;
      var w1 = side2 * j + side2;

      var o1 = side2 * i + side2;
      var p1 = side2 * j + side2;

      ctx.moveTo(x + side / 2, y);
      ctx.arc(x, y, side / 2, 0, Math.PI * 2);
      ctx.moveTo(a + side / 2.5, s);
      ctx.arc(a, s, side / 2.5, 0, Math.PI * 2);
      ctx.moveTo(d + side / 3.5, f)
      ctx.arc(d, f, side / 3.5, 0, Math.PI * 2);
      ctx.moveTo(g + side / 5.3, h)
      ctx.arc(g, h, side / 5.3, 0, Math.PI * 2);
      ctx.moveTo(q + side / 9, w)
      ctx.arc(q, w, side / 9, 0, Math.PI * 2);
      ctx.moveTo(o + side / 18, p)
      ctx.arc(o, p, side / 18, 0, Math.PI * 2);
      ctx.lineWidth = 5;

      ctx.moveTo(x1 + side2 / 2, y1)
      ctx.arc(x1, y1, side2 / 2, 0, Math.PI * 2);
      ctx.moveTo(a1 + side2 / 2.5, s1)
      ctx.arc(a1, s1, side2 / 2.5, 0, Math.PI * 2);
      ctx.moveTo(d1 + side2 / 3.5, f1)
      ctx.arc(d1, f1, side2 / 3.5, 0, Math.PI * 2);
      ctx.moveTo(g1 + side2 / 5.3, h1)
      ctx.arc(g1, h1, side2 / 5.3, 0, Math.PI * 2);
      ctx.moveTo(q1 + side2 / 9, w1)
      ctx.arc(q1, w1, side2 / 9, 0, Math.PI * 2);
      ctx.moveTo(o1 + side2 / 18, p1)
      ctx.arc(o1, p1, side2 / 18, 0, Math.PI * 2);

      ctx.stroke();

      counter++;

    }
  }

}

draw();
<canvas id="canvas"></canvas>

正如Spencer Wieczorek在上面的评论中正确指出的那样,为了获得你想要的结果,你还需要填充最大的弧线,但是我让你找到了做训练的方法。

另外,您在代码中使用的closePath()上的小注释,当我们看到有人滥用它时,他的名字可能会让人感到困惑,但请注意,它并不会结束您的路径声明。所有这一切都是lineTo(last_time_I_putted_the_pencil)。在闭环的情况下,它没有任何效果,因为last_time_I_putted_the_pencil === current_pencil_position_on_the_paper,但它通常是许多问题的根源。

还有一个小小的注释,对于用户来说更有经验(可能是几天/几周的OP):
其他操作允许我们从纸上抬起铅笔:转换命令 (主要是setTransform及其子集transformtranslaterotatescale)。
这些操作将首先抬起笔,然后移动纸张而不是笔。这在很多情况下都很方便 要将其设置回正常位置,您只需拨打setTransform(1,0,0,1,0,0)

答案 1 :(得分:0)

由于我对这个问题的回答太慢,请考虑这是 Kaiido 已经提供的优秀答案的附录。

在考虑这样的问题时,将值的计算与应用程序分开有时很有用。当然,这种理解只有经验,除非那是我们可以插入其他人的经验 - 这正是像StackOverflow这样的网站! :)

我们打算制作的图像完全由圆圈组成,因此我们可以通过创建一个仅为我们处理这一事物的函数来大大减少代码中的重复。有点像...

/* draw circle */
function drawCircle(x, y, r, LW) {
  context.lineWidth = LW;

  context.beginPath();
  context.arc(x, y, r, 0, Math.PI*2, true);
  context.fill();
  context.stroke();
}

虽然此功能仅向画布绘制一个圆圈,但我们可以传递可用于绘制所需元素的值。

参考图像由多组圆构成,每个圆组具有相同的(x,y)位置。如果我们知道X&amp; amp;的那些起始坐标Y,以及集合中最大圆的半径,然后我们可以创建一个函数来计算我们需要的值并将它们传递给上面的drawCircles()函数...

function circleSet(X, Y, Radius) {
  var count = 5; /* number of circles in each set */
  var step = Radius / count;
  var ln_width;
  var rad;

  while (count > 0) {
    ln_width = count > 3 ? 3 : (count > 1 ? 2 : 1);
    rad = count * step;

    drawCircle(X, Y, rad, ln_width);

    count--;
  }
}

while循环将count变量从5减少到1,并且在传递到上述drawCircle()函数之前计算线宽和半径值,以及(x,y)坐标

while循环第一行中的条件表示:

  • 如果count为4或5,则ln_width等于3;
  • 或者,如果count为2或3,则ln_width等于2;
  • ln_width等于1。

rad变量包含一个从Radius开始的值,并且对于循环的每次迭代都会减少1 x steps

既然已经准备好计算圆集参数,那么所需要的就是计算要传递给circleSet()函数的值,即; (x,y)坐标和每组的起始半径。

正如您的原始代码段所示,我们可以使用两个嵌套的for循环。一个处理垂直问题,一个处理水平问题,但首先我们需要做出一些决定。

如果我们想要在画布上有10个圆圈集,那么每个集合的宽度将是......

    var circleSet_Size = canvas.width / 10;

...因此每个圆集的最大半径将是......

    var circleSet_Radius = circleSet_Size / 2;

我们还需要绘制两个圆圈集来制作类似参考图像的东西,一个用于“背景”,一个用于“前景”。因此,我们需要确定每个通道的起始(x,y)坐标以及我们想要绘制的区域的宽度/高度。我们也可以为它创建一个函数。有点像...

function loopXYPosition(startX, startY) {
  for (var y = startY; y < (canvas.height + circleSet_Radius); y += circleSet_Size) {
    for (var x = startX; x < (canvas.width + circleSet_Radius); x += circleSet_Size) {

      circleSet(x, y, setRadius);

    }
  }
}

 /* 'background' pass */
loopXYPosition(0, 0);
/* 'foreground' pass */
loopXYPosition(circleSet_Radius, circleSet_Radius);

有了所有这些,我们可以将我们的功能整理成一个脚本。但在我们这样做之前,值得注意每次调用函数时需要计算的值以及一旦计算出哪些值保持静态。考虑到所有这些,我们最终会得到类似......

var circlePattern = (function() {
  /* define variables available
     to all functions */
  var canvas
    , ctx
    , cWidth
    , cHeight
    , circleCount
    , circleSet_Size
    , circleSet_Radius
    , P360;

  /* draw each cicle */
  function drawCircle(x, y, r, LW) {
    ctx.beginPath();
    ctx.lineWidth = LW;
    ctx.arc(x, y, r, 0, P360, true);
    ctx.fill();
    ctx.stroke();
  }

  /* calculate each set of circles (circle-set) */
  function circleSet(X, Y, R) {
    var count = circleCount,
      radiusSteps = R / count,
      ln_width,
      rad;

    while (count > 0) {
      ln_width = count > 3 ? 2.5 : (count > 1 ? 2 : 1.5);
      rad = count * radiusSteps;

      drawCircle(X, Y, rad, ln_width);

      count--;
    }

  }

  function loopXYPosition(startX, startY) {
    /* add circleSet_Radius to canvas width
       and height to make sure we draw right
       up to the edges of the canvas */
    var cHcR = cHeight + circleSet_Radius;
    var cWcR = cWidth + circleSet_Radius;

    /* to get the effect we want we need to create
       a little padding around each circle-set,
       therefore we reduce the circelSet_Radius value
       by 5% before passing to drawCircleSet() */
    var setRadius = circleSet_Radius * 0.95;

    /* step across and down canvas in
       increments of 'circleSet_Size' */
    for (var y = startY; y < cHcR; y += circleSet_Size) {
      for (var x = startX; x < cWcR; x += circleSet_Size) {

        circleSet(x, y, setRadius);

      }
    }

  }

  function begin(NoC, cCount) {
    var numberOfCircles = NoC;

    /* set variables needed later */
    circleSet_Size = cWidth / numberOfCircles;
    circleSet_Radius = circleSet_Size / 2;
    circleCount = cCount;

    /* draw rows of circles */
    loopXYPosition(0, 0);
    loopXYPosition(circleSet_Radius, circleSet_Radius);
  }

  /* initialise canvas */
  function init(e) {
    /* Set variables to use later */
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');

    cWidth = canvas.width;
    cHeight = canvas.height;

    P360 = Math.PI * 2;

    /* fillStyle & strokeStyle are properties
       of the Canvas, so we only need to set
       them once here */
    ctx.fillStyle = '#fff';
    ctx.strokeStyle = '#000';

    /* fill canvas (background) */
    ctx.fillRect(0, 0, cWidth, cHeight);

    /* first argument: number of horizontal circle-sets
       second argument: number of circles in each set 
       !Try different values here! */
    begin(7, 6);
  }

  return {
    go: init
  };
}());

window.onload = circlePattern.go;
body {
    color:#000;
    background-color:#fff;
    font-family:sans-serif;
}
div.box {
    width:100%;
    height:auto;
    text-align:center;
    margin:2em auto;
    display:block;
}
div.box h3 {
    font-size:1.3em;
    line-height:2.3em;
}
#refimg, #canvas {
    width:600px;
    margin:0 auto;
    clear:both;
    display:block;
}
#refimg {
    height:360px;
}
#canvas {
    height:400px;
}
<div class="box">
    <h3>Refernece Image</h3>
    <img id="refimg" src="http://i.stack.imgur.com/Uxm2Z.jpg)" alt="image" title="reference image" />
</div>

<div class="box">
    <h3>Canvas Image</h3>
    <canvas id="canvas" width="600" height="400" title="Canvas image"></canvas>
</div>

结果不尽相同,但非常接近。除了通过它的(x,y)坐标之外,circleSet()函数每次调用时都会生成相同的值序列,因此这些值可以计算一次并存储在Object中,但是为了简单起见,我把它包含在这里,并突出显示事件的顺序。

begin()函数中输入不同的值并在setRadius函数中使用loopXYPosition()会产生一些有趣的结果。

我希望此处概述的流程为您提供一些有关HTML5 Canvas API的持续探索的提示。