javascript smooth animation from X,Y to X1,Y1 - javascript

I'd like to sloothly move an image (or an element) from its actual X, Y location to X1, Y1.
When the distance between X and X1 is equal to that between Y and Y1 its easy.
But what if the X difference is say 100px and Y diff is 273px?
Being new to Javascript, I don't want to re-invent the wheel!
Besides, since I'm learning, I do NOT want to use jQuery or the likes. I want pure javascript.
Please supply with simple script :-)

One solution:
function translate( elem, x, y ) {
var left = parseInt( css( elem, 'left' ), 10 ),
top = parseInt( css( elem, 'top' ), 10 ),
dx = left - x,
dy = top - y,
i = 1,
count = 20,
delay = 20;
function loop() {
if ( i >= count ) { return; }
i += 1;
elem.style.left = ( left - ( dx * i / count ) ).toFixed( 0 ) + 'px';
elem.style.top = ( top - ( dy * i / count ) ).toFixed( 0 ) + 'px';
setTimeout( loop, delay );
}
loop();
}
function css( element, property ) {
return window.getComputedStyle( element, null ).getPropertyValue( property );
}
Live demo: http://jsfiddle.net/qEVVT/1/

Doing smooth animation on systems with a variety of different capabilities (CPU, graphics power, other things going on on the computer) is not a trivial task. A proper implementation involves developing an effective "tweening" algorithm that can figure out adaptively (as the animation runs) what increments to be using in the animation in order to stay on schedule and be as smooth as possible.
The best way to do this is to stand on the shoulders of others and use what has been invented before. In this day and age, I would never try to write this myself from scratch. It's there to use in CSS3 transitions/animations, but those aren't yet supported everywhere. It's there to use or analyze in jQuery and YUI3. My first choice would be to use one of the frameworks that has a rich set of capabilities here. You don't have to use the framework for anything else, you can just use it for the animation if you want. YUI3 will even let you construct a library that has the least code in it possible for just what you want. jQuery isn't very big to start with.
If you're still dead set against using one of the libraries, then download the source to the relevant modules for each library and study how they do it. Build a sample app in each and step through how it works, setting breakpoints at interesting spots. That will be the best teacher and show you how to build an effective tweening algorithm that can adapt to the speed capabilities of the host computer.
To give you an idea of how a tweening algorithm works for a straight animation (with linear easing), you make an initial calculation of what you want your animation step value to be for the time you want the animation to run. This is probably just a guess as to what the system can support. You divide the number of steps that creates into the time the animation runs and you set a timer for that amount of time so you know when to run the next step. You then run one or two steps of the animation and you see how much time has actually elapsed. If the computer can't keep up with your step value, you will be behind schedule and you will have to adapt and pick a larger step.
Now, if you want to do something other than linear easing, there's obviously even more involved.
Firefox and Chrome have also implemented some new experiemental APIs to help with smooth animation. I discovered this myself when looking at the jQuery source because it uses it when it's available. In Chrome it's called webkitRequestAnimationFrame and you can read about it here in a Firefox blog post.

If you are targeting modern browsers, CSS transitions make the life easier (Example for firefox, for other browsers, change the -moz prefix):
<body>
<input type="button" onclick="move()" value="press" />
<div id="sq" style="position:absolute; top:50px; left:50px; height:50px; width:50px; background-color:Black; -moz-transition : all 0.8s ease 0s;" />
</body>
And the script
function move() {
var sq = document.getElementById("sq");
sq.style.left = "300px";
sq.style.top = "150px";
}

If I was going to write it from scratch I would start with something like this:
function linearEase(start, end, percent) {
return start + ((end - start) * percent);
}
function animateTo(settings) {
var elem = settings.element;
var ease = settings.ease;
var start = { left: elem.offsetLeft, top: elem.offsetTop };
var lastTime = new Date().getTime();
var timeLeft = settings.totalTime;
function update() {
var currentTime = new Date().getTime();
var elapsed = currentTime - lastTime;
timeLeft -= elapsed;
lastTime = currentTime;
var percentDone = 1 - timeLeft/settings.totalTime;
elem.style.top = ease(start.top, settings.top, percentDone) + "px" ;
elem.style.left = ease(start.left, settings.left, percentDone) + "px" ;
if(timeLeft > 0) {
setTimeout(update, 33);
}
}
update();
}
For example, to move a div to (50,50) over the next two seconds.
var elem = document.getElementById("animatable");
setTimeout(function() {
animateTo({
element: elem,
left: 50,
top: 50,
totalTime: 2000,
ease: linearEase
})
}, 10);
Which is a fairly standard pattern for doing this kind of stuff. Getting the element position and setting the style stuff could be better implemented for sure. But abstracting out an ease function will make your life a lot easier in the long run. I've provided a simple linear ease, but other more complicated easing algorithms would abide by that same interface.
Another thing to note is that timeouts and intervals are not guaranteed to run at a set time, so its usually best to set the total time that you want the transition to take to run, and then figure out how much time has elapsed since the last time you rendered.
Also if you are animating a bunch of elements at once, I would definitely refactor this to a single "render loop". calls to the animateTo would push workers into a queue of workers, but only have setTimeout loop that calculates the time elapsed then invokes each worker, so you don't have a bazillion timeout closures floating around.
Anyway, fiddle here

Related

How to animate a style property in plain vanilla javascript?

So for a while I've been moving away from jQuery, and in general just reducing my library use where-every possible to a) write leaner code, and b) really understand at a low level whats going on, particularly around the UI. While I've moved the majority of my UI animation to CSS3, theres often times when you need a little more control, but for a single tiny animation I'd prefer not to always have to pull in velocity.js or greensock etc.
Looking at you-might-not-need-jquery theres a fadeIn function they demostate that looks like this :
function fadeIn(el) {
el.style.opacity = 0;
var last = +new Date();
var tick = function() {
el.style.opacity = +el.style.opacity + (new Date() - last) / 400;
last = +new Date();
if (+el.style.opacity < 1) {
(window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16);
}
};
tick();
}
fadeIn(el);
I generally understand this function, but have a few questions on very specific items :
What does the + before new Date() and el.style.opacity on lines 3,5,6 & 8 indicate? is it something like += ?
On line 5, why the division by 400?
Is there anything inherently wrong with this recursive requestAnimationFrame technique for general quick animations?
If I understand the concept behind this pattern :
we set our starting position (force feeding) and time we're beginning,
we then update the style relevant to the amount of time thats passed,
until the final state is satisfied, call tick again, on the next animation frame.
Is this correct?
A unary + is a quick way to force a value to be interpreted as a number.
The division by 400 is the way that code sets the rate of fade-in. A bigger number would make the fade take longer, and a smaller number would make it faster. The number gives the number of milliseconds that will elapse (more or less) before the element is fully opaque.
It's not recursive. The reference to the function is passed to the timer mechanism (either setTimeout() or requestAnimationFrame()) but by the time the timer fires the original call will have exited.

How to make Vanilla Javascript animation less laggy?

I have a section of code which is supposed to move this image ( id = 'background' ), which I have downloaded locally, and is quite large. It is supposed to move when I hover over top of a certain div(s). This then changes the opacity CSS value, which in turn is detected by the js, which then makes the image move. The js code looks like this:
setInterval(function(){
var element = document.getElementById('background'),
style = window.getComputedStyle(element),
left = style.getPropertyValue('left');
left = left.replace(/px/g, "")
left = parseInt(left,10)
if(getOpacity('rightbar') == .5){
document.getElementById('background').style.left = left - 8 + 'px'
}
if(getOpacity('leftbar') == .5){
document.getElementById('background').style.left = left + 8 + 'px'
}
},10)
The getOpacity(idName) function looks like this:
function getOpacity(param){
var element = document.getElementById(param),
style = window.getComputedStyle(element),
opacity = style.getPropertyValue('opacity');
return opacity
}
So the problem is that, no matter what movement values or setInteveral time I use, the animation always makes out to be laggy. Is there a way to make this smooth with vanilla js, or better yet, to scrap the opacity detection and do it all with CSS?
It works fine when I put the above code in a fiddle, but when it actually runs full browser (on my personal chrome window), it looks like this.
Note: I am running this full browser window on a 4k monitor, is this just too much for chrome to handle?
1. Use requestAnimationFrame instead of setInterval
This signals the browser you want to do something before the next redraw. The callback you provide is executed exactly once per frame.
(In case this matters: requestAnimationFrame does not work in IE9 and below.)
2. Don't increase by a fixed value per frame, tween between values
Both when using requestAnimationFrame and using setInterval, the time difference between frames vary.
You can verify that yourself by using something like this in the developer toolbar:
var last = new Date();
function onFrame(){
var now = new Date();
console.log(new Date() - last);
last = now;
requestAnimationFrame(onFrame);
}
onFrame();
The developer console will output the frame times in ms, like this:
16
17
17
15
19
...
If you increase position (not so noticeable on e.g. opacity) by a fixed amount on vaying intervals, the animation will look jagged. So Instead of doing left = left + 8;, calculate at which position in the animation you are, based on the current time, something like this:
var myElement = ...;
var initialOpacity = 1.0;
var targetOpacity = 0.5;
var duration = 2000;
var startTime = new Date();
function animation() {
var delta = Math.min(1, (new Date() - startTime) / duration);
// delta is now a number in the range [0 ... 1]
myElement.style.opacity = initialOpacity + delta * (targetOpacity - initialOpacity);
if (delta < 1) requestAnimationFrame(animation);
}
requestAnimationFrame(animation);
Yes, this example tweens opacity and not position, but you get the idea - and your teacher can't claim you copy-pasted ;-)
3. Don't read and write back-and-forth between JS and CSS
Assuming the initial position of your image is not viewport-related (say, left: -10%), there is no need to read the position on every frame.
When your JavaScript is the only thing changing the left property, why read it from CSS? Save it in a variable and set it to CSS from your JavaScript.
var initialX = ComputeCssXPosition(myElement);
...
function animate() {
...
myElement.style.left = computedNewXPosition;
}
If you want to change the postition when the user hovers an element, use mouse events in your JS.
myElement.addEventListener('mouseover', function (ev) { ... });
myElement.addEventListener('mouseout', function (ev) { ... });
Alternative: Use CSS transitions
Already covered in the answer by Shomz.
The best approach would be to use requestAnimationFrame instead of setInterval and not to check for the style changes, but use mouseover listeners (too much communication: CSS->JS->CSS->JS...). See here: https://jsfiddle.net/yqpun3eb/4/
However, if you still want to use setInterval, you can simply put the transition CSS rule on your background element. For example:
#background {transition: left 0.2s linear}
That will smooth out all the value changes because CSS performs way better, so it should be fine even on the 4K screens. The problem was your changes can jump by 8 pixels.
Seems to work nice with 0.2s on my machine: https://jsfiddle.net/yqpun3eb/3/
Oh, and btw. you want good performance, but why are you raping the system with this:
function getOpacity(param){
var element = document.getElementById(param),
style = window.getComputedStyle(element),
opacity = style.getPropertyValue('opacity');
return opacity
}
This doesn't create extra variables (which you don't need anyway):
function getOpacity(param){
return window.getComputedStyle(document.getElementById(param))
.getPropertyValue('opacity');
}
Finally, here's a slightly optimized version using requestAnimationFrame (that's how I would do it + use listeners instead of reading the style values): https://jsfiddle.net/yqpun3eb/4/

Javascript time-based animation and requestAnimationFrame

I have been playing around with canvas and animation, with HTML5 games in mind specifically and quickly learnt the limitations of just using requestAnimationFrame (rFA) and have moved to time-based animations.
I want to maintain constant gameplay regardless of monitor refresh rate or FPS but am unsure how best to handle the animations. I have read through all sorts of implementations but have not found any best practice so to speak. Should I be using a combination of the two?
So far I have considered several options:
rFa only (changes results when fps changes):
var animate = function() {
draw();
requestAnimationFrame(animate);
}
time-based only (not always consistent):
var animate = function() {
now = Date.now();
delta = now - last;
last = now;
draw(delta);
window.setTimeout(animate, 1000/60)
}
set FPS on rFA with setInterval (not always consistent):
setInterval(function () {
draw();
requestAnimationFrame();
}, 1000/fps);
rFA trying to force fps (does not seem very robust, variable delta would work better):
var delta = 1000 / fps;
var animate = function() {
now = Date.now();
if (now - last >= delta) {
last = now;
}
draw(delta);
requestAnimationFrame(animate);
}
time-based rFA (some strange results):
var animate = function () {
now = Date.now();
delta = now - last;
last = now;
draw(delta);
requestAnimationFrame(animate);
}
Ignore the lack of browser support and the use of Date.now(), I just want to demonstrate my flow of thinking. I think that the last option is preferable, but the last two can run into problems with updating too far and missing collisions etc as well as updates taking too long that the animation looses all control.
Also when a user tabs out using rFA only the animation will pause, using a time based function to call rFA means that the game/animation will continue to run in the background which is not ideal.
What would be the best way to handle animations trying to keep consistent results regardless of fps, all of the above might be bad and my apologies for the long post (it is just what I have tried so far and am still pretty lost)? even better with with the above issues in mind?
If you have requestAnimationFrame available, I wouldn't go against it and only call draw() from its callbacks. Of course, you should always use delta timing.
Here's a sophisticated variation of raF with a fallback to setTimeout for the game logic updates in case the frame rate is too low:
var maximalUpdateDelay = 25; // ms
var updateTimeout, now;
function animate() {
updateTimeout = setTimeout(animate, maximalUpdateDelay);
var delta = -now + (now = Date.now());
update(now, delta);
}
function main() {
clearTimeout(updateTimeout);
animate(); // update the scene
draw(); // render the scene
requestAnimationFrame(main);
}
main();
I'ld recommend taking a look at the HTML 5 - Game Development course on Udacity. I don't remember the implementation of this problem from the course (but there definitely was one), but my opinion from a gameplay perspective is that just using rAF (like your first bullet) is the most fun, even if there is game slow down due to too much processing needed on slower computers.
I think you're on the right track with the last one because it should give you the most consistency across devices running at different frame rates, but you definitely want to force your delta value down if it gets too high to avoid big jumps:
var animate = function () {
now = Date.now();
delta = now - last;
last = now;
if(delta > 20) {
delta = 20;
}
draw(delta);
requestAnimationFrame(animate);
};

HTML Canvas Interval vs RequestAnimationFrame

So, maybe total brainfart here. The syntax for setInterval() is pretty clear. Do something every x miliseconds. How is this best translated to using the requestAnimationFrame() ?
I have about 300 objects and each is supposed to perform an animation sequence at a certain interval (every 8, 6, 2, etc seconds)? How can I best accomplish this using requestAnimationFrame() which gets called ~60 times a second? There is probably an easy answer, I just, for the life of me, can't figure it out.
To force requestAnimationFrame to stick to a specific FPS you can use both at once!
var fps = 15;
function draw() {
setTimeout(function() {
requestAnimationFrame(draw);
// Drawing code goes here
}, 1000 / fps);
}
A little weird, but noth the most confusing thing in the world.
You can also use requestAnimationFrame not with FPS but with elapsed time in order to draw objects that need to be updated based on the time difference since the last call:
var time;
function draw() {
requestAnimationFrame(draw);
var now = new Date().getTime(),
dt = now - (time || now);
time = now;
// Drawing code goes here... for example updating an 'x' position:
this.x += 10 * dt; // Increase 'x' by 10 units per millisecond
}
These two snippets are from this fine article, which contains additional details.
Good question by the way! I don't think I've seen this answered on SO either (and I'm here way too much)
requestAnimationFrame is pretty low level, it just does what you already said: roughly gets called at 60fps (assuming the browser can keep up with that pace). So typically you would need to build something on top of that, much like a game engine that has a game loop.
In my game engine, I have this (paraphased/simplified here):
window.requestAnimationFrame(this._doFrame);
...
_doFrame: function(timestamp) {
var delta = timestamp - (this._lastTimestamp || timestamp);
for(var i = 0, len = this.elements.length; i < len; ++i) {
this.elements[i].update(delta);
}
this._lastTimestamp = timestamp;
// I used underscore.js's 'bindAll' to make _doFrame always
// get called against my game engine object
window.requestAnimationFrame(this._doFrame);
}
Then each element in my game engine knows how to update themselves. In your case each element that should update every 2, 6, 8 seconds needs to keep track of how much time has passed and update accordingly:
update: function(delta) {
this.elapsed += delta;
// has 8 seconds passed?
if(this.elapsed >= 8000) {
this.elapsed -= 8000; // reset the elapsed counter
this.doMyUpdate(); // whatever it should be
}
}
The Canvas API along with requestAnimationFrame are rather low level, they are the building blocks for things like animation and game engines. If possible I'd try to use an existing one like cocos2d-js or whatever else is out there these days.

requestAnimationFrame at a limited framerate

As I understand it, usage of the JS requestAnimationFrame API is intended for cases where the framerate isn't in need of being controlled, but I have a use case where it's essential that a <canvas> only updates at a certain fps interval that may be anywhere between 1 and 25 (between 1 and 25 frames per second, that is). Can I then somehow still effectively use rAF to get at the optimizations it offers?
This question has similarities to mine, but the accepted answer there made close to zero sense to me in the context of that question.
I have two possible solutions for this. The first one involves using a while loop to halt the execution of the script for a specified delay before calling requestAnimationFrame from within the callback. In the example where I saw this, it effectively limited the fps of the animation, but it also seemed to slow down the entire tab. Is this still actually a good solution? The second alternative, as mentioned in the question I linked to above, calls requestAnimationFrame within a setInterval. To me that seems a bit convoluted, but it could be that's the best option?
Or is there a better alternative to accomplish this?
Yoshi's answer is probably the best code solution to this problem. But still I'll mark this answer as correct, because after some research I basically found that my question was invalid. requestAnimationFrame is really meant to keep frame rates as high as possible, and it optimizes for scenarios where animation is meant to be kept consistent and smooth.
Worth noting though is that you don't need requestAnimationFrame to get optimization (even though rAF was touted as a great performance booster) since browsers still optimize regular drawing of a <canvas>. For example, when a tab isn't focused, Chrome for one stops drawing its canvases.
So my conclusion was that this question was invalid. Hope this helps anyone who was wondering something similar to me.
This is just a proof of concept.
All we do is set our frames per second and intervals between each frame. In the drawing function we deduct our last frame’s execution time from the current time to check whether the time elapsed since the last frame is more than our interval (which is based on the fps) or not. If the condition evaluates to true, we set the time for our current frame which is going to be the “last frame execution time” in the next drawing call.
var Timer = function(callback, fps){
var now = 0;
var delta = 0;
var then = Date.now();
var frames = 0;
var oldtime = 0;
fps = 1000 / (this.fps || fps || 60);
return requestAnimationFrame(function loop(time){
requestAnimationFrame(loop);
now = Date.now();
delta = now - then;
if (delta > fps) {
// Update time stuffs
then = now - (delta % fps);
// Calculate the frames per second.
frames = 1000 / (time - oldtime)
oldtime = time;
// Call the callback-function and pass
// our current frame into it.
callback(frames);
}
});
};
Usage:
var set;
document.onclick = function(){
set = true;
};
Timer(function(fps){
if(set) this.fps = 30;
console.log(fps);
}, 5);
http://jsfiddle.net/ARTsinn/rPAeN/
What you can do, though I don't really know if this is really any better is:
render to an invisible context with requestAnimationFrame
update a visible context with setInterval using a fixed fps
Example:
<canvas id="canvas"></canvas>​
<script type="text/javascript">
(function () {
var
ctxVisible = document.getElementById('canvas').getContext('2d'),
ctxHidden = document.createElement('canvas').getContext('2d');
// quick anim sample
(function () {
var x = 0, y = 75;
(function animLoop() {
// too lazy to use a polyfill here
webkitRequestAnimationFrame(animLoop);
ctxHidden.clearRect(0, 0, 300, 150);
ctxHidden.fillStyle = 'black';
ctxHidden.fillRect(x - 1, y - 1, 3, 3);
x += 1;
if (x > 300) {
x = 0;
}
}());
}());
// copy the hidden ctx to the visible ctx on a fixed interval (25 fps)
setInterval(function () {
ctxVisible.putImageData(ctxHidden.getImageData(0, 0, ctxHidden.canvas.width, ctxHidden.canvas.height), 0, 0);
}, 1000/40);
}());
</script>
Demo: http://jsfiddle.net/54vWN/

Categories

Resources