Javascript transitions not executing in order - javascript

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);
}

Related

How to create animation class library for p5js

I am working on an application where I'd like to provide overlays of different animations onto a range of videos using p5js. I'm looking to organize my classes of animation types so that each animation has a similar structure to update and destroy objects during each loop. My plan is to have an array of animations that are currently "active" update them each iteration of the loop and then destroy them when they are completed. I built a class to fade text in this manner but I'm getting some weird flashy behavior that seems to occur every time a new animation is triggered in the middle of another animation. I've been trying to debug it but have been unsuccessful. Do you have any suggestions as to:
(1) if this is due to my code structure? (and maybe you have a suggestion of a better way),
or
(2) I'm doing something else incorrectly?
Here is the code:
// create an array of currently executing animations to update
// each animation class needs to have one function and one attribute:
// (1) update() -- function to move the objects where ever they need to be moved
// (2) done -- attribute to determine if they should be spliced out of the array
var animations = [];
//////////////////////////////////////////
// Global Variables for Animations //
//////////////////////////////////////////
let start = false;
let count = 0;
function setup(){
let canv = createCanvas(1920, 1080);
canv.id = "myP5canvas";
background(0);
}
function draw(){
background(0);
// Check things to see if we should be adding any animations to the picture
var drawText = random(100);
if (drawText > 98) {
//if (start == false) {
let r = 255;
let g = 204;
let b = 0;
let x = random(width-10);
let y = random(height-10);
animations.push(new TextFader("Wowwwzers!", 100, 'Georgia', r, g, b, x, y, count));
start = true;
count += 1;
}
// Update animations that exist!
for (var i=0; i < animations.length; i++) {
// update the position/attributes of the animation
animations[i].update();
// check if the animation is done and should be removed from the array
if (animations[i].done) {
console.log("SPLICE: " + animations[i].id);
animations.splice(i, 1);
}
}
}
// EXAMPLE ANIMATION
// TEXT FADE
let TextFader = function(words, size, font, red, green, blue, xloc, yloc, id) {
this.id = id;
console.log("create fader: " + this.id);
// translating inputs to variables
this.text = words;
this.size = size;
this.font = font;
// To Do: separating out each of the values until I figure out how to fade separately from the color constructor
this.red = red;
this.green = green;
this.blue = blue;
this.xloc = xloc;
this.yloc = yloc;
// Maybe add customization in the future for fading...
this.fade = 255;
this.fadeTime = 3; // in seconds
this.fadeIncrement = 5;
// Variables to use for destruction
this.createTime = millis();
this.done = false;
}
TextFader.prototype.update = function() {
// Update the fade
// If the fade is below zero don't update and set to be destroyed
this.fade -= this.fadeIncrement;
if (this.fade <= 0) {
this.done = true;
} else {
this.show();
}
}
TextFader.prototype.show = function() {
textFont(this.font);
textSize(this.size);
fill(this.red, this.green, this.blue, this.fade);
text(this.text, this.xloc, this.yloc);
console.log("Drawing: " + this.id + " fade: " + this.fade + " done: " + this.done);
}
Yay, I've got you an answer! It works like expected when you reverse the for loop that loops over the animations.
Because you splice elements of the same array inside the loop, some elements are skipped. For example; animations[0].done = true and gets removed. That means that animations[1] is now in the spot of animations[0] and animations[2] is now in the spot of animations[1].
The i variable is incremented to 1, so on the next loop, you update animations[1] (and skip the animation that is now in animation[0]).
When you reverse the loop, everything before the element you splice stays the same and nothing is skipped.
For example; animations[2].done = true and gets removed. That means that animations[1] is still in the spot of animations[1].
The i variable is decremented to 1, so on the next loop, you update animations[1] and don't skip any elements.
// Update animations that exist!
for (var i = animations.length - 1; i >= 0; i--) {
// update the position/attributes of the animation
animations[i].update();
// check if the animation is done and should be removed from the array
if (animations[i].done) {
//console.log("SPLICE: " + animations[i].id);
animations.splice(i, 1);
}
}

Increment loop but it increments at the end

I’m trying to use an increment loop but I want it to increment at the end of the loop. Sadly, whenever I simply put the i++ at the end of the loop it doesn’t behave like I’d expect or want it to. Anyone mind showing me the proper way of doing it?
The referred increment loop:
for (i = 1; i < 15; i++) {
// do somthing here
}
Here is the loop I’m working with:
for (i = 1; i < 15; i++) {
for (x = 1; x < 15; x++) {
var take = document.getElementById("row" + i + "sm" + x);
Tesseract.recognize(take)
.then(function(result) {
console.log(result.text);
// rows[i][x] = result.text;
})
}
}
What I’d like it to do:
for (i = 1; i < 15) {
for (x = 1; x < 15) {
var take = document.getElementById("row" + i + "sm" + x);
Tesseract.recognize(take)
.then(function(result) {
console.log(result.text);
//rows[i][x] = result.text;
x += 1;
})
i += 1;
}
}
I am using the for loop because I need to iterate over something one by one. How do I properly increment i at the end of the loop?
Here is a video explaining my problem with context and explanation why it is not an ASYNC problem. Sorry if it is hard to follow, ill update it with audio soon so I can explain it propperly.
https://drive.google.com/file/d/1n1ZwNJif5Lb5zfLb2GPpBemObwpOqNf7/view
The problem is that the second one doesnt wait until first one is complete.
You can try with recursion inside then. There maybe some mistake with i,x but you get the point.
You execute first with i=1 and x=1, after the operation is done (then) you call the next until all elements are executed.
function execItem(i, x) {
var take = document.getElementById("row" + i + "sm" + x);
Tesseract.recognize(take)
.then(function(result){
rows[i][x] = result.text;
if (i < 15 && x < 15) {
if (i > 15) {
x += 1
i = 1
} else {
i += 1
}
execItem(i, x)
}
})
}
execItem(1, 1)
As a comment suggests this actually seems likely to be a problem with an asynchronous call (Tesseract...then) inside a loop. By the time the function inside then is called, your values of x and i have already moved on, so you don't get the result you expect.
One way around this would be to use a 'closure' - making a function that creates another function based on the value of i and x.
function getDisplayFunc(row, col) {
function displayRecognisedText(result) {
console.log(row, col, result.text);
//rows[row][col] = result.text;
}
return displayRecognisedText;
}
for (i = 1; i < 15; i++) {
for (x = 1; x < 15; x++) {
var take = document.getElementById("row" + i + "sm" + x);
Tesseract.recognize(take).then(getDisplayFunc(i, x));
}
}
I guess #Mike spot the error on: your code is asynchronous. What does it mean?
So, let's suppose you have this loop:
for (i = 0; i < 10; i++) {
console.log(i);
}
It will print this, right?
0
1
2
3
4
5
6
7
8
9
However, you do not print your value inside the loop directly, but as a follow-up operation to a promise. This makes this code asynchronous. It means that it does not have to execute at the exact moment you call it. I do not have Tesseract here so I will make my loop asynchronous using another very old trick, setTimeout():
for (i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}
If I run it, I get this:
10
10
10
10
10
10
10
10
10
10
What happens is, when I pass the operation we want to do (in this case, printing the i value) to an asynchronous function (recognize().then() in your case, setTimeout() in my case) through a callback (function() {console.log(i);} in my example) the asynchronous function "schedules" the operation to execute as soon as possible, but this "soon" is not faster than the loop. So, the loop finishes executing but our callback is not called, not even once! Since you are not declaring i with let, it is a global variable, so there exists only one i. And since the loop finished, the value of the i variable is 10 already.
It used to be a hard thing to solve, but with ES6 it is quite straightforward: declare i with let!
for (let i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}
The let-ed variable has a new binding at each iteration of the loop, so in practice you have 10 variables called i. The closure of your function will have access only to the one with the right value!
Maybe you should try to use while loop.
Like this:
while i < 15:
//do something
i += 1
For two variables: x, i with embeding:
while x < 15:
//do something
while i < 15:
//do something2
i += 1
x += 1
Hope I understand the problem correctly.

Incorrect syntax on setTimeout

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);
}

Variable "i" acts oddly, jQuery functions not doing what I want them to

I'm making an ascii game.
$("#GameContainer").eq(i).contents().html(map[i]);
This line of code. I have no idea why, but it's not working. I've spent the past few hours trying to figure it out, but nope.
function startGame() {
map = map_Default.slice();
for (var i = 0; i < map.length; i++) {
$("#GameContainer").eq(i).contents().html(map[i]);
}
}
So in the for loop, when called in the eg() function, it acts like it should, being 0, 1, 2, 3, 4, 5, and so on. When called in the html() function, it seems to stay at 0, so instead of rendering the map like normal it repeats the first row of tiles. Also, I'm pretty sure I shouldn't have to use the contents() function, but when I don't the html() function just replaces every child of #GameContainer with map[i].
Here's more:
$(document).ready(function () {
var mapl_Half = Math.floor(map.length / 2);
var trl_Half = Math.floor(map[mapl_Half].length / 2);
var whitespace = Math.floor(trl_Half - (startmessage.length / 2));
map[mapl_Half] = map[mapl_Half].substring(0, whitespace) + startmessage + map[mapl_Half].substring(whitespace + startmessage.length);
for (var i = 0; i < map.length; i++) {
$("#GameContainer").append("<p class=\"tilerow\">" + map[i] + "</p>");
}
$(document).keydown(function (key) {
switch (key.keyCode) {
case 13:
startGame();
break;
}
});
});
The only things left out are the two variables map and map_Default which are arrays of strings.
Here it is published: http://camron.onyxtek.com/Main/AsciiGame/
You can only have one element with a given id.
So
$("#GameContainer")
contains only one element and thus
$("#GameContainer").eq(i)
makes no sense.
But it seems that what you want is the child of index i in #GameContainer. You can get it with
$("#GameContainer .tilerow").eq(i)

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.

Categories