Parsing a number of pixels to an integer - javascript

In my code i'm trying to compare the height with the position of an element, to ensure that the element doesn't leave the game's div.
First I get the position of my element, snake. and if the cursor is too close then i move it. Then at the end I do a check that it is at least 20 pixels away from the top and bottom. For some reason everything is working except the when it reaches the bottom of the screen (which is the else if statement in the end block of code)
var posL = $("#snake").position().left;
var posT = $("#snake").position().top;
if((e.pageX-200 < posL) && (posL < e.pageX-50)){
if(posL > 20){
posL = posL - 5;
}else{
posT = posT + 5;
}
...
if(posT < 20){
posT = 20;
}else if(posT > parseInt($("#game").height)){
posT = parseInt($("#game").height) - 20;
}

You forgot the () after .height ...twice! ;-)
if(posT < 20) {
posT = 20; // ------------------------v
} else if(posT > parseInt($("#game").height)){
posT = parseInt($("#game").height) - 20;
} // -----------------^

Related

setTimeout to access DOM in for loop

Please look at this codepen: http://codepen.io/dragonOne/pen/rrwrQw (doesn't work yet!)
I want this Javascript memory board to display cards one by one in spiral order.
The spiralOrder(output) function takes in a matrix of div id's and changes each card div (id is tile_i) to display = "block" one by one in spiral order, every two seconds. But my setTimeout isn't working properly and is only displaying the first four cards (all at once...)
When I read the console.log in spiralOrder I see that the function correctly reads each card in the order that I want. But how come my setTimeout isn't working to add delay after every card display?
function spiralOrder(output) {
// Initialize our four indexes
var top = 0;
var down = output.length - 1;
var left = 0;
var right = output[0].length - 1;
var regexp = /\d+/g;
while(true)
{
// Starting showing top row
for(var j = left; j <= right; ++j) {
console.log("tile_"+output[top][j].match(regexp)); //THIS SHOWS SPIRAL ALGORITHM IS CORRECT
setTimeout(function() { document.getElementById("tile_"+output[top][j].match(regexp)).style.display = "block"; }, 2000); //THIS DOESN'T BEHAVE RIGHT
}
top++;
if(top > down || left > right) {
break;
}
//Starting showing rightmost column
for(var i = top; i <= down; ++i){
console.log("tile_"+output[i][right].match(regexp));
setTimeout(function() { document.getElementById("tile_"+output[i][right].match(regexp)).style.display = "block"; }, 2000);
}
right--;
if(top > down || left > right) {
break;
}
//Starting showing bottom row
for(var j = right; j >= left; --j){
console.log("tile_"+output[down][j].match(regexp));
setTimeout(function() { document.getElementById("tile_"+output[down][j].match(regexp)).style.display = "block"; }, 2000);
}
down--;
if(top > down || left > right) {
break;
}
//Starting showing leftmost column
for(var i = down; i >= top; --i){
console.log("tile_"+output[i][left].match(regexp));
setTimeout(function() { document.getElementById("tile_"+output[i][left].match(regexp)).style.display = "block"; }, 2000);
}
left++;
if(top > down || left > right) {
break;
}
}
}
What is going wrong here?
You are trying to use setTimeout as a pause function. That isn't what it does. It just ensures that the contained function is called around the offset time. So, in your code, it is running through all of the divs and scheduling all the calls to occur simultaneously in about 2 seconds.
Couple of possible solutions
(POOR) Rewrite the code to call the timeout functions at (count++)*2000 so they are called successively
(BETTER) Rewrite the code so that the timeout function updates one div and then updates state and sets another timeout to perform the next update (and so on). Basically unwrap your loops into a series of timeout callbacks.
Update: take a look at How do I add a delay in a JavaScript loop?

timing and delays on javascript

Im changing the page background of a from white to black following a sequence of 1 and 0. At the moment I manage to make it work were the black and white background changes with the same delay:
var syncinterval = setInterval(function(){
bytes = "10101010101010101010101010101010101010101010101010101010101010101";
bit = bytes[i];
output_bit(bit);
i += 1;
if (i > bytes.length) {
clearInterval(syncinterval);
i = 0;
for (i=0; i < input.length; i++) {
tbits = input[i].charCodeAt(0).toString(2);
while (tbits.length < 8) tbits = '0' + tbits;
bytes += tbits;
}
console.log(bytes);
}
}, sync_speed);
Complete working demo: http://jsfiddle.net/kn48Z/
How can I modify the function to make the white background last for sync_speed seconds and the black background for other any value?
If I'm understanding what you're trying to do, try placing your code into a separate function and use setTimeout. This will give you more control over what you're trying to accomplish.
I modified your code a bit. I check to see if bit is 1 (which I'm assuming is white), if it is, it will run backgroundOrSomething within sync_speed seconds. If it's not, it will run backgroundOrSomething within otherTime. I set otherTime to 1 second, just for giggles.
function backgroundOrSomething (){
bytes = "10101010101010101010101010101010101010101010101010101010101010101";
bit = bytes[i];
output_bit(bit);
i += 1;
if(i > bytes.length) {
i = 0;
for (i=0; i < input.length; i++) {
tbits = input[i].charCodeAt(0).toString(2);
while (tbits.length < 8) tbits = '0' + tbits;
bytes += tbits;
}
console.log(bytes);
}
otherTime = 1000;
if (bit == 1)
setTimeout(backgroundOrSomething, sync_speed);
else
setTimeout(backgroundOrSomething, otherTime);
}
setTimeout(backgroundOrSomething, sync_speed);
The last setTimeout at the bottom of the code will be the first to execute backgroundOrSomething.
As above you'll need to use a set interval as follows:
var bit = 0;
var syncInterval = setInterval(function() {
document.body.style.backgroundColor = bit == 0 ? 'black' : 'white';
bit = 1;
}, 3000);
var stopInterval = setTimeout(function() {
clearInterval(syncInterval);
}, 50000);
Fiddle here http://jsfiddle.net/kn48Z/4/. Note slowed down background switch so it should give a fit.

Reverse image on keydown - Javascript

What I am trying to implement here is that I have a character on my screen and I can move it around to the left, right, up and down with a press on my arrow keys.
What I want to do is when I click to the left is that the image will reverse and when I click to the right and it should reverse back, with this the character is always facing the way it walks.
My code looks like this where I define the original image and the reversed one:
Game.Hero = function (myX, myY){
var my = new createjs.Bitmap("img/hero.png");
my.reverseHero = function (){
my.image.src = "img/hero-1.png";
};
Then I have a code snippet here that shows how I am trying to work this thing out:
my.moveBy = function(dirX, dirY){
var testX = my.posX + dirX;
var testY = my.posY + dirY;
if (testX < 0 || testY < 0 || testX > 21 || testY > 8){
return;
}
if (dirX === -1){
my.reverseHero();
} else if (dirX === 1) {
Game.stage.addChild(Game.hero);
}
My problem at this moment, is that when I walk to the left, the image will actually reverse, but when I try to go to the right again, the image will not reverse.
So the code is actually only running the first if statement and just keeps hanging on there.
Do you have any suggestions what could be wrong ?
Aren't you supposed to reset the image after you set it?
This means setting a second function to return the hero to his original image. Because now it just sets your character to the other image when that action is called, but that keeps that image set, even when that action isn't called anymore.
Game.Hero = function (myX, myY){
var my = new createjs.Bitmap("img/hero.png");
my.reverseHero = function (){
my.image.src = "img/hero-1.png";
};
my.returnHero = function(){
my.image.src = "img/hero.png";
};
Then, in the else if statement, call that function
my.moveBy = function(dirX, dirY){
var testX = my.posX + dirX;
var testY = my.posY + dirY;
if (testX < 0 || testY < 0 || testX > 21 || testY > 8){
return;
}
if (dirX === -1){
my.reverseHero();
} else if (dirX === 1) {
Game.stage.addChild(Game.hero);
my.returnHero();
}
Does this make sense to you, or should I clarify some more?
What's going wrong is that you initialize the image with "img/hero.png", then change that for "img/hero-1.png". This is fine, however, when you call the reverseHero method a second time it won't put back the old image ("img/hero.png").
You can change this by implementing something like this:
var direction = true; // true means forward, false means backwards
my.reverseHero = function (){
my.image.src = direction ? "img/hero-1.png" : "img/hero.png";
direction = !direction;
};

Javascript Google Style Pagination Menu

The math to correctly display a pagination menu via Javascript is driving me insane. Hopefully someone who understands this a little better than me can help me out.
All I need is to understand how to properly display a pagination menu on the client side with javascript.
Lets say I have 10 total items.
The max I want show at once is 1.
The menu should display 5 numbers and the last number should be the last page with a ... if we aren't on the last set of numbers.
So for instance < 1 2 3 4 ... 10 >
User clicks 2 - Start number should be 1, last 4 < 1 2 3 4 ... 10 >
User clicks 4 - start number should 2, last 5 < 2 3 4 5 ... 10 >
User clicks 5 - start should be 3, last 6 < 3 4 5 6 ... 10 >
User clicks 7 - we are on the last set so < 6 7 8 9 10 >
So far all I have is:
var pages = Math.ceil(count / amount);
var maxIterations = 5;
var iterations = Math.min(pages, maxIterations);
var offset = Math.max(0, page-Math.round(maxIterations/2));
for(var i=0; i<iterations; i++) {
var label = page + i;
if(i == maxIterations-1) label = pages; //Last page number
paginatorObjects.push({label: label}); //This create the menu button
}
Basically the point is that I need the numbers to iterate up and down as the user clicks through them (how google does it) without having to use the arrow buttons. I understand jQuery has a nice plugin for this sort of thing, but this has to be done in vanilla javascript.
This problem had me scratching my head for a while too, I recently had to implement it for an AngularJS application, but the pagination logic is pure javascript.
There's a working demo on CodePen at http://codepen.io/cornflourblue/pen/KVeaQL
I also wrote this blog post with further details
Here's the pagination logic
function GetPager(totalItems, currentPage, pageSize) {
// default to first page
currentPage = currentPage || 1;
// default page size is 10
pageSize = pageSize || 10;
// calculate total pages
var totalPages = Math.ceil(totalItems / pageSize);
var startPage, endPage;
if (totalPages <= 10) {
// less than 10 total pages so show all
startPage = 1;
endPage = totalPages;
} else {
// more than 10 total pages so calculate start and end pages
if (currentPage <= 6) {
startPage = 1;
endPage = 10;
} else if (currentPage + 4 >= totalPages) {
startPage = totalPages - 9;
endPage = totalPages;
} else {
startPage = currentPage - 5;
endPage = currentPage + 4;
}
}
// calculate start and end item indexes
var startIndex = (currentPage - 1) * pageSize;
var endIndex = startIndex + pageSize;
// create an array of pages to ng-repeat in the pager control
var pages = _.range(startPage, endPage + 1);
// return object with all pager properties required by the view
return {
totalItems: totalItems,
currentPage: currentPage,
pageSize: pageSize,
totalPages: totalPages,
startPage: startPage,
endPage: endPage,
startIndex: startIndex,
endIndex: endIndex,
pages: pages
};
}
What I ended up doing is this. This is directly from my source code, so there is some templating logic that doesn't make sense, but anyone who might be reading this in the future should be able to adjust according to their needs.
function paginate() {
var paginator = sb.find('#' + moduleName + 'Pagination')[0];
var container = paginator.getElementsByClass('container')[0];
sb.dom.clearAll(container);
if(count && count > 0) {
sb.dom.removeClass(paginator, 'hidden');
var pages = Math.ceil(count / amount);
var maxIterations = 5;
var iterations = Math.min(pages, maxIterations);
var paginatorObjects = [];
var center = Math.round(maxIterations/2);
var offset = page-center;
if(page < center) offset = 0; //Don't go lower than first page.
if(offset + iterations > pages) offset -= (offset + iterations) - pages; //Don't go higher than total pages.
for(var i=0; i<iterations; i++) {
var label = (i+1) + offset;
paginatorObjects.push({label: label});
}
sb.template.loadFrom(templateUrl, 'paginator').toHTML(paginatorObjects).appendTo(container, function(template) {
var pageNumber = template.obj.label;
if(pageNumber != page) {
sb.addEvent(template, 'click', function() {
getAppointments(pageNumber);
});
} else {
sb.dom.addClass(template, 'highlight');
}
});
if(offset + iterations < pages) {
var dots = document.createTextNode(' ... ');
container.appendChild(dots);
sb.template.loadFrom(templateUrl, 'paginator').toHTML({label: pages}).appendTo(container, function(template) {
sb.addEvent(template, 'click', function() {
getAppointments(pages);
});
});
}
var backBtn = paginator.getElementsByClass('icon back')[0];
var forwardBtn = paginator.getElementsByClass('icon forward')[0];
sb.removeEvent(backBtn, 'click');
sb.removeEvent(forwardBtn, 'click');
if(page - 1 > 0) {
sb.dom.removeClass(backBtn, 'hidden');
sb.addEvent(backBtn, 'click', function() {
getAppointments(page-1);
});
} else {
sb.dom.addClass(backBtn, 'hidden');
}
if(page + 1 <= pages) {
sb.dom.removeClass(forwardBtn, 'hidden');
sb.addEvent(forwardBtn, 'click', function() {
getAppointments(page+1);
});
} else {
sb.dom.addClass(forwardBtn, 'hidden');
}
} else {
sb.dom.addClass(paginator, 'hidden');
}
}
When getAppointments() is called from the buttons, I use AJAX to get the next pages results, then this pagination() function is called again from that function so everything gets reset. The variables that determine the amount to show per page and the current page number are global (to the container function) and are not shown in this code sample.
The logic behind this is fairly simple. The center is determined (in my case the max it shows at once is 5, so the center is 3) and then an offset is calculated based on the page number. Clicking 4 will adjust the start number +1, and 2 would adjust it -1.

Having trouble using jQuery to change a class dependent on scroll position

Some very helpful folks wrote and then modified a script to change the class on a nav item when scrolling the page to a related section. Here's the original post: Use jQuery to change a class dependent on scroll position.
My first issue is that the last section of the array is being ignored. Someone else had this question and a solution was offered, which worked for them, but not for me: jQuery class change based on scroll ignoring last section.
The second issue is that even setting aside that the last section in my array is not ever getting selected, the new class name is only getting added to my nav items, but never removed, so that by the time I scroll to the bottom of the page, every item except the last in my nav has "selected" as a class. I'd really appreciate any insight. Here's my code, which is virtually identical to the modified version, except I'm using headings rather than sections (which is maybe significant?):
var $anchs = $('.content h1');
var $navs = $('#zone-submenu div .content > .item-list > ul > li');
topsArray = $anchs.map(function(){
return $(this).position().top - 100;
}).get(),
topsArray.push(window.height);
var len = topsArray.length;
var currentIndex = 0;
var getCurrent = function( top ) { // take the current top position, and see which
for( var i = 0; i < len; i++ ) { // index should be displayed
if( top > topsArray[i] && topsArray[i+1] && top < topsArray[i+1] ) {
return i;
}
}
};
$(document).scroll(function(e) {
var scrollTop = $(this).scrollTop();
var checkIndex = getCurrent( scrollTop );
if( checkIndex !== currentIndex ) {
currentIndex = checkIndex;
$navs.eq( currentIndex ).addClass("selected").siblings(".selected").removeClass(".selected");
}
});
I'm afraid I can't post the site itself - it's in development and I can't reveal it before it goes live. Thanks for any help, though!
This looks a bit odd:
currentIndex = checkIndex;
$navs.eq( currentIndex ).addClass("selected").siblings(".selected").removeClass(".selected");
From what I can tell in your code, it should be:
$navs.eq( currentIndex ).removeClass('selected'); // no dot
currentIndex = checkIndex;
$navs.eq( currentIndex ).addClass("selected");
UPDATED
Fixing the "not displaying the last index item" ...
You are referencing an invalid array element in your getCurrent function - [i+1] is invalid when i == len-1. You need to just return the last element if that happens.
var getCurrent = function( top ) { // take the current top position, and see which
for( var i = 0; i < len-1; i++ ) { // index should be displayed
if( top > topsArray[i] && topsArray[i+1] && top < topsArray[i+1] ) {
return i;
}
}
return len-1;
};

Categories