Pure JS equivalent of jQuery .animate() - javascript

What is the equivalent of the following jQuery animate in pure JavaScript?
function animate(element, position, speed) {
$(element).animate({
"top": position
}, speed);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

You can acheive complex animations with pure javascript by using setTimeout and setInterval methods.
Please check here.
Here is the key part of moving an element:
function move(elem) {
var left = 0
function frame() {
left++ // update parameters
elem.style.left = left + 'px' // show frame
if (left == 100) // check finish condition
clearInterval(id)
}
var id = setInterval(frame, 10) // draw every 10ms
}

This version uses vanilla javascript .animate() function, which is better or more performant
than requestAnimation frame. & it is also the proper alternative to JQuerys .animate().
you can play around with the iterations, timing functions & fill method aswell as daisy chain it with other animations
document.getElementById("Elem");
Elem.style.position = "absolute";
Elem.animate({
top: ['8px', '280px']
},{ duration: 1760, // number in ms [this would be equiv of your speed].
easing: 'ease-in-out',
iterations: 1, // infinity or a number.
// fill: ''
});
I believe the setTimeout & setInterval functions both use the unsafe eval() function under the hood, but not 100% sure about that, just remember reading an article about it...
Don't Quote me on that! just research it,
but the code I wrote out has been tested to be working..
hope this helps someone...

The setInterval() method is too heavy for the browser, so it's better to use requestAnimationFrame() for animations. The following code is an example of using this method.
let _btns = document.querySelectorAll('.btn'),
_start = null;
let _loop = function (timestamp, duration, position, wrap) {
if (!_start) _start = timestamp;
let progress = (timestamp - _start) / duration;
wrap.style.left = progress * position + 'px'
if ( progress < 1 ) {
window.requestAnimationFrame( function (timestamp){
_loop(timestamp,duration,position,wrap);
} );
} else {
_start = null;
}
},
_animation = function () {
const wrap = document.querySelector('.logo-2'),
position = 300, // 300 pixels
duration = 500; // 500 milliseconds
_loop(0,duration,position,wrap);
},
_addEvents = function () {
[].forEach.call(_btns,function(el){
el.addEventListener('click', function (e) {
_animation();
})
});
},
_init = function() {
_addEvents();
};
_init();

Element.animate() function seems to be very simple and useful. But there are for now a lot of compatibility issues. You can read about it:
https://developer.mozilla.org/en-US/docs/Web/API/Element/animate
I would recommend to get used to requestAnmationFrame. It's compatible with all browsers and it is very powerful:
https://javascript.info/js-animation

Related

jQuery within a JavaScript for loop not working

I've trying to run jQuery inside a for loop like this:
var frameNumber = 15; // How many frames are in your animation
for(var i = 1; i < frameNumber + 1; i++){
var flipDelay = i * 100;
$('.flipbook').delay(flipDelay).addClass('flipbook-' + i);
}
I'm trying to get the equivalent of this but with more flexibility so I can change how many frames I use:
$(function(){
setTimeout(function(){ $(".flipbook").addClass("flipbook-1") }, 100 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-2") }, 200 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-3") }, 300 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-4") }, 400 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-5") }, 500 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-6") }, 600 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-7") }, 700 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-8") }, 800 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-9") }, 900 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-10") }, 1000 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-11") }, 1100 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-12") }, 1200 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-13") }, 1300 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-14") }, 1400 );
setTimeout(function(){ $(".flipbook").addClass("flipbook-15") }, 1500 );
});
The classes are defined in a CSS stylesheet and each class has a different background applied to it. I just need jQuery/JS to loop through them until the last frame is reached.
EDIT: It's not the flipbook-01 vs flipbook-1 that's preventing me from getting this to work. Sorry about that confusion.
EDIT: I need my div to look like this after the function has ran:
<div class="flipbook flipbook-1 flipbook-2 flipbook-3 flipbook-4 ... flipbook-15"></div>
You are misusing the delay() function. jQuery will only queue up effects out-of-the-box (e.g. fadeIn() or slideUp()), and not things like show(), hide(), or addClass().
Example of non-working delay() with addClass(): http://jsbin.com/hayay/4/
Instead, you should just use setTimeout like others have mentioned. I'd recommend a recursive approach, though, instead of a for loop:
var frameNumber = 15;
showFrame(1);
function showFrame(i) {
$('.flipbook').addClass('flipbook-' + i);
if (i < frameNumber) {
setTimeout(function() { showFrame(i+1); }, 100);
}
}
Try this:
addClass('flipbook-' + (i<10?("0"+i):i));
This will add the missing zero for i<10.
And about the delay - it would not work with addClass. You should stick with the setTimeout option.
Fiddle Demo
var frameNumber = 15;
for (var i = 1; i < frameNumber + 1; i++) {
(function (x) {
setTimeout(function () {
$('.flipbook').addClass('flipbook-' + x);
console.log(x);
}, x * 100)
})(i)
}
As has been said already, the addClass() method is an immediate method invocation. It does not, by default, go through the animation queue which is required in order to work with .delay().
Here's an interesting solution I came up with that allows you to make any jQuery method work via the animation queue and thus you can sequence methods via the built-in animation queue, can use .delay() to control timing, can intersperse with other animations, etc...
// generic method to allow you to run any jQuery method through the animation queue
// so they will be serialized with other asynchronous animation methods
// such as .delay(), .animate(), .slideUp(), etc...
jQuery.fn.queueMethod = function(methodName /* arg1, arg2, ... */) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
self.queue(function(next) {
self[methodName].apply(self, args);
next();
})
return this;
}
var frameNumber = 15; // How many frames are in your animation
for (var i = 1; i <= frameNumber; i++) {
$('.flipbook').delay(100).queueMethod("addClass", "flipbook-" + i);
}
This will run each addClass method, 100ms apart.
Several working examples of slightly different ways of doing it: http://jsfiddle.net/jfriend00/38BbM/

Accessing 'this' variable in function passed to setInterval/setTimeout

I'm creating a simple game with a character that can jump, move right, and move left.
I'm having trouble with the jump function, which uses a setInterval.
Here is the function:
jumpUp: function (speed) {
setInterval(function () {
this.yPos += 10;
this.playerElement.css("top", '-=' + 10);
alert("dude, im not moving....so stop trying"); //for some reson, this line works and other dont.
}, 100);
}
I should add that the code works without the setInterval. I really don't have any idea why it's not working when I add the setInterval.
My questions:
What is stopping this code from running?
Is setInterval a good way to make a character look like it jumping and landing? Or should i use different method?
EDIT 1:
fiddle
The problem is your use of this. When the function you pass to setInterval is called, this will be the global object (window in browsers). You need to preserve the this value from when you call setInterval. One way of doing this is to store the value of this into a variable, which will then be closed over by the anonymous function (which is a closure):
jumpUp: function (speed) {
var self = this;
setInterval(function () {
self.yPos += 10;
self.playerElement.css("top", '-=' + 10);
}, 100);
}
EDIT:
To answer your second question, a better approach to animating a sprite (like your character) is to store the character's velocity, and then have a single game loop that will calculate the next position of the sprite based on that information. A very simple example would look like:
// Somewhere else in the code:
function tick() {
// move player by player.velocity.x and player.velocity.y
// code to decelerate player gradually, stop player when they hit a wall/floor, etc...
// A very simple example:
if (player.velocity.y > 0) {
player.velocity.y -= 1
}
// call next tick() (setTimeout, or preferably requestAnimationFrame)
}
// In the player code:
velocity: {x: 0, y: 0},
jumpUp: function () {
this.velocity.y -= 10;
},
moveRight: function () {
this.velocity.x += 10;
}
As darma and danronmoon pointed out, you have a scoping problem with this.
Try the following code:
jumpUp: function (speed) {
var that = this;
setInterval(function () {
that.yPos += 10;
that.playerElement.css("top", '-=' + 10);
}, 100);
}
I added a variable, that, to maintain the reference to whatever this is supposed to be.
In addition to your closure problem, this is probably going to cause choppy jumping.
Here's another pattern that watches the clock to see how much time has elapsed between each call to the function (setInterval is not consistent):
jumpUp: function (speed) // speed in pixels per second
{
var last = +new Date();
var c = this;
var jumptick = function ()
{
var interval = +new Date() - last;
c.yPos += speed * interval;
c.playerElement.css("top", c.yPos);
if (/* condition for reaching maximum height */) speed = -speed;
if (! /* condition for reaching ground */) setTimeout(jumptick);
// could add an interval but this will lead to fastest frame rate
};
jumptick();
}
Setinterval is not a good way to achieve this because it will use a lot a ressources.
You also need to consider the framerate when you are moving your character, otherwise he will move fast on a fast machine/browser and slow on a slow machine.
A good method is using the requestAnimationFrame method. You can find a javascript file on google that will make it crossbrowser compatible.
Then, everytime your function is called, you will need to check the time elapsed between to frame and move your sprites accordingly. It's more work but that way, your game will run at the same pace on any machine.

JavaScript animation is stuck, and will show only end result

I am trying to fiddle around and create a little fadeOut function in JavaScript,
This is what I came up with:
function fadeOut(id, time){
var elem = document.getElementById(id);
elem.style.opacity = 1;
var opc = 1;
while(opc >= (1/time)){
opc -= (1/time);
console.log(opc);
elem.style.opacity = opc;
}
elem.style.opacity = 0;
}
But this will not show the div's opacity in "real-time" but rather the end result, which is opacity = 0;
I've tested it out here:
fadeOut("hello",10000);
document.getElementById("hello").style.opacity = 1;
fadeOut("hello",10000);
document.getElementById("hello").style.opacity = 1;
fadeOut("hello",10000);
document.getElementById("hello").style.opacity = 1;
It would take it a long time to calculate and only when it finishes it will dump the result,
not showing it seamlessly, while calculating,
How can I resolve this?
You need to set timers, as, until your function is done and the event handler can run, the UI won't be updated.
It is because you are not 'yielding` the thread so as to allow the browser to apply the changes.
Use setTimeout(..) instead like this:
function fadeOut(id, time){
var elem = document.getElementById(id);
if(opc >= (1/time)){
opc -= (1/time);
console.log(opc);
elem.style.opacity = opc;
setTimeout(function(){fadeOut(id,time);}, 100);
}
else
elem.style.opacity = 0;
}
Not really a great code but it gives you the idea.
May be you could use jQuery library. In that case, you will use fadeOut
one potential causing this problem can be bubbling of event.try to use event.stopPropagation() to prevent the event (in case that you are using the FadeOut function in response to an event) from bubbling up.
the code is
function StopBubble(e)
{
if (!e)
e = window.event;
e.cancelBubble = true; /* Microsoft */
if (e.stopPropagation)
e.stopPropagation(); /* W3C */
}
You have to specify a delay between every modification you make to the DOM in order to see the effect. There is no option in javascript to add a pause feature, so you have to rely on timers. There are two ways to achieve this. Most commonly used approach is setInterval(fn,sec). But setInterval is bad, because it executes the callback every interval of time specified regardless the previous execution is complete or not.
I personally suggest using a library rather than re-inventing the wheel. But its always good to have a basic understanding what the libraries do behind the scene.
Below is a sample snippet to achieve this without setInterval.
Note: This is just for demonstration. Not cross browser compatible. Extend it for reusability and cross broswer compatibility.
<div id="mydiv"></div>
function fadeOut() {
var _timeOut,
_proceed = true,
_elm = document.getElementById( "mydiv" ),
_opacity;
function addOpacity() {
// _elm.style.opacity will return empty string
// see https://developer.mozilla.org/en-US/docs/DOM/window.getComputedStyle
// getComputedStyle() not cross broswer
_opacity = +window.getComputedStyle( _elm, null ).getPropertyValue( "opacity" );
if( _opacity > 0.1 ) {
// a simple algorithm for basic animation
_elm.style.opacity = _opacity / 2;
} else {
_elm.style.opacity = 0;
_proceed = false;
}
}
(function animate() {
if( !_proceed ) {
clearTimeout( _timeOut );
return;
}
addOpacity();
// unlike setInterval(), this approach waits for addOpacity() to complete before its executed again
_timeOut = setTimeout( animate, 1000 );
}());
}
Why not to use a JavaScript library like jQuery? Because some JavaScript code is not compatible with different sorts of browsers.
In jQuery you can use this:
$(".id").css("background","blue").fadeOut("slow");

Increase the value of a number in an element every x milliseconds

So I have this simple HTML:
<span id="badge">0</span>
I want the number 0 to increase by 1 every x milliseconds. How do I do that with Javascript (with or without jQuery)?
Thanks a bunch - I'm new to this :)
You should do this:
<script>
var $badge = $('#badge'); // cache
setInterval(function () {
var value = parseInt($badge.html());
value++;
$badge.html(value);
}, 1000);
</script>
Assuming 1000 milliseconds.
function increment() {
document.getElementById("badge").value = Number(document.getElementById("badge").value) + 1;
setTimeout("increment()",3000);
}
increment()
Every of the answers I see here has the same drawbacks:
performance issue because of selecting the DOM element every ms cycle. Especially when using a heavy library as jQuery.
setInterval() is probably the tool designed for that functionality, but not reliable. It can diverge a lot from the real time, especially when using a small interval. If you want exactly x executions per second, you may google for some timing libraries.
I would code:
var textNode = document.getElementById(badge).firstChild;
var start = Date.now();
window.setInterval(function update() {
textNode.data = Math.round((new Date()-start)/ms);
}, ms);
If you don't want to start at 0, it will be trivial to add an offset (determined before the loop begins), eg.
var start = Date.now() - (textNode.data * ms || 0); // NaN catching, implicit number cast
Something like this?
var millisecs = 10;
setInterval(function() {
var $badge = $('#badge');
$badge.text(parseInt($badge.text())++);
}, millisecs);
http://jsfiddle.net/iambriansreed/MPP8n/3/
Check this http://www.w3schools.com/js/js_timing.asp
A little more about timers:
// setting a variable for your timer will allow you the ability to "turn it on and off"
var tmrChangeI;
// setTimeout is a function to initiate a function once after given amount of milisecs
// whereas setInterval will continue a function until cancled every so many milisecs
// the following wil "turn on" your timer
tmrChangeI = setInterval(function() {
var $badge = $('#badge');
$badge.html($badge.html() + 1);
}, 500); // 500 will = every half of a second
// to "turn off" timer
clearInterval(tmrChangeI);
// or set a bool to test and use timeout to repeat till bool is false
var tmrBool = true;
// establish function to execute
function tmrFunc() {
var $badge = $('#badge');
$badge.html($badge.html() + 1);
if (tmrBool) tmrChangeI = setTimeout(function() { tmrFunc(); }, 500); // 500 will = every half of a second
};
// execute function, begin timer
tmrChangeI = setTimeout(function() { tmrFunc(); }, 500);
// clear via bool allowing one more execution
tmrBool = false;
// clear by force possibly stoping next execution,
// tho in this manner it may be too late if timer is very short
// and maybe overriden by bool still being true, this is not safest
// but is example of how to use setTimeout
clearTimeout(tmrChangeI);
You can use setInterval.
var $badge = $('#badge');
setInterval(function () {
$badge.html(parseInt($badge.html()) + 1);
}, 1);​//Specify the milliseconds here, right it will update the value every 1 millisecond
Working demo - http://jsfiddle.net/8FMZh/
You could create a Jquery plugin, so you can reuse whenever you need.
$.fn.increment= function(options) {
var $this = $(this);
var coef = options.coef;
var speed = options.speed;
var value = 0;
setInterval(function(){
value = value + coef ;
$this.html(value);
}, speed);
};
And in your main javascript file :
$("#badge").increment({coef: 1, speed:1000});
working demo : http://jsfiddle.net/8FMZh/102/

JavaScript timeline animation (setTimeout accuracy issues)

I began writing a simple animation class in JS, which utilizes Zepto.js animation capabilities but adds timeline-like capability to it.
The timeline itself is a simple array, that executes functions embedded in it when it's play() function is called:
play : function(callback){
for(var i=0; i<Animator.timeline.buffer.length; i++){
Animator.timeline.buffer[i].animation();
}
if(callback){
callback();
}
}
The setTimeout goes directly in the animation:
alpha : function(parameters, callback, delay){
var target = parameters.target;
var duration = parameters.duration;
var easing = parameters.easing;
var value = parameters.value;
if(delay){
setTimeout(function(){run();},delay*1000);
} else {
run();
}
function run(){
$(target).anim({opacity:value},duration,easing);
if(callback){
callback();
}
}
}
So basically, the timeline just runs the setTimeout-ed functions which are placed in it's buffer array.
This approach works (almost) as intended with WebKit animations, but i've run into a few problems when doing image sequences (animations using setInterval which change the image's src). As JS timers don't guarantee execution in their appointed time, sometimes animations run late, likely because of the embedded setInterval inside them.
Any ideas on how to solve this? I am aware that embedding all animations as callbacks inside one another would solve much of the issues, but i don't really know how to do that from inside the timeline loop. Also, it would quickly become an unreadable mess of callbacks if I call all functions in a direct manner (without using the timeline).
For reference, the sequence function of my animator class:
sequence : function(parameters, callback, delay){
var target = parameters.target;
var path = parameters.path;
var baseName = parameters.baseName;
var digits = parameters.digits;
var extension = parameters.extension;
var frames = parameters.frames;
var loop = parameters.loop;
if(parameters.interval){
var _interval = parameters.interval
} else {
var _interval = 15;
}
var currentFrame = 0;
var imageUrl = '';
var fileName = baseName;
for(var i=0; i<=digits; i++){
fileName+='0';
}
if(delay){
setTimeout(function(){runSequence();},delay*1000);
} else {
runSequence();
}
function runSequence(){
var interval = setInterval(function(){
if(currentFrame >= frames){
currentFrame = 0;
if(!loop) {
clearInterval(interval);
if(callback){
callback();
}
}
} else {
imageUrl = path+fileName.substring(0, fileName.length-currentFrame.toString().length)+currentFrame+"."+extension;
$(target).attr('src',imageUrl);
currentFrame++;
}
},_interval);
}
}
Also, a sample of an animation created by using this class:
Animator.timeline.append(function(){
Animator.alpha({'target':'#logo', 'value':1, 'duration':1, 'easing':'ease-out' });
});
Animator.timeline.append(function(){
Animator.sequence({'target':'#sequence', 'path':'images/sequences/index/', 'baseName':'nr1_', 'digits':3, 'extension':'png', 'frames':50},'',1.5);
});
Animator.timeline.append(function(){
Animator.scale({'target':'#text', 'width':.5, 'height':.15, 'duration':1, 'easing':'ease-in-out'},'',3.2);
});
Animator.timeline.append(function(){
Animator.alpha({'target':'#link', 'value':1, 'duration':1,'easing':'ease-out'},'',4.7);
});
Animator.timeline.play();
As an additional note, I was aiming to create something similar to GreenSock in AS3, if that helps.
Thanks.
Accurate setInterval can be simulated by compensating for the time it takes to execute every iteration, maybe this gist I wrote can help you:
https://gist.github.com/1185904

Categories