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?
Related
Im trying to make an infinite loop for a carousel when i click a desired div it will be centered at a desired position, if i click the item that is adjacent to the centered position, everything works fine, but if he is more than 1 position away from the center it triggers an effect that doesnt follow the usual logic.
I have tried to solve the problem by checking the distance from the center and than moving the items 1 by 1 n times, but i guess because the loop doesn't wait for the animations to finish im getting this weird effect.
The final outcome should be making an infinite feel to the carousel when you click an item that is 5 positions away from the center it would center it and the ones that are out of view will slide from the respective direction to create a loop
any help will be appreciated, Im relatively new to web dev so a well explained answer will be highly appreciated
JS:
const serviceList = document.querySelectorAll('.service__block');
serviceList.forEach(service => {
service.addEventListener('click', () => {
markSelectedService(service);
moveService(checkDistance(service));
});
});
//Adds the class to the clicked service
function markSelectedService(service) {
removeSelectedClass();
service.classList.add('selected');
}
//Removes the selected class from all the services
function removeSelectedClass() {
serviceList.forEach(service => {
service.classList.remove('selected');
});
}
//Check distance from center
function checkDistance(service) {
let distance = service.dataset.order - 4;
return distance;
}
//Move the service 1 by 1 n times
function moveService(distance) {
if (distance > 0) {
for (var i = 0; i < distance; i++) {
serviceList.forEach(service => {
service.dataset.order = parseInt(service.dataset.order) - 1;
if (parseInt(service.dataset.order) === -1) {
service.dataset.order = 11;
}
});
}
} else if (distance < 0) {
distance = distance * -1;
for (var i = 0; i < distance; i++) {
serviceList.forEach(service => {
service.dataset.order = parseInt(service.dataset.order) + 1;
if (parseInt(service.dataset.order) === 12) {
service.dataset.order = 0;
}
});
}
}
}
Link to codepen:
https://codepen.io/tomyshoam/pen/yLLLYyQ
If you want to wait for the animation's end to trigger the next one you insert a setInterval and set the interval time to your animation's time.
Example:
var i = 0;
var animationInterval = setInterval(function () {
//YOUR LOGIC HERE
if (++i === distance) {
window.clearInterval(animationInterval);
}
}, 500);
Update
After some tries I think I got it working how you wanted, although it's not the better way to do it.
I copied the icons list, one to the right side and one to the left, this way the icons will only move to the other side when they are far far away from the user's view and will not trigger that weir behavior.
Code below:
https://codepen.io/diogoperes/pen/oNNNBGY
I have a circle, consisting of 12 arc segments and I want to allow the user to see the transition from the start pattern to the end pattern. (there will be many start and end patterns).
Here is my code so far:
http://codepen.io/blazerix/pen/jrwNAG
function playAnimations(){
var totalLength = document.getElementsByClassName("container")[0].children.length
for(var i = 0; i < totalLength; i++){
var current_pattern = document.getElementsByClassName("container")[0].children[i]
console.log(current_pattern)
for(var j = 0; j < 12; j++){
$('#LED' + (j+1) ).css('transition-duration', '0s');
$('#LED' + (j+1) ).css({fill: current_pattern.children[1].children[j].style.backgroundColor});
}
setTimeout(function () {
for(var k = 0; k < 12; k++){
$('#LED' + (k+1) ).css('transition-duration', "" + current_pattern.children[3].children[0].value + "ms");
$('#LED' + (k+1) ).css({fill: current_pattern.children[2].children[k].style.backgroundColor});
}
}, 150);
}
}
The outer for loop goes through all of the patterns, and the two inner for loops will go through the start and end pattern respectively. For some reason, my program only displays the animation of the very last pattern. I suspect this is because the code is executing really quickly - however I am unsure of how to fix this.
Does anyone know a good workaround or what I could possibly do to rectify this issue? Any feedback or help is appreciated.
Ok, not entirely understanding all the parts of your code, I've whipped this up. It doesn't work just yet, but you may get the idea of what I'm trying to do: wait 250 milliseconds before you fire off the next animation, once you run out of siblings, bounce to the other animation. I can't spend any more time on this, but I hope this gets you where you want to be:
function playAnimations() {
var $patternHolder = $(".container");
playAnimation($('#LED1'), 0, $patternHolder, 1, 1);
}
function playAnimation($target, index, $patternHolder, childPatternIndex, animationNumber) {
//just set both fill color and transition in the object you pass in:
//.eq() chooses a child, returns jQuery object of that child by index
//Use jQuery to get background-color style
$target.css({ fill: $patternHolder.children(childPatternIndex).children().eq(index).css("background-color"), transition: "0s" });
setTimeout(function () {
if ($target.parent().next().length > 0) {
playAnimation($target.parent().next(), index++);
} else if (animationNumber == 1) {
playAnimation($("#LED1"), 0, patternHolder, 3, 2);
}
}, 250);
}
I'm trying to make a faux loading screen, and I need delays between loading messages of about 20-50ms or so so that people can actually see what's going on before it cuts to the initialized screen. The button that activates this goes to the following function:
function gameinit() {
for (k = 0; k <=1; k += 0.125) {
setTimeout(function () {
var nexttxt = "Loading... " + toString(100 * k) + "%"
}, 20);
displayupdate(nexttxt);
}
}
However this comes up as an incorrect syntax (on JSfiddle - https://jsfiddle.net/YoshiBoy13/xLn7wbg6/2/) when I use JShint - specifically lines four and five. I've looked at the guides for this and everything seems to be in order. What am I doing wrong?
(Note: displayupdate(nexttxt) updates the <p> tags with the next line of text)
When executing the script, nothing happens - the sixteen lines of text on the HTML move up as normal, the top eight being replaced with the eight generated by the gameinit() function, but the gameinit() only generates blank. If the script is executed again, it just outputs eight lines of 112.5% (as if it was the 9th iteration of the for loop).
I'm almost certain it's something elementary that I've missed, could someone please tell me what I've done wrong?
Use setInterval() instead, you can clear interval using clearInterval()
function gameinit() {
displayupdate("Loading... 0%");
var k = 0;
var inter = setInterval(function() {
if (k < 1) {
k += .25;
displayupdate("Loading... " + 100 * k + "%")
} else {
clearInterval(inter);
}
}, 2000);
}
function displayupdate(d) {
console.log(d);
}
gameinit();
here is another function can do this better ---- setInterval
var txt = '';
var time = 0;
var id = setInterval(function(){
console.log("loading..."+time/8*100+"%");
if(time++>7)
clearInterval(id);
},1000);
setTimeout doesn't work as you would expect it to work inside loops. You have to create a closure for each loop variable passed on to setTimeout, or create a new function to execute the setTimeout operation.
function gameinit() {
for (var k = 0; k <= 1; k += 0.125) {
doSetTimeOut(k);
}
}
function doSetTimeOut(k) {
setTimeout(function() {
var nexttxt = "Loading... " + toString(100 * k) + "%"
}, 20);
displayupdate(nexttxt);
}
I am trying to make a simple game in HTML5 Canvas. I want, at most, two keyboard input per second.
This is my code so far:
function move( x, y, r ) {
var canid=document.getElementById("draw");
canid.addEventListener('keydown',readkey,false);
function readkey(e) {
if(e.keyCode == 37) {
clearField();
x = x-draw.width / 10;
redrawpic(x,y,r);
}
else if(e.keyCode == 38){
clearField();
y = y-draw.height / 10;
redrawpic( x, y, r );
}
//..........etc
}
}
The function move is used to move picture from one cell to another. How to set delay a between two moves?
You can use a timestamp to check when the last event has occured:
function move(x,y,r){
/* your variable declarations*/
var d1 = Date.now();
var d2;
function readkey(e){
d2 = Date.now();
// difference between timestamps needs to be 500ms
if(d2-d1 > 500){
// set old Timestamp to new one
d1 = d2;
/*
Rest of your code
*/
}
This allows one key event every 500ms. Not exactly the same like 2 events in 1 second (which could occure in 50ms and then pause 950ms), but maybe close enough?
Timeout/Interval is possible too, but I personally dislike the overhead of consecutive (possibly unnecessary) timeout calls.
var throttle = false;
function readkey(e) {
if (throttle)
return;
throttle = true;
setTimeout(function () { throttle = false; }, 500);
/* the rest of your code */
500ms is two inputs per second, but they are throttled individually. You could also keep a count of how many inputs there have been in a full second. Something like
if (throttle > 1)
return;
throttle++;
setTimeout(function () { throttle = 0; }, 1000);
I have a game I am making: http://www.taffatech.com/DarkOrbit.html
What I want to be able to do is when I kill a certain amount of enemies it will level up. This part is working, however I also want the spawn amount to increase when you level up.
In my loop function:
function Loop()
{
if (isPlaying == true)
{
updateLevel(); //this updates the level but it calls it every frame which might be bad.
Player1.draw();
drawAllEnemies();
updateStats();
requestAnimFrame(Loop);
}
The update level function:
function updateLevel()
{
if(Player1.enemiesKilled <3)
{
level = 1;
}
else if(Player1.enemiesKilled > 3 && Player1.enemiesKilled <= 9)
{
level = 2;
}
else if(Player1.enemiesKilled > 9 && Player1.enemiesKilled <=18)
{
level = 3;
}
else if(Player1.enemiesKilled > 18 && Player1.enemiesKilled <= 38)
{
level = 4;
}
else if(Player1.enemiesKilled > 38 && Player1.enemiesKilled <= 70)
{
level = 5;
s
}
else if (Player1.enemiesKilled > 120)
{
level = 6;
}
if(level == 1)
{
spawnAmount = 1;
}
else if(level == 2)
{
spawnAmount = 2;
}
else if(level == 3)
{
spawnAmount = 3;
}
else if(level == 4)
{
spawnAmount = 4;
}
else if(level == 5)
{
spawnAmount = 5;
}
else if(level == 6)
{
spawnAmount = 6;
}
}
spawn enemy function:
function spawnEnemy(number) //total enemies starts at 0 and every-time you add to array
{
for (var x = 0; x < number; x++)
{
enemies[enemies.length] = new Enemy();
}
}
My init:
function init()
{
spawnEnemy(spawnAmount);
drawMenu();
sndIntro.play();
document.addEventListener('click', mouseClicked ,false);
}
I tried adding: spawnEnemy(spawnAmount); to after I change the spawn amount global var in the updateLevel function but as this gets called every frame it makes hundreds of enemies fly at me. How can I solve this problem?
If I try something like this hundreds of enemies are genrated, I assume because it gets called every frame:
if(level == 1)
{
spawnAmount = 1;
spawnEnemy(spawnAmount);
}
If your intent is to increase the spawn rate on each level, I'd suggest you start off with a spawnRate first. Let spawnRate represent the number of game updates in between enemy spawns. Every time an enemy spawns, decrement another variable (say spawnRateCountdown), until that variable reaches 0, Then spawn an enemy. Reset the variable back up to the spawnRate. Rinse and repeat.
spawnRate = 60; /* one enemy every 60 updates. */
spawnRateCountdown = spawnRate;
function updateLoop() {
spawnRateCountdown--;
if (spawnRateCountdown == 0) {
spawnRateCountdown = spawnRate;
spawnEnemy();
}
}
After that, you can just update your spawnRate variable as you see fit.
The problem is that you don't create new enemies from time to time, but just shift the same enemy further away on its explosion (i.e. call recycleEnemy method). This single enemy is spawned only once: spawnEnemy function is called in init method only with argument 1.
You should rework enemy spawning algorithm. Below is a pretty good sample, which increases enemy spawning rate:
Remove number argument from spawnEnemy function: spawn only one enemy
Change Enemy.prototype.recycleEnemy function to remove this from enemies. Be cautious: if you'll call this function inside a loop by enemies, you will need to decrease counter to make sure that you won't skip next enemy: --i
Add a function getSpawnCooldown() { return 100 / spawnAmount; } (100 is approximate constant, you can variate it to change initial spawning rate)
Add a variable time which means the number of effective Loop calls from the beginning, defaults to 0
Add a variable lastSpawnTime, set it to 0
Inside if block of Loop function, add lines:
++time
if (time >= lastSpawnTime + getSpawnCooldown()) spawnEnemy();
In spawnEnemy function, add a line: lastSpawnTime += getSpawnCooldown()
Generally, you should better structurize your code. At least, split it to several js files which are responsible for different parts of your application.