p5js: How do I make my randomly generated circle packing sketch/canvas responsive when the window is resized?

限于喜欢 提交于 2020-12-13 04:58:05

问题


When I load the window from a narrow size and make it wider, the added canvas space is white.

Canvas resize from narrow to wide:

When I load from a larger window to a smaller size, the canvas stays in the larger size, while HTML and CSS elements respond. In the second picture, the circles are supposed to cut off at the same point at the black rectangle.

I think this is happening because the circles are randomly generated and the layout of them is different whenever the page loads/reloads. Basically, I have to "refresh" the window for the new resized canvas to work. BTW, I tried WindowResized() already.*

var font;
var colors;
var bolder;
var canvas;

setup = () => {

    frameRate(2.5);

    function windowResized() {
        resizeCanvas(windowWidth, windowHeight);
    }

    canvas = createCanvas(windowWidth, windowHeight);
    canvas.position(0, 0);
    canvas.style('z-index', '-1');

    colors = [color(255, 0, 0), color(1, 130, 83), color(0, 0, 255), color(255, 255, 0), color(102, 0, 102), color(255, 107, 31)];

    for (var c = 0; c < 1000; c++) {
        var circle = {
            x: random(width),
            y: random(height),
            r: random(90, 15)
        };

        var overlap = false;

        var protection = 0;

        for (var j = 0; j < circles.length; j++) {
            var other = circles[j];
            var d = dist(circle.x, circle.y, other.x, other.y);
            if (d < circle.r + other.r) {
                overlap = true;
            }
        }

        if (!overlap) {
            circles.push(circle);
        }

        protection++;
        if (protection > 10000) {
            break;
        }
    }

    for (var i = 0; i < circles.length; i++) {

        fill(255, 255, 255);
        strokeWeight(4);
        ellipse(circles[i].x, circles[i].y, circles[i].r * 2, circles[i].r * 2);

    }
}

draw = () => {

    for (var i = 0; i < circles.length; i++) {
        if (mouseX > circles[i].x - circles[i].r && mouseX < circles[i].x + circles[i].r && mouseY > circles[i].y - circles[i].r && mouseY < circles[i].y + circles[i].r)

        {
            stroke(0, 0, 0);
            fill(random(colors));
            strokeWeight(4);
            noStroke;
            ellipse(circles[i].x, circles[i].y, circles[i].r * 2, circles[i].r * 2);

        }
    }
}

回答1:


You are on the right track using windowResized() and resizeCanvas(), however you missed an important detail regarding scope:

  • in JavaScript you can have functions nested within functions: that is how you've defined a custom windowResized() function in your code, local only to setup()
  • you probably meant to use windowResized() as a sibling of setup()/draw(), overriding p5.js's default: same as the resizeCanvas example linked above

The second overlooked detail has to do with drawing:

  • in draw() you are not clearing the canvas (using background() or something similar) which means you expect the circles drawn in setup() to remain drawn
  • resizeCanvas() might clear the canvas as well
  • you can group the circle drawing done in setup() in a function you can simple re-call again in windowResized()

The thirdpart, you guessed it, attention to details in code:

  • you've defined circles at the top, nowever you've never initialized it to an empty array: this would cause an undefined error trying to circles.push
  • don't forget the () when calling a function like noStroke()
  • try to keep your code consistent: if you want to use arrow functions expressions instead of the function you can, but it would make it easier to read/scan the code when it's uniform. (In the end you'll spend more time reading the code than writing it: make it easy for your future self to easily predict what your past self coded :) )

Here's a modified version of your sketch with the points above addressed:

var font;
var colors;
var bolder;
var canvas;
var circles;

setup = () => {

    frameRate(2.5);

    canvas = createCanvas(windowWidth, windowHeight);
    canvas.position(0, 0);
    canvas.style('z-index', '-1');

    colors = [color(255, 0, 0), color(1, 130, 83), color(0, 0, 255), color(255, 255, 0), color(102, 0, 102), color(255, 107, 31)];
    
    setupCircles();
}
// group circle drawing functionality in a re-usable
setupCircles = () => {
  circles = [];
  for (var c = 0; c < 1000; c++) {
      var circle = {
          x: random(width),
          y: random(height),
          r: random(90, 15)
      };

      var overlap = false;

      var protection = 0;

      for (var j = 0; j < circles.length; j++) {
          var other = circles[j];
          var d = dist(circle.x, circle.y, other.x, other.y);
          if (d < circle.r + other.r) {
              overlap = true;
          }
      }

      if (!overlap) {
          circles.push(circle);
      }

      protection++;
      if (protection > 10000) {
          break;
      }
  }

  for (var i = 0; i < circles.length; i++) {

      fill(255, 255, 255);
      strokeWeight(4);
      ellipse(circles[i].x, circles[i].y, circles[i].r * 2, circles[i].r * 2);

  }
  
  noStroke();
}

windowResized = () => {
    console.log('window resized');
    resizeCanvas(windowWidth, windowHeight, true);
    setupCircles();
}


draw = () => {

    for (var i = 0; i < circles.length; i++) {
        if (mouseX > circles[i].x - circles[i].r && mouseX < circles[i].x + circles[i].r && mouseY > circles[i].y - circles[i].r && mouseY < circles[i].y + circles[i].r)

        {
            stroke(0, 0, 0);
            fill(random(colors));
            strokeWeight(4);
            ellipse(circles[i].x, circles[i].y, circles[i].r * 2, circles[i].r * 2);

        }
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>

Closing remarks:

I've noticed you used noStroke() and frameRate(2.5) which leads me to believe you are visual thinker paying attention to visual details. If the intention is to avoid jagged circle edges from overdrawing and not using as much CPU/power on mobile devices you might want to check out createGraphics(): it's really cool. In terms of avoiding continously rendering in draw() if you simply want to fill circles when they're hovered you can use mouseMoved()

You could use a global graphics object at the top that gets populated with circles in setup() then simply fill circles when the mouse is moved.

Additionally you could further encapsulate:

for (var i = 0; i < circles.length; i++) {
        if (mouseX > circles[i].x - circles[i].r && mouseX < circles[i].x + circles[i].r && mouseY > circles[i].y - circles[i].r && mouseY < circles[i].y + circles[i].r)

        {
            stroke(0, 0, 0);
            fill(random(colors));
            strokeWeight(4);
            ellipse(circles[i].x, circles[i].y, circles[i].r * 2, circles[i].r * 2);

        }
    }

to something like

isInsideCircle = (circle, x, y) => {
   return (x > circle.x - circle.r && x < circle.x + circle.r ) && (y > circle.y - circle.r && y < circle.y + circle.r);
}

getCircleAt = (circles, x, y) => {
   let numCircles = circles.length;
   for (let i = 0; i < numCircles; i++){
      if(isInsideCircle(circles[i], x, y){
         return circles[i];
      }
   }
}

mouseMoved = () => {
   circle = getCircleAt(circles, mouseX, mouseY);
   if(circle){
      fill(random(colors));
      ellipse(circle.x, circle.y, circle.r * 2, circle.r * 2);
   }
}

Note this is an untested snippet, but hopefully it illustrated some of the ideas mentioned.



来源:https://stackoverflow.com/questions/62692767/p5js-how-do-i-make-my-randomly-generated-circle-packing-sketch-canvas-responsiv

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!