I want to learn the JavaScript as well. and looking the various jQuery functions to their equivalent JavaScript.
I want to convert this jQuery function to its equivalent JavaScript functions? How can I do this?
$('.sample').stop().animate({
left: '-102px'
}, 1000);
In a nutshell, a jQuery animation is a recurring timer that changes one or more CSS properties on each timer tick.
In addition jQuery implements a tweening algorithm that calculates on each timer tick, whether the animation is ahead or behind schedule and adjusts so that the animation always completes in the exact time specified.
In addition jQuery implements an animation queue so that multiple animations can be chained together (start the next one when the previous one completes).
In addition jQuery supports easing functions which allow you to specify a non-linear animation that varies it's speed during the time according to a specific algorithm.
FYI, the jQuery javascript source code is fully available if you want more details. The core of the work is in a local function called doAnimation(), though much of the work is done in functions called step and update which can be found with the definition of jQuery.fx.prototype.
Here's another answer that shows a simple fade animation in pure javascript.
Here's a general tutorial on animation.
You can see a discussion of using a timer for an animation here.
You can see a discussion of tweening here.
Here's a javascript version of your specific animation:
// node is the DOM element to animate
// prop is the CSS property name to animate
// end is the CSS value to animate to (only supports px units)
// duration is the time of the animation in ms
// fn is an optional callback when the animation is done
// fn is called like this fn(node, arg)
// arg is an optional argument that is passed to the callback
// context is an optional argument that is the this pointer for the function
function animate(node, prop, end, duration, fn, arg, context) {
var stepTime = 20;
var startTime = new Date().getTime();
var start = parseInt(getComputedStyle(node).getPropertyValue(prop), 10);
if (typeof end === "string") {
end = parseInt(end, 10);
}
function step() {
// calc how much time has elapsed
var nextValue, done, portionComplete;
var timeRunning = new Date().getTime() - startTime;
if (timeRunning >= duration) {
nextValue = end;
done = true;
} else {
portionComplete = timeRunning / duration;
nextValue = ((end - start) * portionComplete) + start;
done = false;
}
// set the next value
node.style[prop] = nextValue + "px";
if (!done) {
setTimeout(step, stepTime);
} else {
if (fn) {
context = context || window;
fn.call(context, node, arg);
}
}
}
// start the animation
step();
}
Working demo: http://jsfiddle.net/jfriend00/Mc3xD/
For simplicity of understanding, this doesn't implement the .stop() part of your jQuery example as that would need a separate data structure to keep track of each animation timer running on a given object which can be seen in a more involved version that supports stop(): http://jsfiddle.net/jfriend00/mp4Yq/.
you remind me when i started learning js , and then was very happy to find jquery , anyway i dont know why you are doing vice versa , but to answer you question
Animation in javascript can be used using setInterval function with changing the top , left .. etc attributes over a very small amount of time ( usually 24 milli secconds ) which to the human eye are like a stream and not like seperate shots of positions , also you may consider using pure css3 , however a function like this may be used
var position,ratio,time,mytimer;
document.getElementById("hi").style.left=0;
function animate_left(position,time)
{
ratio=position/time;
if(parseInt(document.getElementById("hi").style.left)<=position)
{
document.getElementById("hi").style.left=(parseInt(document.getElementById("hi").style.left)+ratio*100).toString()+"px"
}
else
{
clearInterval(mytimer)
}
}
function animate(value1,value2)
{
mytimer=setInterval(function(){animate_left(value1,value2)},10) //where 10 is minimum smooth animation factor for human eye
}
animate(600,1000);
http://jsfiddle.net/prollygeek/er67f/6/
Related
I have an ASCII art "pathfinding visualizer" which I am modeling off of a popular one seen here. The ASCII art displays a n by m size board with n*m number of nodes on it.
My current goal is to slowly change the appearance of the text on the user-facing board, character by character, until the "animation" is finished. I intend to animate both the "scanning" of the nodes by the pathfinding algorithm and the shortest path from the start node to the end node. The animation, which is just changing text in a series of divs, should take a few seconds. I also plan to add a CSS animation with color or something.
Basically the user ends up seeing something like this, where * is the start node, x is the end node, and + indicates the path:
....
..*.
..+.
.++.
.x..
.... (. represents an empty space)
After doing some research on both setTimeout, promises and other options, I can tell you:
JavaScript really isn't designed to allow someone to delay code execution in the browser.
I've found lots of ways to freeze the browser. I also tried to set a series of promises set to resolve after setTimeout(resolve, milliseconds) occurs, where the milliseconds steadily increases (see below code). My expectation was that numOfAnimationsForPath number of promises would be set and trigger a change in the appearance of the board when each one resolved (forming the appearance of a path). But, they all seem to resolve instantly (?) as I see the path show as soon as I click the "animate" button
const shortestPathAndScanningOrder = dijkstras(grid);
promisesRendering(1000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])
function promisesRendering(animationDelay, algoPath, scanTargets) {
const numOfAnimationsForScanning = scanTargets.length;
const numOfAnimationsForPath = algoPath.length;
for (let i = 1; i < numOfAnimationsForPath - 1; i++) {
const xCoordinate = algoPath[i][0];
const yCoordinate = algoPath[i][1];
renderAfterDelay(animationDelay * i).then(renderNode(xCoordinate, yCoordinate, "path"))
}
}
function renderAfterDelay(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
function renderNode(x, y, type) {
if (type === "scan") {
const targetDiv = getLocationByCoordinates(x, y);
targetDiv.innerHTML = VISITED_NODE;
} else if (type === "path") {
const targetDiv = getLocationByCoordinates(x, y);
targetDiv.innerHTML = SHORTEST_PATH_NODE;
} else {
throw "passed incorrect parameter to 'type' argument"
}
}
In my other attempt, I tried to generate pathLength number of setTimeouts as in:
const shortestPathAndScanningOrder = dijkstras(grid);
renderByTimer(10000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])
function renderByTimer(animationDelay, algoPath, scanTargets) {
const numOfAnimations = algoPath.length;
for (let i = 1; i < numOfAnimations - 1; i++) {
const xCoordinate = algoPath[i][0];
const yCoordinate = algoPath[i][1];
setTimeout(i * animationDelay, updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate))
}
}
...but this also resulted in the path being "animated" instantly instead of over a few seconds as I want it to be.
I believe what I want is possible because the Pathfinding Visualizer linked at the start of the post animates its board slowly, but I cannot figure out how to do it with text.
So basically:
If anyone knows how I can convince my browser to send an increasing delay value a series of function executions, I'm all ears...
And if you think it can't be done, I'd like to know that too in the comments, just so I know I have to choose an alternative to changing the text slowly.
edit: a friend tells me setTimeout should be able to do it... I'll update this w/ a solution if I figure it out
Edit2: Here is the modified version of #torbinsky's code that ended up doing the job for me...
function renderByTimer(algoPath, scanTargets) {
const numOfAnimations = algoPath.length - 1; // - 1 because we don't wanna animate the TARGET_NODE at the end
let frameNum = 1;
// Renders the current frame and schedules the next frame
// This repeats until we have exhausted all frames
function renderIn() {
if (frameNum >= numOfAnimations) {
// end recursion
console.log("Done!")
return
}
// Immediately render the current frame
const xCoordinate = algoPath[frameNum][0];
const yCoordinate = algoPath[frameNum][1];
frameNum = frameNum + 1;
updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate);
// Schedule the next frame for rendering
setTimeout(function () {
renderIn(1000)
}, 1000);
}
// Render first frame
renderIn()
}
Thanks #torbinsky!
This should absolutely be doable using setTimeout. Probably the issue is that you are immediately registering 10,000 timeouts. The longer your path, the worse this approach becomes.
So instead of scheduling all updates right away, you should use a recursive algorithm where each "frame" schedules the timeout for the next frame. Something like this:
const shortestPathAndScanningOrder = dijkstras(grid);
renderByTimer(10000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])
function renderByTimer(animationDelay, algoPath, scanTargets) {
const numOfAnimations = algoPath.length;
// Renders the current frame and schedules the next frame
// This repeats until we have exhausted all frames
function renderIn(msToNextFrame, frameNum){
if(frameNum >= numOfAnimations){
// end recursion
return
}
// Immediately render the current frame
const xCoordinate = algoPath[frameNum][0];
const yCoordinate = algoPath[frameNum][1];
updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate);
// Schedule the next frame for rendering
setTimeout(msToNextFrame, function(){
renderIn(msToNextFrame, frameNum + 1)
});
}
// Render first frame
renderIn(1000, 1)
}
Note: I wrote this code in the StackOverflow code snipppet. So I was not able to test it as I did not have the rest of your code to fully run this. Treat it more like pseudo-code even though it probably works ;)
In any case, the approach I've used is to only have 1 timeout scheduled at any given time. This way you don't overload the browser with 1000's of timeouts scheduled at the same time. This approach will support very long paths!
This is a general animation technique and not particularly unique to ASCII art except that old-school ASCII art is rendered one (slow) character at a time instead of one fast pixel frame at a time. (I'm old enough to remember watching ASCII "movies" stream across hard-wired Gandalf modems at 9600bps to a z19 terminal from the local mainframe...everything old is new again! :) ).
Anyhow, queueing up a bunch of setTimeouts is not really the best plan IMO. What you should be doing, instead, is queueing up the next event with either window.requestAnimationFrame or setTimeout. I recommend rAF because it doesn't trigger when the browser tab is not showing.
Next, once you're in the event, you look at the clock delta (use a snapshot of performance.now()) to figure what should have been drawn between "now" and the last time your function ran. Then update the display, and trigger the next event.
This will yield a smooth animation that will play nicely with your system resources.
In examples I see two different ways how to handle animations using requestAnimationFrame():
The first one using setTimeout()
const e = document.getElementById('e');
let count = 0;
function move(timestamp) {
e.style.left = ++count + 'px';
setTimeout(f=>{
requestAnimationFrame(move);
}, 200);
};
requestAnimationFrame(move);
Try it in this jsfiddle.
The second one by calulating the ellapsed time yourself
const e = document.getElementById('e');
let count = 0;
let past = null;
function move(timestamp) {
if (past !== null && timestamp - past < 200) {
requestAnimationFrame(move);
return;
}
past = timestamp;
e.style.left = ++count + 'px';
requestAnimationFrame(move);
};
requestAnimationFrame(move);
Try it in this jsfiddle.
Now my question is: Which one performs better?
My guess is, that if an interval is applicable, it will perform better. There aren't so many logical expressions to evaluate and so many calculations to do. But that's just a guess. So, are their any benefits of one way over the other?
You should do the first one. However you shouldn't assume it will be exactly 200ms. If you need to know exactly how many milliseconds it's been, you must calculate it every time like in your second example, but keep using the structure of your first example.
The problem with the second example is that it calculates the new frames just as often as the first example, but it runs the function just to check that it doesn't need to run the function every other time it runs. Lots of wasted computation there.
You need to calculate the time elapsed (if you need accuracy) because the setTimeout combined with the requestAnimationFrame will not be guaranteed to run in 200ms. The requestAnimationFrame in particular will add time onto that if it feels it needs to before it's ready to give you permission to animate (which is basically what requestAnimationFrame is -- it's giving you permission to animate, saying 'I'm ready now').
I've been trying to implement a global pause for all tweens. If in my animate loop I just don't update TWEEN, it stops but then after I unpause jumps ahead to the position it should have as if I never paused.
TWEEN.update(time);
To tackle this I want to have a separate timeline as an argument of update function. I've tried to create a different value and update it independently, but then the tween won't start at all.
So here's the thing that ended up working, wondering if there is a more elegant way to do it using Tween's internal variables like elapsed.
var delta = 0;
var tmp = 0;
var recorded = false;
function animate(timestamp) {
if(paused){
if(!recorded) {
tmp=timestamp;
recorded=true;
}
}
else {
if(recorded){
delta += timestamp-tmp;
recorded=false;
}
TWEEN.update(timestamp-delta);
}
Keep the time in TWEEN.update(time) will pause the tween. #Eugene was right.
cache the pause times;
TWEEN.update(timenow - pause times).
For more detail, see:
https://github.com/tweenjs/tween.js/issues/341#issuecomment-447653541
Today i have written little code, that can help you with pause for your tweens.
//First you need set some id for your tween animation.
var myTween = createjs.Tween.get(your_object).to({your_animation_params} ...);
//When you need to paused your tween by some event
//just get this object and set it on the same coordinates(by example),
//with time animation = 0 to have accses to use Pause method
c.Tween.get(your_object).to({x:your_object.x}, 0).pause(myTween);
//Later you can just play(unpause) it again using same trick
c.Tween.get(your_object).to({x:your_object.x}, 0).play(myTween);
I have this function :
d3.timer(function(elapsed) {
//console.log('change mod pos ...');
// Because the node should remain fixed, the previous position (.px, .py)
// needs to be set to the same value as the new position (.x, .y). This way
// the node will not have any inherent movement.
move.x = move.px = interpolateX(elapsed / duration);
move.y = move.py = interpolateY(elapsed / duration);
// Re-calculate the force layout. This will also invoke tick()
// which will take care of the rendering.
force.start();
// Terminate the timer when the desired duration has elapsed.
return elapsed >= duration;
});
It can be found in this fiddle : http://jsfiddle.net/smqsusdw/1/
Notice the parameter 'elapsed'. Where does this function get that value from ? Does it get it from the 'd3.timer' ?
Obviously I don't know the ins and outs of javascript so i think im missing something totally obvious here.
Yes you're correct in that assumption. When the d3.timer function calls the anonymous function (function(elapsed) {...}), it will pass something to that function as the first parameter. I'm not exactly sure what d3.timer does without digging into the code but it does something like this:
d3.timer = function(anonFunc) {
var elapsed = 0;
// Some code that does something to elapsed
anonFunc(elapsed);
}
You can find out what is being passed back to the anonymous function by using console.log(elapsed); at the beginning of the function. You can see what d3.timer does by typing in d3.timer in your browser's Javascript console.
timer(callback[, delay[, time]])
The callback is passed two arguments each time it is invoked: the elapsed time since the timer became active, and the current time. The latter is useful for precise scheduling of secondary timers.
https://github.com/d3/d3-timer
D3 basically uses setInterval and setTimeout to call your callback after the delay ms with the following signature:
timer(function(elapsed, time) {
console.log(elapsed, time);
return elapsed > 200;
}, 150);
You can also use the Arguments object to see what parameters a function has been passed.
var x = function(){ console.log(arguments) };
x(1,2)
> [1, 2]
I'm trying to write a simple music-sequencer in Javascript.
Sounds will be played by SoundManager2
I quickly realised that setTimeout and setInterval are useless for this kind of timing. Their accuracy is simply not good enough.
So what I am trying instead is this:
Create a queue of sound-events (a 2d array [ note, time ] )
Process the queue in a while loop
In pseudo code it could look like this:
// The queue of time/note values (millisecs)
var q = [[0, C], [250, D], [500, E]]
var begin = (new Date).getTime()
while(q.length > 0 ){
var now = (new Date).getTime()
var eventTime = q[0][0] + begin
if( now >= eventTime){
playNote(q[0][1]) // Play the note
q.shift() // Now that the note has been played, discard it.
}
}
Through debugging I've found that this method seems precise enough (the playNote call is made at the exact time it's supposed to).
However while the 'sequence' is playing, all other Javascript (including the bit that actually MAKES the sound) is suspended which is hardly a surprise.
This means that I have silence for as long time it takes to run the sequence and then ALL of the calls to playNote are executed.
I've tried isolating the while-loop in it's own function and then call it through setTimeout, in the hopes that this would create a background thread executing it's own thing (namely playing the sequence) without halting all other JS execution. That doesn't work
I've also tried calling the playNote function using setTimeout, in the hopes that while indeed the UI is frozen at least the sequence is playing correctly, but that doesn't work either.
I've also tried a combination, but as you probably already guessed, that doesn't work either.
So my question is:
How can I have a queue of events processed with precise timing, while NOT shutting down all other code-execution during the run
I don't know if there is a solution for older browers, but web workers are intented for doing parrallel execution in JavaScript.
They are supported in recent versions of Chrome and Firefox, I don't know about other browsers.
If you replace your while loop with a recursive function (it doesn't have to be self-executing) the browser wont freeze.
(function foo (q) {
var now = (new Date).getTime(),
eventTime = q[0][0] + begin;
if (q.length > 0) {
if (now >= eventTime) {
playNote(q[0][1]); // Play the note
q.shift(); // Discard played note
}
return foo(q); // Pass q into the next round
} else {
return; // End the "loop"
}
}(q));
Edit: The problem with recursive calls + timer accuracy should be taken care of by setInterval:
var timer = setInterval(function foo () {
var now = (new Date).getTime(),
eventTime = q[0][0] + begin;
if (q.length > 0) {
if (now >= eventTime) {
playNote(q[0][1]); // Play the note
q.shift(); // Discard played note
}
} else {
clearInterval(timer); // End the "loop"
}
}, 10); // This is in ms (10 is just a random number)