Javascript pause in WinRT for animation - javascript

I want to pause execution in javascript so that I can animate the appearance of text on the screen. My code is currently this:
function AnimateWord(word) {
for (var i = 0; i <= word.length; i++) {
myTest.textContent += word.charAt(i);
// need to pause here and then continue
}
}
I've done some research and the preferred method to do this in javascript seems to be setTimeout, although I can't really see how that would work in this case without creating a recursive loop (which just doesn't seem like the right solution to me).
Is there a way around this, or am I stuck with setTimeout and a recursive loop?
EDIT:
Based on some additional tests, I've tried using the Promise.timeout:
for (var i = 0; i <= word.length; i++) {
WinJS.Promise.timeout(1000).then(TypeLetter(word.charAt(i)));
}
function TypeLetter(letter) {
myTest.textContent += letter;
}
But this doesn't seem to actually pause. In fact, it seems to completely ignore the timeout. I've also tried:
setTimeout(TypeLetter(word.charAt(i)), 1000);
With basically the same results. this page seems to imply that it should wait and then execute the task. I'm quite new to WinJS, but am equating a promise to an await keyword in C#.

setTimeout/setIngerval/requestAnimationFrame are pretty much your only choices. I wouldn't call them recursive perse - while you do call your same function over & over. The calls tack is completely independent.
What kind of animation are you really trying to create? It may be better to create a span for each character, have them hidden, and then fade/translate the, in using CSS animations.

var i = 0;
var str = "plz send teh codez";
var intervalId = setInterval(function(){
myTest.textContent += str.charAt(i);
if (++i >= str.length)
clearInterval(intervalId);
}, 1000);
demo http://jsfiddle.net/qxfVu/1/

Does this do what you are looking for:
var array1 = [];
function AnimateWord(word) {
var test = $('#test');
for (var i = 0; i <= word.length; i++) {
array1.push(word.charAt(i));
test.delay(100).queue(function() {
this.innerHTML += array1[0];
array1.splice(0, 1);
$(this).dequeue();
});
}
}
please see fiddle link as well: http://jsfiddle.net/7Ea9u/

Related

Making a delay in Javascript [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
I am working on a WordPress plugin. One of its features involves hiding and revealing segments of text by class using <span>.
This functionality works, but I have been hoping to enhance it by having the segments of text reveal one letter at a time (quickly of course) as though they were being typed out very quickly, rather than all at once in large chunks.
I know there are animations out there for this kind of thing ... and perhaps that would be a better solution, but I've been trying to keep it. But the functionality is not really graphic or "animation" oriented; my intent is more just to make a text-based feature look prettier.
I've gotten the portion of the code that builds each segment of text character by character, but I'm trying to insert a very short (5-10ms) delay between each character so that the effect is actually visible. I simply cannot get the setTimeout function to work; can anyone please give me some suggestions?
For simplicity I'm just including the segment of the text that does this; let me know if more context is needed. The following is the FOR loop that goes through every element of an array called cols[] and reveals each element in the array by character. This code works but the delay is never observed.
numberofSnippets = the size of the array cols[]
for (c = 0; c < numberofSnippets; c++)
{
h=0;
currentshown = '';
snippet = cols[c].textContent;
sniplength = snippet.length;
(function addNextCharacter()
{
onecharacter = snippet.charAt(h);
currentshown = currentshown.concat(onecharacter);
cols[c].textContent = currentshown;
h=h+1;
if (h < sniplength) {window.setTimeout(addNextCharacter, 200); }
})();*/
}
}
}
There were a few oddities in your code that was preventing the setTimeout from performing as expected, mostly due to the closure reusing variables within the loop due to the fact that the loop isn't going to wait for the IIFE to finish recursively executing with a setTimeout. I solved that by moving those variables to parameters passed to addNextCharacter.
var cols = document.getElementsByClassName('foo');
var numberofSnippets = cols.length;
for (var c = 0; c < numberofSnippets; c++) {
(function addNextCharacter(h, c, snippet, sniplength, currentshown) {
var onecharacter = snippet.charAt(h);
currentshown = currentshown.concat(onecharacter);
cols[c].textContent = currentshown;
h = h + 1;
if (h < sniplength) {
setTimeout(function () {
addNextCharacter(h, c, snippet, sniplength, currentshown);
}, 10);
}
})(0, c, cols[c].textContent, cols[c].textContent.length, '');
}
<div class="foo">Apple</div>
<div class="foo">Banana</div>
<div class="foo">Orange</div>
<p class="foo">There were a few oddities in your code that was preventing the setTimeout from performing as expected, mostly due to the closure reusing variables within the loop due to the fact that the loop isn't going to wait for the IIFE to finish recursively executing with a setTimeout. I solved that by moving those variables to parameters passed to addNextCharacter.</p>
And here's the obligatory .forEach version which avoids needing to pass the variables around as parameters.
var cols = document.getElementsByClassName('foo');
var numberofSnippets = cols.length;
[].forEach.call(cols, function(el) {
var snippet = el.textContent;
var sniplength = snippet.length;
var currentshown = '';
(function addNextCharacter(h) {
var onecharacter = snippet.charAt(h);
currentshown = currentshown.concat(onecharacter);
el.textContent = currentshown;
h = h + 1;
if (h < sniplength) {
setTimeout(function() {
addNextCharacter(h);
}, 1000);
}
})(0);
});
Well, one issue is that you're setting your timeout to 0, which means, effectively 'next tick'. If you want a 5 second delay, for example, you need to put 5000 in there as the second param.

Optimizing a for loop in JS (push inside)

I have the following loop. The length is around 1500 points. But this snippit might get called multiple times on a page load (6-7).
buffer[xname] = [xname];
buffer[yname] = [yname];
for (var i = 0; i < rawdata.length; i++) {
buffer[xname].push( rawdata[i][0] );
buffer[yname].push( rawdata[i][1] );
}
I need to do this operation in the browser (it is used to condition the data before plotting them).
Currently this makes the browser very slow.
I tried to use a setTimeout() to ease the event loop a bit. That works but it takes seconds.
Is there any way to make this loop faster? Maybe some sort of mapping?
You can reduce the loop to half by doing:
buffer[xname] = [xname];
buffer[yname] = [yname];
var dataLength = rawdata.length;
for (var i = 0; i < dataLength / 2; i++) {
buffer[xname][i] = rawdata[i][0];
buffer[yname][i] = rawdata[i][1];
buffer[xname][dataLength - i -1] = rawdata[dataLength - i -1][0];
buffer[yname][dataLength - i -1] = rawdata[dataLength - i -1][1];
}
Not sure if the change between using push or direct assignment would impact enough to make the execution time the same.
Thanks to #royhowie
Why is array.push sometimes faster than array[n] = value?
If you have control over the source of rawdata, you might want to consider changing it so it can be used without additional processing.

jQuery append in loop - DOM does not update until the end

When looping over a large collection and appending it to the DOM, the DOM only refreshes after all items have been appended. Why doesn't the DOM update after each append() call? Can I force the DOM to refresh after each append (or maybe after each n number of appends)?
var i = 0;
for (i=0; i<5000; i++) {
$('#collection').append('<li>Line Item</li>');
}
Link to jsfiddle
NOTE: I understand that better performance (avoiding DOM reflow) can be achieved by appending all elements to a local variable, and then appending that variable to the DOM. But I want the first n elements to render on the screen, then the next n, etc. until all elements are rendered.
Most everything in the browser stops while a Javascript is running. This includes for example DOM rendering, event handling, image animation.
The only way to cause the DOM to be rendered is to end the Javascript. You can use the setTimeout method to start code again after the update:
var i = 0;
function appendSomeItems() {
for (var j=0; j<50; i++, j++) {
$('#collection').append('<li>Line Item</li>');
}
if (i < 5000) window.setTimeout(appendSomeItems, 0);
}
appendSomeItems();
Demo: http://jsfiddle.net/Uf4N6/
you need to give back control to the browser every once in a while:
var current = 0
function draw() {
var next = current + 10
for(var i = current; i < next; i++) {
$('#collection').append('<li>Line Item</li>');
}
current = next
setTimeout(draw, 50);
}
draw();
Generally, please don't touch DOM too many times. It is a performance killer as you have already observed.
var result="";
for (i=0; i<5000; i++) {
result+='<li>Line Item</li>';
}
$('#collection').append(result);
In this way, you touch DOM only once!
An alternative way is using array.
var result=[];
for (i=0; i<5000; i++) {
result.push('<li>Line Item</li>');
}
$('#collection').append(result.join(""));
If that's really what you want to do...
var i = 0;
for (i=0; i<5000; i++) {
setTimeout(function() {
$('#collection').append('<li>Line Item</li>');
}, 0);
}

how restricted is recursion in javascript?

I guess its to stop browsers getting nailed all the time by duff code but this:
function print(item) {
document.getElementById('output').innerHTML =
document.getElementById('output').innerHTML
+ item + '<br />';
}
function recur(myInt) {
print(myInt);
if (int < 10) {
for (i = 0; i <= 1; i++) {
recur(myInt+1);
}
}
}
produces:
0
1
2
3
4
5
6
7
8
9
10
10
and not the big old mess I get when I do:
function recur(myInt) {
print(myInt);
if (int < 10) {
for (i = 0; i <= 1; i++) {
var x = myInt + 1;
setTimeout("recur("+x+")");
}
}
}
Am I missing something or is this how you do recursion in JS? I am interested in navigating trees using recursion where you need to call the method for each of the children.
You are using a global variable as loop counter, that's why it only loops completely for the innermost call. When you return from that call, the counter is already beyond the loop end for all the other loops.
If you make a local variable:
function recur(int) {
print(int);
if (int < 10) {
for (var i = 0; i <= 1; i++) {
recur(int + 1);
}
}
}
The output is the same number of items as when using a timeout. When you use the timeout, the global variable doesn't cause the same problem, because the recursive calls are queued up and executed later, when you have exited out of the loop.
I know what your doing wrong. Recursion in functions maintains a certain scope, so your iterator (i) is actually increasing in each scope every time the loop runs once.
function recur(int) {
print(int);
if (int < 10) {
for (var i = 0; i <= 1; i++) {
recur(int+1);
}
}
}
Note it is now 'var i = 0' this will stop your iterators from over-writing eachother. When you were setting a timeout, it was allowing the first loop to finish running before it ran the rest, it would also be running off the window object, which may remove the closure of the last iterator.
Recursion is very little restricted in JavaScript. Unless your trees are very deep, it should be fine. Most trees, even with millions of elements, are fairly wide, so you get at most log(n) recursive calls on the stack, which isn't noramally a problem. setTimeout is certainly not needed. As in your first example, you're right that sometimes you need a guard clause to guarantee that the recursion bottoms out.

JS setTimeout Not Delaying

Alright guys, I'm trying to add numbers on my page every 1/4 second or so. So the change is visible to the user. I'm using setTimeout and all my calculations are occurring correctly but without any delay. Here's the code:
for(var i = 0; i < 10; i++)
{
setTimeout(addNum(i),250);
}
I've also tried capturing the return value:
for(var i = 0; i < 10; i++)
{
var t = setTimeout(addNum(i),250);
}
I've also tried using function syntax as part of the setTimeout params:
for(var i = 0; i < 10; i++)
{
var t = setTimeout(function(){array[j].innerHTML + 1},250);
}
I've also tried putting code in a string & the function call in a string. I can't ever get it to delay. Help Please!
How about:
var i=0;
function adder() {
if(i>=10) {return;}
addNum(i++);
setTimeout(adder,250);
}
adder();
When you did setTimeout(addNum(i),250); you executed the function straight away (function name followed by () will execute it right away and pass the return value to the timeout to be executed 1/4 second later). So in a loop that would just execute all 10 immediately. Which is what you saw.
Capturing the return value var t = setTimeout(...); is helpful, but not in your use case; the value is the timer id number, used for cancelling the timeout.
Not sure what your last attempt is, although presumably it's the function body of your addNum routine, so the same logic applies as above.
Perhaps instead, since you're running the same method multiple times, you should use the setInterval method instead? Here's an example of how you might do that.
Try setTimeout("addNum(" + i + ")", 250); the reason its not working is because its evaluating the parameter and executing it and changing it to something like setTimeout(result of addNum(i), 250);

Categories

Resources