Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Draw clickable grid of 1 million squares

I need to find a way to draw a 1000x1000 squares grid, each square is clickable and they must be independently color changeable. Like mines game. I can use HTML (pure or using Canvas or SVG), CSS and JavaScript for this.

I know how to create one grid with these characteristics with JavaScript and CSS, it does well with 10x10 squares, with 100x100 the squares will turn into tall rectangles and 1000x1000 it loads, but the "squares" are soo much compressed that borders meet each other and renders a full gray page.

I tried using HTML and JavaScript to draw SVG squares, the squares' size problem solves, but I don't know how to make they change color when clicked and when I set to load 1000x1000 squares it will freeze the browse and eventually crash the tab.

Is this feasible in any way?

EDIT

Sorry if I wasn't clear, but yes, I need scroll bars in that. They are no problem for me.

You can see the two trials I described here:

JavaScript and CSS

var lastClicked;
var grid = clickableGrid(100,100,function(el,row,col,i){
    console.log("You clicked on element:",el);
    console.log("You clicked on row:",row);
    console.log("You clicked on col:",col);
    console.log("You clicked on item #:",i);

    el.className='clicked';
    if (lastClicked) lastClicked.className='';
    lastClicked = el;
});

document.body.appendChild(grid);
     
function clickableGrid( rows, cols, callback ){
    var i=0;
    var grid = document.createElement('table');
    grid.className = 'grid';
    for (var r=0;r<rows;++r){
        var tr = grid.appendChild(document.createElement('tr'));
        for (var c=0;c<cols;++c){
            var cell = tr.appendChild(document.createElement('td'));
            ++i;
            cell.addEventListener('click',(function(el,r,c,i){
                return function(){
                    callback(el,r,c,i);
                }
            })(cell,r,c,i),false);
        }
    }
    return grid;
}
.grid { margin:1em auto; border-collapse:collapse }
.grid td {
    cursor:pointer;
    width:30px; height:30px;
    border:1px solid #ccc;
}
.grid td.clicked {
    background-color:gray;
}

JavaScript and HTML

 
    document.createSvg = function(tagName) {
        var svgNS = "http://www.w3.org/2000/svg";
        return this.createElementNS(svgNS, tagName);
    };
    
    var numberPerSide = 20;
    var size = 10;
    var pixelsPerSide = 400;
    
    
    
    var grid = function(numberPerSide, size, pixelsPerSide, colors) {
        var svg = document.createSvg("svg");
        svg.setAttribute("width", pixelsPerSide);
        svg.setAttribute("height", pixelsPerSide);
        svg.setAttribute("viewBox", [0, 0, numberPerSide * size, numberPerSide * size].join(" "));
        
        for(var i = 0; i < numberPerSide; i++) {
            for(var j = 0; j < numberPerSide; j++) {
              var color1 = colors[(i+j) % colors.length];
              var color2 = colors[(i+j+1) % colors.length];  
              var g = document.createSvg("g");
              g.setAttribute("transform", ["translate(", i*size, ",", j*size, ")"].join(""));
              var number = numberPerSide * i + j;
              var box = document.createSvg("rect");
              box.setAttribute("width", size);
              box.setAttribute("height", size);
              box.setAttribute("fill", color1);
              box.setAttribute("id", "b" + number); 
              g.appendChild(box);
              svg.appendChild(g);
            }  
        }
        svg.addEventListener(
            "click",
            function(e){
                var id = e.target.id;
                if(id)
                    alert(id.substring(1));
            },
            false);
        return svg;
    };
    
    var container = document.getElementById("container");
    container.appendChild(grid(100, 10, 2000, ["gray", "white"]));

    
<div id="container">
    
</div>

I will be trying implementing the given answers and ASAP I'll accept or update this question. Thanks.

SOLUTION

Just to record, I managed to do it using canvas to draw the grid and the clicked squares and added an event listener to know where the user clicks.

Here is the code in JavaScript and HTML:

function getSquare(canvas, evt) {
    var rect = canvas.getBoundingClientRect();
    return {
        x: 1 + (evt.clientX - rect.left) - (evt.clientX - rect.left)%10,
        y: 1 + (evt.clientY - rect.top) - (evt.clientY - rect.top)%10
    };
}

function drawGrid(context) {
    for (var x = 0.5; x < 10001; x += 10) {
      context.moveTo(x, 0);
      context.lineTo(x, 10000);
    }
    
    for (var y = 0.5; y < 10001; y += 10) {
      context.moveTo(0, y);
      context.lineTo(10000, y);
    }
    
    context.strokeStyle = "#ddd";
    context.stroke();
}

function fillSquare(context, x, y){
    context.fillStyle = "gray"
    context.fillRect(x,y,9,9);
}

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');

drawGrid(context);

canvas.addEventListener('click', function(evt) {
    var mousePos = getSquare(canvas, evt);
    fillSquare(context, mousePos.x, mousePos.y)
}, false);
<body>
    <canvas id="myCanvas" width="10000" height="10000"></canvas>
</body>
like image 461
Rodrigo Martins de Oliveira Avatar asked Feb 18 '15 05:02

Rodrigo Martins de Oliveira


1 Answers

Generating such a large grid with HTML is bound to be problematic. Drawing the grid on a Canvas and using a mouse-picker technique to determine which cell was clicked would be much more efficient.

This would require 1 onclick and/or hover event instead of 1,000,000. It also requires much less HTML code.

like image 180
Fuzzy Logic Avatar answered Sep 24 '22 10:09

Fuzzy Logic