问题
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 tosetup()
- you probably meant to use
windowResized()
as a sibling ofsetup()
/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 (usingbackground()
or something similar) which means you expect the circles drawn insetup()
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 inwindowResized()
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 anundefined
error trying tocircles.push
- don't forget the
()
when calling a function likenoStroke()
- 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