I've been reading through guides on the internet, but I haven't been able to find a way to animate a line from one position to a new position.
Apparently this requires "tweening", to create a smooth animation, and isn't built in? And all I ever find is "Check out THIS JavaScript framework that handles that for you!"
Does something exist in vanilla JavaScript (or jQuery) akin to:
animateLine(current, target, duration, easingFunction)
{
move(line.x1, target.x1, duration, easingFunction);
move(line.y1, target.y1, duration, easingFunction);
move(line.x2, target.x2, duration, easingFunction);
move(line.y2, target.y2, duration, easingFunction);
}
I want to just iterate through an array of lines, calling animateLine(lines[i], targets[i], duration, easingFunction) inside of setInterval().
Lots of different ways to skin this cat... However, as far as the frame-by-frame animation, you want requestAnimationFrame. This function calls whatever you pass to it, and passes a timestamp. Then it's just a matter of changing properties of these "lines" to move them across the screen.
If you were animating an <hr> element you could make it absolutely positioned and then just change the top amount each frame:
html:
<hr id="myLine"/>
css:
#myLine { position:absolute; width:100% }
js:
function easeOutQuad(t) { return t*(2-t) };
var startTime = null,
percent, elapsed,
duration = 3000,
end = 400,
hr = document.getElementById('myLine');
function step(timestamp) {
if (startTime === null) startTime = timestamp;
elapsed = timestamp - startTime;
percent = elapsed/duration;
if (elapsed < duration) {
// apply easing fn
percent = easeOutQuad(percent);
var frameDist = end * percent;
hr.style.top = frameDist + 'px';
// next frame
requestAnimationFrame(step);
} else {
// on complete...
}
}
// begin
requestAnimationFrame(step);
Here is a fiddle: http://jsfiddle.net/oytwdk9s/
If you want to support IE9 and older, you'll need a polyfill for requestAnimationFrame.
Related
I'm experimenting in Javascript with animating various elements using a cubic-bezier timing function.
(I know this is usually better done with CSS3, or a Javascript animation library. I'm simply using Javascript here to understand how bezier functions work, and teach myself about them.)
So, I get the basic concept, and I'm using a simple bezier curve library written by Stephen McKamey, which is a great Javascript port of the Webkit implementation.
I'm having trouble understanding how I can actually use this function to control an animation in Javascript. So, starting with something very simple: a basic black square that I can animate by moving it to the right, by incrementing its style.left property:
CSS:
.parent {
position: relative;
}
.foo {
border: 1px solid #000000;
background-color: black;
height: 200px;
width: 200px;
position: absolute;
}
HTML:
<div class = "parent">
<div class = "foo" id = "target"></div>
</div>
Okay, so, given a cubic bezier function "bezier", defined as follows:
function bezier(p1x, p1y, p2x, p2y, x, duration) { ... }
Where p1x, p1y, p2x and p2y are the curve control points (between 0 and 1.0), x is the value of the x coordinate, and duration is a duration in milliseconds. The function returns the corresponding y coordinate. I'm trying to simply animate this black box by moving it 400px to the right.
For my first attempt, I use the standard "ease" bezier values, which CSS3 uses, so our ease bezier function could be written as:
function ease(x, duration) {
return function() {
Bezier.cubicBezier(0.25, 0.1, 0.25, 1.0, x, duration);
}
}
So this should give us a slow start, then move fast, then end slowly.
Okay, so I assume the basic way to implement this is to use window.setInterval, and then for each interval, call the bezier function with a new value for x, and then apply the result somehow to the property we want to animate.
The thing is, I'm not sure what my "x" value is here. I assume that in this situation, x is actually the time, and y is the delta between the old position and new position (distance to move), but I'm not really sure. I'm probably wrong.
Anyway, plugging this all in, I'd write a function like:
var startPos = 0;
var endPos = 400; // pixels
var duration = 400; // milliseconds
var millisecondsPerInterval = 10;
var target = document.getElementById("target");
var t = 0;
var pos = 0;
var bezierFunction = Bezier.cubicBezier;
var interval = window.setInterval(
function() {
pos = pos + bezierFunction(0.25, 0.1, 0.25, 1.0, t / 1000, duration); // "ease" values
target.style.left = (pos * millisecondsPerInterval) + "px";
t += millisecondsPerInterval;
if (t === duration) { window.clearInterval(interval); }
},
millisecondsPerInterval
);
This seems to work - the box slowly begins moving and then speeds up. But then it just stops abruptly, rather than easing out. So I'm probably not applying this function correctly. I'm not even certain if "x" is supposed to be my time value here, and "y" is supposed to be the position delta (distance to move), but that seems the only way to apply this that makes any sense.
So, am I doing something wrong here? What is the correct way to apply a cubic bezier function to a property we want to animate using Javascript?
If you use JQuery, it may make the process simpler.
Based on an answer to a similar question (https://stackoverflow.com/a/6824695/363099), you could extend jQuery easing in order to add your custom easing function:
According to the jQuery 1.6.2 source, the meaning of the easing function is as follows. The function is called at various points in time during the animation. At the instants it is called,
x and t both say what the time is now, relative to the start of the animation. x is expressed as a floating point number in the range
[0,1], where 0 is the start and 1 is the end. t is expressed in
milliseconds since the start of the animation.
d is the duration of the animation, as specified in the animate call, in milliseconds.
b=0 and c=1.
So here is how it could work for your code:
$.extend(jQuery.easing,{bezier: function(x,t,b,c,d) {
return (
x <= 0 ? 0 :
x >= 1 ? 1 :
bezierFunction(0.25, 0.1, 0.25, 1.0, x, d)
);
} });
Then you can just use JQuery animation function:
$('#target').animate({right:'400px'},400,'bezier');
I'm develop my first phaser game for a client. The game is a car moving forward and it have two minutes to get the goal.
I want to increase progressive the car speed while the Up key is pressed until the speed limit has reached.
I'm moving the racetrack not the car by doing:
this.highway2 = game.add.tileSprite(game.world.centerX,game.world.height/2, game.cache.getImage('highway').width, game.cache.getImage('highway').height, 'highway');
this.highway2.anchor.setTo(0.5,0.5);
this.highway2.autoScroll(0,1000);
So, my questions are:
how can I control the speed of the autoScroll to simulate acceleration?
Is there a way to know how much time was a key pressed?
Is this the right approach to get this done?
Thanks in advanced.
Well, I don't know if this is the better way to do this, but it's work pretty well.
Just set speed limit and track it in the update function.
var playState = {
create: function(){
this.setInitialValues();
game.physics.startSystem(Phaser.Physics.ARCADE);
this.cursor = game.input.keyboard.createCursorKeys();
//highway
this.highway2 = game.add.tileSprite(game.world.centerX,game.world.height/2, game.cache.getImage('highway').width, game.cache.getImage('highway').height, 'highway');
this.highway2.anchor.setTo(0.5,0.5);
this.highway2.autoScroll(0,0);
//car
this.player = game.add.sprite(game.world.centerX+10, game.world.height-150, 'player');
this.player.anchor.setTo(0.5,0.5);
game.physics.arcade.enable(this.player);
//other things
},
update: function(){
this.movePlayer();
},
movePlayer: function(){
// move left and right
// If the left arrow key is pressed
if (this.cursor.left.isDown)
{
// Move the player to the left
this.player.body.velocity.x = -200;
}
// If the right arrow key is pressed
else if (this.cursor.right.isDown)
{ // Move the player to the right
this.player.body.velocity.x = 200;
}
// If neither the right or left arrow key is pressed
else
{
// Stop the player
this.player.body.velocity.x = 0;
}
//speed up and speed down
if (this.cursor.up.isDown)
{
if(this.currentSpeed < this.maxSpeed )
{
this.currentSpeed+=10;
this.highway2.autoScroll(0,this.currentSpeed);
}
}
else{
if(this.currentSpeed > 0 )
{
this.currentSpeed-=10;
this.highway2.autoScroll(0,this.currentSpeed);
}
}
if (this.cursor.down.isDown)
{
if(this.currentSpeed > 0 )
{
this.currentSpeed-=30;
this.highway2.autoScroll(0,this.currentSpeed);
}
}
},
setInitialValues: function(){
this.maxSpeed=1500;
this.currentSpeed=0;
}
}
You should apply a strict separation of concerns to 1) simplify coding 2) ease the fine tuning and even make the game more interesting and 3) easily 'plug' you logic with another controller (touch event instead of keyboard).
So here you have two separate concerns :
* measure for how long the user has been accelerating.
* given current speed, min/max speed, thrust time, decide what is current acceleration (== speed change).
For 1) it's pretty straightForward : record the time when input start, now duration is current time - start time. You'd better use game time if you have one (rather than Date.now()), so that you avoid surprises after the game resumes from a long tab-out.
For 2) You should fine tune the acceleration/deceleration of your game, it will make it more interesting.
Most obvious is not to have a constant acceleration : it must be harder and harder to reach the last % of the max speed. This way you give and incentive/reward to the player not to touch obstacle.
What you should do if the player is not thrusting, i don't know : slowly decelerate or quickly return to normal speed ?
Also you'll have to decide wether the boost is infinite or not, and maybe of a cool-down time.
So the function that compute current acceleration depends on thrusting (bool), thrust time (double) regularSpeed, maxSpeed, minAcc, maxAcc.
There are a lot of options here, but the code to compute acceleration could look like :
if (thrusting) {
// if we're not even at regular speed, max acceleration
if (speed<regularSpeed) { acc = maxAcc; return; }
// if we are above maxSpeed, no acceleration (?? or friction ??)
if (speed>maxSpeed) { acc=0; return; }
// compute current speed ratio
// a figure in [0;1] representing where is the speed in [minSpeed; maxSpeed]
var speedRatio = (currSpeed-regularSpeed)/(maxSpeed-regularSpeed);
// ease this ratio as you like
speedRatio = Math.sqrt(speedRatio);
// compute acceleration : the more speed, the less acceleration
// you might want to put one/some threshold this formula.
acc= minAcc + (1-speedRatio)*(maxAcc-minAcc);
return;
} else {
// do nothing if <= regularSpeed.
if (speed<=regularSpeed) { acc=0 ; return;}
// reduce speed if above regular speed
acc = breakAcc ; // or with friction => acc = - k * currSpeed;
return;
}
I'm having some troubles when animating the popular Simon Says game.
Here's what I'm doing:
squence = new Array();
colors = ["#green", "#red", "#yellow", "#blue"];
function add_sequence() {
var number = Math.random();
number = number * (4-1);
number = number.toFixed(0);
sequence.push(colors[number]);
for (var i = 0; i < sequence.length; i++) {
anim(sequence[i]);
};
}
function anim(id){
$(id).animate({
opacity: 0.3,
duration: 300
}, function(){
$(id).animate({
opacity: 0
}, 300);
});
}
The logic of the game is working, but I can't make the lights animate one by one. They just animate all at the same time.
I've tried with setTimeout(), but I can't get it work.
First, you'll have to pass the index in the function :
anim(sequence[i], 1);
Then use delay() like that :
$(id).delay(600*i) //Duration of the complete animation
.animate({opacity : 0.3}, 300) //Don't insert the duration in the CSS properties
.animate({opacity : 0}, 300) //You can chain animation
Partial answer since I don't personally have much experience with the jQuery animate function
Judging from the jQuery .animate documentation, I believe you have the animate function parameters incorrect. It should be something like this:
$(id).animate({
opacity: 0.3
// other CSS properties as desired
},
300)
The first parameter should contain the CSS the animate function should progress towards, while the second parameter contains the duration in milliseconds. There is another method as well, with the second parameter as an object containing options, which you can see in the documentation link provided.
Let me know whether that helps.
Change your for loop to a function that calls itself recursively. The recursive call should be from within the setTimeout, this way you get one iteration of the recursion per Timeout time.
function animate(seq, idx, timeout) {
anim(seq[idx]);
setTimeout(function() { animate(seq, idx+1, timeout) }, timeout);
}
Once you have set up the sequence you want to animate, you can start the animation by calling:
animate(sequence, 0, timeout)
and you would get one color animating per timeout.
Easing the excution flow in JS/JQuery
I've loop like this:
for (int i = 0; i < 100; i++)
{
doSomething(...); // returns momentally
}
I'm looking for a way to apply easing to the execution flow - by giving a total duration and an easing pattern (ex. 2 seconds & easeback). Is is something doable in JS (I'm using jQuery too)?
Update 2 Updated to clarify the question - I'm looking for the following:
Update 3 - Sheikh Heera is right, the sample I gave doesn't illustrate the real problem, execute function is updated to call an external module, which is closer to what I have. I don't see how jQuery's animate can be applied directly for calling functions.
easer.ease({ start: 0,
end: 100,
duration: 900,
easing: "easeOutBounce",
execute: function (e) { ExternalModule.DoSomethingUseful(e); } });
where start the end are integers, specifying the animated range, duration is animation duration in milliseconds, easing is the easing pattern used to animate the values within a range, execute - the function which gets called with values from 0 to 100, using the easing pattern supplied in the sample above it will animate myDiv's height from 0 to 100 within 0.9 seconds using easeOutBounce easing function.
Ideally as a small standalone plugin based on jQuery, definitely not part of Mootools or any other heavy hitters I can't afford bringing them in just for that.
To my best, I tried to achieve the thing you want using jQuery "animate" property.
Using this jQuery property will allow to add "easing", "duration", "callback" etc as needed by you. I used the "step" property to achieve this.
In order to work, you need to add a "dummy" tag to the HTML and hide it.
DEMO: http://jsfiddle.net/vaakash/Wtqm3/
HTML
<!-- Add a dummy tag to apply animate property -->
<span class="holder" style="display:none" ></span>
jQuery
$('.holder').animate(
{
'end': 100 // There is no such "end" css property just using as an end mark
},
{
duration: 500,
step: function(now, fx) {
myFunction(now); // Call your function here with the "now" param (i.e ExternalModule.DoSomethingUseful(now) ) in your case
}
// Add easing property if wanted
}
);
// The function
function myFunction(param){
$('body').append('Value now: ' + param + '</br/>');
}
Hope this helps.
If I understand your question correctly....You could try using .delay(100) or .delay(xmilliseconds) so it takes longer at each step.
Read more about delay on : http://api.jquery.com/delay/
Easing in jQuery
jQuery only has two easings, linear and swing. What you are looking for is the functions used in the jQuery UI. They can be accessed from $.easing.
$.easing demo where you can play with them.
You can call any function you'd like by name $.easing.easeOutBounce(e, t, n, r). The only confusing part is that they are actually 4 variable functions. From the jQuery UI docs:
based on easing equations from Robert Penner
The "standard" way to use them in f(x, 0, 0, 1) since e is the variable we typically want to change. n seems to be a "start point" for most functions, t looks to be a "power" in many of them, and r a linear scale factor.
Disclaimer
This is only my best guess from looking at the jquery and jquery-ui source files. I'd recommend if you want to do easing that you just write your own functions instead of relying on internal parts that are certainly not part of the stable API.
Your ease function
Although I wouldn't recommend making a function like this, it was an interesting experiment. Demo.
var ease = function(options) {
var t = 0;
// we need a time interval for animating
var tstep = options.interval || 10;
var duration = options.duration || 500
var i = options.start || 0;
var end = options.end || 100;
// the easing functions only work over x=0..1
var scale = end - i;
// one divided by the number of tsteps
var interval = tstep/duration;
var easing = options.easing || 'linear';
var callback = options.execute || function(){};
var timeout = function() {
// we call the callback but pass it the scale result of our easing
callback(scale*$.easing[easing](Math.min(i, 1)));
// if we haven't reached the end of the animation, queue up another frame
if (t <= duration) {
window.setTimeout(function() {
timeout();
}, tstep);
i += interval;
t += tstep;
}
};
timeout();
};
ease({
start: 0,
end: 100,
duration: 900,
easing: 'easeOutBounce',
// we'll print to the screen the results of the easing
execute: function(e) {$('body').append('<p>' + e)}
});
I'm using skinning / skeletal animation in ThreeJS. I have an animation, and I want to be able to move backward and forward through it, and jump to different locations within it, rather than the usual looping behaviour.
The animation is created like this, as in the example:
var animation = new THREE.Animation( mesh, geometry.animation.name );
I have tried updating the animation with negative deltas, as well as setting animation.currentTime directly:
animation.currentTime = animationLocation;
These appear to work only if I move forward in time, but if I go backward the animation breaks and I get an error:
THREE.Animation.update: Warning! Scale out of bounds: ... on bone ...
One thing that does actually work without error is to call stop() and then play() with a new start time:
animation.stop();
animation.play( true, animationLocation );
...however when I look at what these functions are actually doing, they involve many many function calls, looping, resetting transforms etc. This seems like a horrible way to do it, even if it works as a hack.
It may be that this functionality does not exist yet, in which case I'll try to dig in and create a function that does a minimal amount of work, but I'm hoping there is another way that I haven't found.
Can anyone help with this?
[UPDATE]
As an update on my progress, I'll post the best solution I have at this time...
I pulled out the contents of the stop() and play() functions, and stripped out everything I could, making some assumptions about certain values having already been set by 'play()'.
This still seems like it is probably not the best way to do it, but it is doing a bit less work than by just calling stop() then play().
This is what I was able to get it down to:
THREE.Animation.prototype.gotoTime = function( time ) {
//clamp to duration of the animation:
time = THREE.Math.clamp( time, 0, this.length );
this.currentTime = time;
// reset key cache
var h, hl = this.hierarchy.length,
object;
for ( h = 0; h < hl; h ++ ) {
object = this.hierarchy[ h ];
var prevKey = object.animationCache.prevKey;
var nextKey = object.animationCache.nextKey;
prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ];
prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ];
prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ];
nextKey.pos = this.getNextKeyWith( "pos", h, 1 );
nextKey.rot = this.getNextKeyWith( "rot", h, 1 );
nextKey.scl = this.getNextKeyWith( "scl", h, 1 );
}
//isPlaying must be true for update to work due to "early out"
//so remember the current play state:
var wasPlaying = this.isPlaying;
this.isPlaying = true;
//update with a delta time of zero:
this.update( 0 );
//reset the play state:
this.isPlaying = wasPlaying;
}
The main limitation of the function in terms of usefulness is that you can't interpolate from one arbitrary time to another. You can basically just scrub around in the animation.
You can use THREE.Clock and assign startTime, oldTime, elapsedTime.