I am very new to javascript.
I am working a webgame. I am trying to use for loop to let players enter their names and select gamepiece by clicking. I'm struggling to make code to wait for user input (click on a game piece image) before moving on to the next player.
function GetPlayerNames(){
for (var i=1; i<=NumberPlayers; i++) {
ctxPlayers.fillStyle = "blue";
ctxPlayers.textAlign = "center";
var PlayerName = prompt("Name");
ctxPlayers.fillText (PlayerName + " Select Game Piece", cnvPlayers.width/2,cnvPlayers.height* (1/6));
// here i want to wait for a player to click on a game piece image
};
};
in vb.net version i used do while loop with application.doevents. It's my understanidng javascript doesn't have an equivalent, but i hope for a rather simple solution that will allow my to accomplish the same.
Thanks in advance.
The problem is that you're using prompt, which stops the execution of the script, and then you need to wait for a click, but there's no way to stop the execution for that like you can with prompt.
A better approach would be to check the values of all player names and images each time there is a change in a name or an image is clicked.
Will not give you any code for that, learn how you'd do it, it should be enough to get you started. So please don't use prompt combined with click listeners, but create an input field.
Here's a super simple example:
// keeping our input elements in a variable
var inputs = document.getElementsByTagName('input');
// loop over them to attach them a keyup listener
for(var i = 0; i < inputs.length; i++) {
inputs[i].addEventListener('keyup', checkNames);
}
// this function is called on each keyup event (and once in the beginning)
function checkNames() {
var playersReady = 0;
for(var i = 0; i < inputs.length; i++) {
if (inputs[i].value != "") {
playersReady++; // increment the value if the input is not empty
}
}
// output the message
document.getElementById('status').textContent = "Waiting for " +
(inputs.length - playersReady) + " more player" +
(inputs.length - playersReady == 1 ? "" : "s") + // this line is just for pluralizing
"..." +
(inputs.length - playersReady == 0 ? "The game can start now!" : "");
}
// initial call, just so we get the first status message
// without needing a keyup event first
checkNames();
<input type="text" placeholder="Player1 name" />
<input type="text" placeholder="Player2 name" />
<input type="text" placeholder="Player3 name" />
<p id="status"></p>
You can expand this example by adding images, and keeping track of how many of them are selected, similarly to how I keep track of non-empty player names.
Once you get comfortable with that, a bit more advanced task would be to try creating boxes for player names directly on canvas and listening for focus and keyboard inputs on them, but that's a whole another question...
Not required. When input occurs, the browser will call the JavaScript functions that you have attached to those events. Simply put, the browser has its own main loop that calls your code on a standardized set of conditions.
Let's say someone presses something on the keyboard. The browser will fire keydown and keyup events as the button is pressed and released. Also, if you use the statement window.requestAnimationFrame(yourFunction);, then yourFunction() will be called as early as possible in the next frame. If yourFunction() also calls window.requestAnimationFrame(yourFunction);, then yourFunction() will be your main loop.
In your case, most of your heavy code will be attached to the mousedown, mouseup, or click events. You should not need a main loop.
function yourClickHandler() {
//Whatever happens when your gamepiece is clicked.
}
/*
* This attaches the click event to some element that you use as your gamepiece.
* If you're using Canvas, you will attach it to the canvas (instead of #GamePiece
* and then need to figure out what is in the pixel that you clicked on in it.
*/
document.getElementById("#GamePiece").addEventListener("click", yourClickHandler, false);
Your game can just sleep between user clicks, unless you need complex animations and stuff that cannot be done with CSS transitions and so forth. If it's, say, a turn-based strategy, then you can just make the piece look clicked, then sleep, then give it a destination, then sleep, then select something else, then sleep. Etc.
If you do need complex animations and stuff...
Then it is best to have yourClickHandler() do as little as possible. Try to do nothing more than set a variable and return. Your requestAnimationFrame()-based draw/update functions should use those variables to perform the intense calculations then.
For instance, if you are making a character walk, let all the walking / falling / etc. happen once per frame. Just keep track of whether the button is pressed or not (or how far a joystick is tilted, etc.)
The reason for this is simple: Drawing happens once per frame, but input events might happen several times. You want to draw to the screen only once per draw to the monitor. Also, input events can happen at any time. You don't want a big calculation to happen a tenth of a millisecond before the frame is needed by the monitor. You want that to happen as early in the frame as possible.
Related
I have this fiddle : https://jsfiddle.net/reko91/e6uwqnof/2/
On button press it creates 50 rectangles that all move down towards the bottom of the screen.
for(i=0;i<50;i++){
enemyArray.push(new enemy(normalBullet.x+i*5, normalBullet.y, normalBullet.speed, 1, 10, "#F00"));
}
Works fine on first click, but once I start adding more, it really starts to lag. Is there a best of practice way of dealing with hundreds of moving elements ? Or is HTML and Javascript not the best language to deal with this amount of moving data ?
Your main problem is in the update function:
function update() {
// enemy.update();
//if (keystate[SpaceBar]) {
$('#newEnemy').click(function() {
createNewEnemy()
})
//...
}
Probably a mistake, but you're attaching the event every time update gets called, which is 60 times per seconds! (Until it can't do it anymore, that is.)
This means that every time you press the button, you generate a ton of elements right in the canvas.
Move the event listener addition outsite update and you're golden.
You are assigning button pushes inside the frame loop, so when you push it, it's actually calling the button push however many times the loop has run.
Move this code outside:
$('#newEnemy').click(function() {
console.log("createEnemy");
createNewEnemy()
});
HTML
<div id="backspace" ng-click="deleteString(''); decrementCursor();">
JS
<script>
$scope.deleteString = function() {
if($scope.cursorPosVal > 0){
//$scope.name = $scope.name - letter;
$scope.name = [$scope.name.slice(0, $scope.cursorPosVal - 1) + $scope.name.slice($scope.cursorPosVal)].join('');
console.log($scope.name);
setTimeout(function(){ setCaretPosition("inputBox", $scope.cursorPosVal); }, 30);
} else {
$scope.cursorPosVal = 1;
}
};
</script>
I am designing an on screen touchscreen keyboard. This is my backspace button. I am going to make it so that when you click and hold the backspace button, it starts removing characters automatically. I don't know where to begin with creating a setInterval, and I know a setInterval is exactly what I need to use here.
If I'm not wrong, you want that while you're keeping your button pressed, a function repeats itself.
You're right with setInterval(). However, the way you manage the event is wrong.
Take a look at this fiddle (It's not your code, but a simple example is the best way to understand):
http://jsfiddle.net/daq9atdd/1/
$(function(){
var interval = null;
$('#myButton').mousedown(function(){
interval = setInterval(function(){
console.log('Hello !');
}, 250);
});
$('#myButton').mouseup(function(){
clearInterval(interval);
});
});
I start the interval when the button is pressed, store it, and clear it when the button is released.
You’re so sure about setInterval.
If browser briefly hangs for whatever reason (say some background task), setInterval would go on queueing your backspace calls until it has some CPU time. This means user may see no change and hold backspace longer than needed, and then see a whole bunch of characters suddenly vanish when browser is back to normal.
Thus by setting a timeout after every call you’re making sure user won’t remove more characters than needed. Might be important if the goal is to improve UX.
Example implementation with AngularJS directives and setTimeout
See also:
setTimeout or setInterval?
noKid’s fiddle updated with setTimeout in mind
I'm trying to limit the user's ability to click on an object to a certain time limit. I looked around and found that apparently, setTimeout() is the correct function to use for this type of thing. I've applied the function to my code, but its not working. I'm thinking/know now that the problem is that the setTimeout in my code isn't limiting the actual click event, which I need to do. Here is a snippet of my click code:
function clickRun(event) {
var $objectVersion = correspondingObject(event.target.id);
if (isAnyVisible() == false) { // none open
$objectVersion.makeVisible();
} else if (isAnyVisible() && $objectVersion.isVisible()) { //click already open div
$objectVersion.makeInvisible();
} else if (isAnyVisible() && $objectVersion.isVisible()==false) { //different div open
searchAndDestroy();
$objectVersion.delay(600).makeVisible();
};
};
$('.ChartLink').click(function(event) {
setTimeout(clickRun(event),5000);
});
I've also created a JSFiddle to represent what I'm talking about: http://jsfiddle.net/FHC7s/
Is there a way to achieve limiting the actual click detection on the page?
I think the easiest way to do it is to keep track of the time of the previous click and if the current click is too soon after that, then don't do anything:
onClick = function(){
if(new Date().getTime() - lastCheck < MIN_CLICK_SPACING) return;
}
Have a look at this JSFiddle, I've set it up so you can have the button disable itself for time duration after detecting a click. Just make sure to remember how your closures are operating with your setTimeouts.
Your code contains an error... your line should be
setTimeout(function(){clickRun(event)},5000);
but even then I don't think that's exactly what you're looking for; that code will "delay" the click by 5 seconds, not actually prevent more clicks. If your true intent is to ignore all clicks after a certain amount of time, then I would go with mowwalker's answer; there's no way to stop the clicks, but you can check to see if you should honor them or not.
EDIT: For clarity, the question is why doesn't the code below work as expected (why does it not animate for the duration of the while loop), how can I improve it and how can I red in the unit that it should travel via a user input slider.
My aim is to have shape animates it's way down the screen.
A button will start and stop the animation. There will also be an input for the rate of change or speed at which it travels.
I can make it continuously travel down the screen but the following code doesn't work - I've used ble as a test variable, in the final scenario I'd hope that this would be replaced with something similar to while(stop != true) or something similar.
startDescent.onclick = function(){
startDescent.disabled = true; //prevent it being clicked again
//animation process
var ble = 1;
while(ble < 10){
console.log(ble);
testDrillbit.animate('top', '+=1',{
duration: 1000,
onChange: canvas.renderAll.bind(canvas),
onComplete: function(){
startDescent.disabled = false;
}
});
ble++;
}
};
the +=1 increment should also read in from a user input box, any suggestions on how to achieve this would also be very welcome. Thanks for all and any help.
I believe you are making use of Fabric JS to provide the animation logic. My answer is based on that assumption.
The issue has to do with your intepretation of how the animate function works. It is not a synchronous call. So your loop will essentially initialize the animate action 10 times, not execute 10 animations. Given that the action you defined was to move object "testDrillBit" down 1 pixel over a period of 1 seconds, it would probably appear like nothing happened.
To approximate the operation you are trying to perform, you would need to employ a callback that basically indicates when the animation is complete, do it again, until the user hits their "stop" button. This would probably cause a jerky animation though. Alternatively you can set an arbitrarly large endpoint for the animation and add an abort handler, but you would then need to determine your rate of change (pixels/time) to come up with the right duration.
It's not clear that this library is appropriate for your implementation, but I cannot offer an alternative at this time. The code example below illustrates the second option while illustrating the points you had asked for, a stop mechanism, arbitray rate of change etc. The significant change is rather than specifying +=1 for the rate of change, we alter the duration it takes for the animation to complete and animate over a larger distance (in this case the canvas height).
First, we add a stop button and an input for our speed:
<button id="stop" disabled="true" onclick="stop=true;">Stop</button>
<form>
<input type="text" id="speed" value="10" />
</form>
Then, in our script box we make sure we can use these values and then employ them in the onclick handler.
var stopBtn = document.getElementById('stop');
var speedBox = document.getElementById('speed');
var stop = false;
startDescent.onclick = function() {
// Get our speed, in case the user changes it. Speed here is actually the duration
// of the animation, not a true velocity. But, we can do something like entering 0.5
// and "slow down" the animation
var speed = 10000 / (new Number(speedBox.value));
stop = false; // ensure that we won't abort immediately
stopBtn.disabled = false; // enable the stop button
startDescent.disabled = true;
// I chose canvas.height as an arbitrary fixed distance. Not this won't stop the
// the element from rolling out of the canvas, its just a fixed value.
// The significant change is the addition of the "abort" function which basically
// polls our stop variable to determine whether the animation should be aborted.
testDrillbit.animate('top', "+="+canvas.height, {
duration: speed,
abort: function () {
// If the user has clicked the stop button, flip our buttons
if (stop) {
startDescent.disabled = false;
stopBtn.disabled = true;
}
return stop;
},
onChange: canvas.renderAll.bind(canvas),
onComplete: function() {
startDescent.disabled = false;
stopBtn.disabled = true;
}
});
};
The above code should allow the user to alter the "speed" by stretching or shortening the amount of time to perform the animation. In addition you have your mechanism to stop the animation partway through the execution.
I created a relatively small dynamic banner rotation script with icons at the bottom for bringing a particular banner into focus. Firing a mouseenter over a banner pauses the show, but sometimes when I mouseout from my banner, the delay for certain banners gets shortened. I'd even understand if it just happened once, but the delay is then set for that shorter amount of time every time the banner comes back around in the rotation, and often the shortening happens in one other place in the list of banners, as well. Sometimes this can be corrected by an as yet undetermined set of actions. I'm starting to suspect that my logic is catching the loop in the middle somewhere and so the process branches out, runs two loops, which appear to speed up the calling of the showNextBanner function. Not sure how to solve this. I've put in tests to see if it's currently in play mode, to no avail.
I include what I think are the relevant parts of the code below.
var firstRun = true;
var play = true;
var current = 0;
var banners = $$( '.banner' );
banners.invoke( 'hide' );
var images = $$( '.image' );
var icons = $$( '.icon' );
//dynamically clones an initial icon to match the number of banners
initIcons();
banners.invoke( 'observe', 'mouseenter', function( field ) {
play = false;
});
banners.invoke( 'observe', 'mouseleave', function( field ) {
if( !play ) {
play = true;
showNextBanner().delay(3);
}
});
icons.invoke( 'observe', 'click', function( field ) {
play = false;
hideBanner( current );
showBanner( findObj( icons, field.findElement()));
});
showNextBanner().delay(3);
function hideBanner( which ) {
icons[ which ].src = blankIconSRC;
banners[ which ].hide();
}
function showBanner( which ) {
icons[ which ].src = selectedIconSRC;
banners[ which ].show();
current = which;
}
// loops the hiding and showing of icons
// (mouseenter sets play to false)
function showNextBanner() {
if( play ) {
if( !firstRun ) {
if( ++current == banners.length ) current = 0;
var previous = 0;
( current == 0 )? previous = banners.length - 1: previous = current - 1;
hideBanner( previous );
} else {
icons[0].src = selectedIconSRC;
firstRun = false;
}
showBanner( current );
showNextBanner.delay(3);
}
}
}());
After all that, the client wants a jQuery solution so he can have a slide-in effect not available via scriptaculous. So all that work is down the drain. The good news is that I can just use jCarousel, probably, and tweak the stylesheet. Thanks for the help!
I suspect what is happening is that you've got multiple .delay calls stacking up. So you've got one with less than 3 seconds remaining and showNextBanner is called again, setting another delay timer.
As I read the docs, it appears .delay is intended to put gaps in the jquery event pipeline, rather than actually delay function calls. You may benefit from switching to calling setTimeout instead of delay, so that you get a handle to the timeout, which you can then cancel before setting a new timeout (or cancel if play is set to false, then reset when play is true again) This is mentioned in the JQuery docs for .delay
My guess is that since you don't "cancel" the delay()'ed function, they hang around for too long, but they don't do anything when they fire, because play is false. But once play is true again, the all start having an effect again.
You can save the returned value for delay() and cancel the timer by using clearTimeout() with the value.
However, I'd also suggest that you use a single container for all the banners (and maybe put the the icons in there too), and set the mouseenter/mouseleave events on that, rather than on individual banners. Then there's just a single element that'll start/stop the banner rotation. If you also split everything up in specific functions that play and stop the rotation, and one to show a specific banner, you can possibly get a cleaner code structure.
Here's an example (it's just something I put together for fun rather than an edit of your code, so it's quite different. Sorry. But hopefully you can still use for something)