Grid of squares that fits within any width/height container - javascript

I am trying to create a grid of squares that could fit in any dimensions container (ie. the squares resize themselves or add/delete new ones if the container width/height are changed).
I am going the Javascript route for now (and Jquery, for now) - there might be a flexgrid solution but since I plan to populate my squares with some kind of cellular automata type thingie, I figured that it wouldn't hurt. This doesn't solve my problem, since the number of lines of squares seems to be fixed.
Here is what I have so far:
var screen = {
width: 0, // these I get with jquery, on load and on resize events
height: 0
}
var values = {
min: 100, // minimum square size
max: 500 // maximum square size
}
var findSize = function() {
var r = 1;
var currVal = values.min;
while (r > 0) {
if((screen.width % currVal) === (screen.height % currVal)) {
// this should mean that they are both divisible by this value, right?
// get out of the loop and return value
r = 0;
return currVal;
} else if (currVal > values.max ) {
// if we exceed the maximum size, get out of the loop and return 0
r = 0;
return 0;
} else {
// if not, increment a bit
currVal += 0.25; // increment the value to check the modulo against
}
}
}
Calling the findSize() function should return either the dimensions of the square (from which I can then build my grid easily, with either floated squares or absolutely positioned ones.
The problem is that it doesn't. Well it sometimes does. And it also pretty often gives me nothing...
The border are done with box-shadow so it shouldn't affect the dimensions.
So I am wondering...
Where is my code faulty?
Can I change my function so it return always something (maybe with smaller incrementations?). I can work with rounded values for display purpose.
The brute force aspect doesn't seem too efficient. Is there a way to refactor this so the loop is shorter?
Thanks a lot!

The problem with this code is it tries to find the solution but fails to account for answers between each deltas (0.25).
Also, if there can be any numbers of cells (added removed automatically) then the answer can also be "always 100".
I guess what you're trying to achieve here is a "best fit" that could leave no borders horizontally and vertically at the same time. I'm not sure if there is a proper answer to that question (you probably need to search for Greatest common divisor, close to what you are doing), and I wonder if something like the code below wouldn't work in your case:
var findSize = function() {
var ratio = screen.width / screen.height; // get the ratio between width and height
if (ratio > 1) {
ratio = 1 / ratio; // always have it always <= 1
}
var size = values.max * ratio; // size between 0 and max
if (size < values.min) {
return values.min; // failed, could try other things here
}
return size; // size between min and max
}

Related

How can I detect whether the mouse is in a certain part of the screen?

So I'm trying to change the value of a variable based on the Y coordinate of the pointer, and I'm not sure how I would do this. I tried this:
var righttext = mousemove.clientY;
switch(righttext){
case //not sure what to put here
}
but I wasn't sure what to put in the case. What I want to do is have something like this example: case "top 20% of screen". How would I figure out what the coordinates of the screen are, and then figure out how to use that in a switch statement?
The parts that I want to affect the value are just fifths of the screen divided horizontally.
EDIT:
So I was linked to another post, and found a similar answer to what I was looking for. I changed the code to look like this:
document.addEventListener('mousemove', _.throttle(mouseMoveEventAction, 200));
function mouseMoveEventAction(e) {
changetext(isInsideThreshold(e.clientY));
}
var rpaneltext = "";
function changetext(isActive) {
if (isActive) {
rpaneltext = "awareness";
} else {
rpaneltext = "";
}
}
function isInsideThreshold(cursorY) {
var threshold = .2;
var clientHeight = document.documentElement.clientHeight;
return cursorY > (clientHeight - threshold);
}
But it still doesn't work. Any ideas?
Project here: The part that will be affected by righttext is document.querySelector(".r2").textContent = righttext
You can look at window.innerHeight and window.innerWidth. They return the viewport dimensions.
You may have better luck with if-else logic like this.
if (event.clientY < window.innerHeight / 2) {
// it's in the upper half
} else {
// it's in the lower half
}

calling object.width is returning NaN in JavaScript

I am currently working on a game that has to do with a rocket ship moving around and objects(circles) are falling from the top. The goal of this game is to not hit the objects as they are falling down the screen. I am running into problems when writing my collision algorithm.
I have declared var hit = false; at the top of my code
I have also put all of the circles into an array called projectiles.
I believe that I have the logic correct but I discovered that when calling either p.width or ship.width it returns NaN. I have tried using offsetWidth and that didn't work either. I am wondering how else to go about getting the width of my objects
The else statement at the bottom is just to check if .width is returning the correct number. Once I get it to work it will be removed and replaced with the final parts of the collision algorithm.
function checkCollision()
{
for (i = 0; i < projectiles.length; i++) {
var p = projectiles[i];
if((p.x + p.width) < ship.x)
{
hit = false;
}
else if(p.x > (ship.x + ship.width))
{
hit = false;
}
else if(p.y > (ship.y + ship.height))
{
hit = false;
}
else if((p.y + p.height) < ship.y)
{
hit = false;
}
else {
console.log(ship.x + ship.width);
}
In the documentation for createjs.Bitmap there do not exist properties for .width and .height. Instead access it's .image (HTMLImageElement) property which have defined width and height properties: ship.image.width and ship.image.height.
If your object is an EaselJS Bitmap, you can retrieve the physical size using getBounds(). This method works for some EaselJS display objects (ie, not Shapes, and accuracy varies with Text).
var bounds = ship.getBounds();
var w = bounds.width;
Notes:
per #Spencer's message, you can access the .image, and get the width/height, but it will be the original size of the image, so if you transform the bitmap instance, or any of the parent containers, the value will be wrong. The getBounds will consider the scale transformation (if it exists)
values may not be correct if the item is rotated.
bounds are based on the registration point, so the x/y might be non-zero.
You will get 0 for width/height if the image is not yet loaded
For your projectile, if it is a shape, the bounds will always be null, but you can manually set them if you know them, and they will be properly calculated/transformed:
var p = new createjs.Shape();
p.graphics.beginFill("red").drawCircle(0,0,20);
p.setBounds(new createjs.Rectangle(-20,-20,40,40));
Here is some info on why there is no .width or .height: http://blog.createjs.com/update-width-height-in-easeljs/

Find the element with the largest area (main content area)?

Given a web page, how do you find the largest rectangle on the webpage which is the main content area?
For example, compare the size of sidebar, header, footer, and main content area. Is it possible to find the main content area by simply searching for the largest rectangle out of all the rectangles discovered on a page?
Usually the tallest and widest rectangle is suspected to be the main content area, wondering if there's an algorithm of some sort in Javascript or Python to test this hypothesis out.
So while the question didn't make much sense to me, I couldn't resist the urge to toy around with the concept of recursively scanning a DOM tree to retrieve and sort elements by their sizeĀ :)
Here's a dumb function for doing so (you can paste it in your browser console):
function scanSizes(root) {
return [].reduce.call(root, function(sizes, node) {
var bounds = node.getBoundingClientRect();
sizes.push({tag: node.outerHTML, area: bounds.width * bounds.height});
var children = node.querySelectorAll("*");
if (children.length > 0)
sizes.push.apply(sizes, scanSizes(children));
return sizes;
}, []).sort(function(x, y) {
var a = x.area, b= y.area;
return a > b ? -1 : a < b ? 1 : 0;
});
}
var sizes = scanSizes(document.querySelectorAll("body > *"));
// sizes[0].tag contains the largest html tag (as a string)
// sizes[0].area its area size in pixels (width * height)
Edit: more seriously, you might be interested in this topic and related answers.
Edit: of course performance wise recursion wasn't a really good idea. You can go with something like this to have a more efficient solution:
function scanSizes(root) {
return [].map.call(root, function(node) {
var bounds = node.getBoundingClientRect();
return {tag: node.outerHTML, area: bounds.width * bounds.height};
}).sort(function(x, y) {
var a = x.area, b= y.area;
return a > b ? -1 : a < b ? 1 : 0;
});
}
var sizes = scanSizes(document.querySelectorAll("*"));
I'm adding another answer because I've just stumbled upon the <main> HTML5 element spec, which developers are supposed to use to define their main contents area, so that's probably the very first element you'll want to check for in any scraped page.
So basically you should check for any single <main> or role="main" element in the page, then only use other contents detection strategiesĀ :)
The current answer is overly complex. The main thing you need to know is element.getBoundingClientRect();. Here's a smaller function - I'm looking for the biggest table but you can use any CSS selector you want.
// Fix NodeList.sort()
NodeList.prototype.sort = Array.prototype.sort
var elements = document.querySelectorAll('table')
var getArea = function(element){
var rectangle = element.getBoundingClientRect();
return rectangle.width * rectangle.height;
}
elements.sort(getArea)[0]

canvas getImageData returns too many pixels

I'm creating a mobile webapp game that lets the user swipe over the screen to clear it.
I am trying to write a smart function that detects when nearly the whole canvas is transparent.
I have an interval that calls the function below each second, to do the check.
The way it is supposed to work is that I take a sector of the canvas that is 20px wide, and as tall as the canvas is.
This function should run quite smooth, but for some reason, the getImageData function, returns to many pixels. My sector is always 20px wide and has the same height, so it should return the same amount of pixels each time. Of course, since getImageData returns a higher and higher number, my loop gets slower and slower.
Does anyone know what causes this? have I misunderstood the getImageData function in the way it works?
isCanvasTransparent: function (fromX, toX, currentIteration) {
if(currentIteration < this.clearedIterations) { // If a sector is already clear, we dont need to check it again
this.isCanvasTransparent(fromX + 20, toX + 20, currentIteration + 1);
return;
} else {
var data = this.context.getImageData(fromX, 0, toX, parseInt(this.canvas.style.width)).data;
var counter = 0;
var i = 0;
// For some reason, the length increases, but the diff between fromX and toX is always 20
console.log(data.length);
for(i=0;i<(data.length);i+=4) {
if(data[i+3]!==0){
counter++;
// I stop iterating, since there are too many non transparent pixels
if(counter > 10) {
break;
}
}
}
// I accept that 10 pixels in each sector is not transparent
if((counter < 10) && !this.alerted) {
this.clearedIterations++; // Here we increase clearedIterations, since this sector is clear
// There are no more iterations, so we are done
if(currentIteration === this.maxIterations) {
// this is the interval that calls isCanvasTransparent(0, 20, 0)
// in the first place. The interval is called each second. But as soon the whole view
// is transparent, we clear it, so that isCanvasTransparent is no longer called
clearInterval(this.checkCanvasTimeout);
this.alerted = true;
TouchHelpers.removeTouchEvents();
this.levelCompleted();
return;
} else {
// this sector is clear, but we need to check the next one, since we are not at the end
this.isCanvasTransparent(fromX + 20, toX + 20, currentIteration + 1);
}
}
}
},
It's not certain without seeing how you set fromX/toX, but it looks like you're not giving getImageData the proper parameters.
context.getImageData has these parameters:
x: starting x coordinate to extract,
y: starting y coordinate to extract,
width: extract a block this many pixels wide,
height: extract a block this many pixels high
[ Addition: based on additional info ]
If you want equal sized chunks of pixel data, your first call is
var data=getImageData(0,0, 20, canvas.height).data;
And your second call would be:
// Note: the width arguement remains at 20, not increase to 40
var data=getImageData(20,0, 20, canvas.height).data;

Prevent touching corners (JS Game)

How can I prevent this map generator from creating touching corners like this:
-X
X-
Or
X-
-X
Here is a simplified example of the generator: http://jsfiddle.net/fDv9C/2/
Your question answers itself, almost.
Here's the fiddle: http://jsfiddle.net/qBJVY/
if (!!grid[y][x] && !!grid[y+1][x+1] && !grid[y+1][x] && !grid[y][x+1]) {
good=false;
grid[y+1][x]=2;
}
It simply checks for the combinations you do not want and patches them up. It always adds a grid point so as not to disconnect any parts of the map.
This in turn may lead to another situation where the issue may occur, but if it changed anything (that is, if it found a problem), it will simply check again. This can be optimized, for instance by recursively adjusting whatever was changed, but usually it only needs 1 or 2 passes. There's a limiter on there to not allow more than 100 passes, just in case there is some unforeseen circumstance in which it cannot fix it (I can't think of such a situation, though :) ).
Because of the way that you are creating board it's very difficulty to do this checking during generation. I create simple function that check board after. It's using flood algorithm. Here is the fiddle http://jsfiddle.net/jzTEX/8/ (blue background is original map, red background is map after checking)
Basically we create second array grid2. After filling grid we run recursively floodV function
function floodV(x,y) {
var shiftArray = [[0,1],[0,-1],[1,0],[-1,0]];
grid2[y][x]=1;
for(var k=0;k<4;k++) {
var x1=x+shiftArray[k][0];
var y1=y+shiftArray[k][1];
if(grid[y1][x1] == 1 && grid2[y1][x1] == 0 && checkV(x1,y1)) {
grid2[y1][x1] = 1;
floodV(x1,y1);
}
}
}
with the check function
function checkV(x,y) {
var checkVarr = [[-1,-1], [-1,1], [1,1], [1,-1]];
for(var k=0;k<4;k++) {
if(grid[y+checkVarr[k][0]][x+checkVarr[k][1]] == 1 && grid[y+checkVarr[k][0]][x] == 0 && grid[y][x+checkVarr[k][1]] == 0 && grid2[y+checkVarr[k][0]][x+checkVarr[k][1]] == 1)
return false;
}
return true;
}
This isn't perfect because we can sometimes throw away big parts of the map but if we try to start adding new elements we have to check whole map again (in worths case).
This is what I did: http://jsfiddle.net/fDv9C/13/
Where's the magic happening? Scroll down to lines 53 through 58:
var bottom = y_next + 1;
var left = x_next - 1;
var right = x_next + 1;
var top = y_next - 1;
if (grid[top][left] || grid[top][right] ||
grid[bottom][left] || grid[bottom][right]) continue;
In short your touching corner points can only occur at the computed next position. Hence if any one of the four corner neighbors of the next position exists, you must compute another next position.
You may even decrement the counter i when this happens to get as many paths as possible (although it doesn't really make a big difference):
var bottom = y_next + 1;
var left = x_next - 1;
var right = x_next + 1;
var top = y_next - 1;
if (grid[top][left] || grid[top][right] ||
grid[bottom][left] || grid[bottom][right]) {
i--;
continue;
}
See the demo here: http://jsfiddle.net/fDv9C/12/
Edit: I couldn't resist. I had to create an automatic map generator so that I needn't keep clicking run: http://jsfiddle.net/fDv9C/14/

Categories