Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

create random rectangles with random colors without overlapping using javascript

How can i create something like this in HTML using javascript? enter image description here

Actually I know how to create rectangles in HTML but want to do something like this. HTML canvas can be of any size but whenever page is loaded multiple squares are generated with random sizes and colors without overlapping. When I'm trying to do this rectangles are generated in a list form. I'm a web developer(ruby on rails oriented) but new to such javascript stuff. Any help will be appreciated.

html:

<body>
    <div id="randBlock" >
    </div>
</body>

javascript:

(function makeDiv(){
    var divsize = ((Math.random()*100) + 50).toFixed();
    var color = '#'+ Math.round(0xffffff * Math.random()).toString(16);
    $newdiv = $('#randBlock').css({
        'width':divsize+'px',
        'height':divsize+'px',
        'background-color': color
    });

    var posx = (Math.random() * ($(document).width() - divsize)).toFixed();
    var posy = (Math.random() * ($(document).height() - divsize)).toFixed();

    $newdiv.css({
        'position':'absolute',
        'left':posx+'px',
        'top':posy+'px',
        'display':'none'
    }).appendTo( 'body' ).fadeIn(100).delay(300).fadeOut(200, function(){
       $(this).remove();
       makeDiv(); 
    }); 
})();
like image 538
Amarjeet Singh Avatar asked Feb 16 '16 14:02

Amarjeet Singh


2 Answers

A solution with canvas (so I understand the question).

With built-in collision detecting isInside().

Edit: Better random support, does not run forever, a hint from Drawing a 1px thick line in canvas creates a 2px thick line and a little bit from this answer Random Color generator in Javascript

function getRandomColor() {
    var color = '#';
    for (var i = 0; i < 6; i++) {
        color += (Math.random() * 16 | 0).toString(16);
    }
    return color;
}

function Point(x, y) {
    this.x = x;
    this.y = y;
}

function Rectangle(p1, p2) {
    this.p1 = p1;
    this.p2 = p2;
}

Rectangle.prototype.isInside = function (r) {
    function check(a, b) {
        return (
            a.p1.x <= b.p1.x && b.p1.x <= a.p2.x && a.p1.y <= b.p1.y && b.p1.y <= a.p2.y ||
            a.p1.x <= b.p2.x && b.p2.x <= a.p2.x && a.p1.y <= b.p2.y && b.p2.y <= a.p2.y ||
            a.p1.x <= b.p2.x && b.p2.x <= a.p2.x && a.p1.y <= b.p1.y && b.p1.y <= a.p2.y ||
            a.p1.x <= b.p1.x && b.p1.x <= a.p2.x && a.p1.y <= b.p2.y && b.p2.y <= a.p2.y
        );
    }
    return check(this, r) || check(r, this);
}

function generateRectangles() {
    function p() { return Math.random() * 300 | 0; }
    function s() { return 50 + Math.random() * 150 | 0; }

    var rectangles = [],
        r, size, x, y, isInside, i, counter = 0;

    for (i = 0; i < 20; i++) {
        counter = 0;
        do {
            counter++;
            x = p();
            y = p();
            size = s();
            r = new Rectangle(new Point(x, y), new Point(x + size, y + size));
            isInside = rectangles.some(function (a) {
                return a.isInside(r);
            });
        } while (isInside && counter < 1000);
        counter < 1000 && rectangles.push(r);
    }
    return rectangles;
}

function drawRectangles(rectangles) {
    var canvas = document.getElementById("canvas"),
        ctx = canvas.getContext("2d");

    rectangles.forEach(function (a) {
        ctx.lineWidth = 1;
        ctx.strokeRect(a.p1.x + 0.5, a.p1.y + 0.5, a.p2.x - a.p1.x - 1, a.p2.y - a.p1.y - 1);
        ctx.fillStyle = getRandomColor();
        ctx.fillRect(a.p1.x + 0.5, a.p1.y + 0.5, a.p2.x - a.p1.x - 1, a.p2.y - a.p1.y - 1);
    });
}

var rectangles = generateRectangles();
drawRectangles(rectangles);
<canvas id="canvas" width="500" height="500"></canvas>
like image 71
Nina Scholz Avatar answered Nov 15 '22 10:11

Nina Scholz


You already have it drawing one random square with random position. It's easy to go from there to drawing many - simply have a loop for adding more than one div. Of course, then you need to give each one a random id, but that's easy enough. The problem is that this will allow overlapping.

Simplest case: allowing overlap

function makeDiv(i) {
  var divsize = ((Math.random() * 100) + 50).toFixed();
  var color = '#' + Math.round(0xffffff * Math.random()).toString(16);
  var posx = (Math.random() * ($(document).width() - divsize)).toFixed();
  var posy = (Math.random() * ($(document).height() - divsize)).toFixed();
  var divid = 'randBlock' + i;

  $('#randBlock').append("<div id='" + divid + "'>");
  $('#' + divid).css({
    'width': divsize + 'px',
    'height': divsize + 'px',
    'background-color': color,
    'position': 'absolute',
    'left': posx + 'px',
    'top': posy + 'px'
  });
}

for (var i = 0; i < 10; ++i) {
  makeDiv(i);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>
  <div id="randBlock">
  </div>
</body>

If you want to guarantee it doesn't overlap, you have a lot more work to do, unless there's some CSS magic you can do, which is very possible, but I'm not aware of it. You would have to track the position and size of each square, and only fit the results into areas that are clear.

Depending on your exact requirements, your algorithm will be different. You're going to have to have some constraint - either in the maximum size of the squares, the requirement on overlapping, a maximum number of squares that you want to fit, or allow the squares to become increasingly small. I'll give you a verbal walkthrough for two of these. There's a working snippet for the grid idea. From what you added in the comments, that may be sufficient.

You've already built in a fixed size, so maybe the best idea is to use that size as a basis, and create a grid. In that case, you'll know you can have one square in each gridsquare. Have an array that stores each gridsquare position, and randomly choose from (1 to the length of that array) - 1. In that gridsquare, fit a square, very similarly to the function you currently have. Its size is the max you already have set, and its position within that box can be random based on the size.

Gridded case

var fixedScale = 100;
var fixedConstant = 50;
var fixedMax = fixedScale + fixedConstant;
var gridCols = (($(document).width() / fixedMax) + 1).toFixed();
var gridRows = (($(document).height() / fixedMax) + 1).toFixed();
var grid = [];
for (var row = 0; row < gridRows; ++row) {
  for (var col = 0; col < gridCols; ++col) {
    var index = row * gridCols + col;
    grid[index] = {row: row, col: col};
  }
}

function makeDiv(i) {
  var gridId = Math.floor(Math.random() * grid.length);
  
  var divsize = ((Math.random() * fixedScale) + fixedConstant).toFixed();
  console.log(gridId + ", grid.length = " + grid.length);
  var color = '#' + Math.round(0xffffff * Math.random()).toString(16);
  var posx = grid[gridId].col * fixedMax;
  var posy = grid[gridId].row * fixedMax;
  var offsetx = (Math.random() * (fixedMax - divsize)).toFixed();
  var offsety = (Math.random() * (fixedMax - divsize)).toFixed();
  posx = +posx + +offsetx;
  posy = +posy + +offsety;
  var divid = 'randBlock' + i;

  grid.splice(gridId, 1);
  
  $('#randBlock').append("<div id='" + divid + "'>");
  $('#' + divid).css({
    'width': divsize + 'px',
    'height': divsize + 'px',
    'background-color': color,
    'position': 'absolute',
    'left': posx + 'px',
    'top': posy + 'px'
  });
}

for (var i = 0; i < 10; ++i) {
  makeDiv(i);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>
  <div id="randBlock">
  </div>
</body>

You can have infinite (but diminishing squares) using the following fact: any time you add a square into an empty area, it will leave four rectangles of remaining space in that area. Any time you make a colored square, you could actually make 5 divs: the colored square, the empty area on its top, its right, its bottom and its left. Attribute the empty ones with a specific class. Then, look through all divs that have that "empty" class, and set your max size to the max of the empty divs. When you create your next colored square, pick a random div that is at least as big as the square.

Obviously, the code for these is not entirely trivial. The choice you make in your requirements and constraints will have a huge impact on your coding effort.

like image 34
Scott Mermelstein Avatar answered Nov 15 '22 11:11

Scott Mermelstein