Adding easing on requestAnimationFrame - javascript

I need to reproduce the same effect as here: http://www.chanel.com/fr_FR/mode/haute-couture.html = a swipe effect on mouse move event.
I just need some help on the animation part.
function frame() {
$('.images-gallery').css({
'transform': 'translateX('+ -mouseXPerc +'%)'
});
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
$(document).on('mousemove',function(e){
mouseXPerc = e.pageX/containerWidth*100;
});
Here's what I've done so far. It works as supposed, but as you can imagine, it's pretty raw, I need some easing in that. How can I edit my frame() function to get something smoother ?
Edit : I can't use CSS transition / animation as I change the value on requestAnimationFrame (each 1/30 sec).

I think I've found an answer for you. It's based on this library
First, I would just grab a function from that site
function inOutQuad(n){
n *= 2;
if (n < 1) return 0.5 * n * n;
return - 0.5 * (--n * (n - 2) - 1);
};
Then, I would use a modified form of the example code, something like this
function startAnimation(domEl){
var stop = false;
// animating x (margin-left) from 20 to 300, for example
var startx = 20;
var destx = 300;
var duration = 1000;
var start = null;
var end = null;
function startAnim(timeStamp) {
start = timeStamp;
end = start + duration;
draw(timeStamp);
}
function draw(now) {
if (stop) return;
if (now - start >= duration) stop = true;
var p = (now - start) / duration;
val = inOutQuad(p);
var x = startx + (destx - startx) * val;
$(domEl).css('margin-left', `${x}px`);
requestAnimationFrame(draw);
}
requestAnimationFrame(startAnim);
}
I might change how 'stop' is calculated, I might write something to ensure that it ends on destx, etc, but that's the basic format
Showing it in this jsfiddle
I'm actually kinda proud of this one. I've been wanting to figure this out for a while. Glad I had a reason to.

You can create your own ease function and use it inside your frame function:
var ease = function() {
var x = 0;
return function(x_new) {
x = (x_new+x)*.5;
return x;
}
}();
function frame() {
$('.images-gallery').css({
'transform': 'translateX('+ -ease(mouseXPerc) +'%)'
});
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
$(document).on('mousemove',function(e){
mouseXPerc = e.pageX/containerWidth*100;
});

Related

Rotation tween stop and tween start on mouse down

I have an object rotating on z axis. onMouseDown the rotation stops and onMouseUp the rotation resumes after 2 seconds.
Is there a way for the rotation to resume slowly (like ease in for css)?
The code for the rotation start and stop.
if ( etaj1 !== undefined ) {
etaj1.rotation.z += delta * 0;
if(!isMouseDown){
etaj1.rotation.z += delta * 0.05 ;
}
}
Also there is a resetTimer condition if a new click is made before the 2 seconds have passed.
var timerHandle = setTimeout (wait , 2000);
function resetTimer () {
window.clearTimeout (timerHandle);
timerHandle = setTimeout (wait , 2000);
}
function onMouseDown(){
console.log("Mouse DOWN");
isMouseDown = true;
}
function onMouseUp(){
window.clearTimeout (timerHandle);
timerHandle = setTimeout (wait , 2000);
}
function wait (){
resetTimer;
console.log("Mouse UP");
isMouseDown = false;
}
the idea is to make a transition for isMouseDown = false; and another transition for isMouseDown = true;
Basically what i want to do is stop the rotation from the link below with a quart.out tween, wait 2 seconds and if no click resume rotation with a quart.in tween.
https://threejs.org/examples/#webgl_loader_collada
Thanks.
I'm not a three.js guy, however what you are asking is applicable in other areas as well. So I will put here a general solution. First the proof of concept:
https://jsfiddle.net/ibowankenobi/u4xwnLam/4/
I'll start declaring the globals I need, wrap them up as you want:
var rotation = 0;
var bar = document.getElementById("bar");
var container = document.getElementById("container");
I need to demonstrate this without three.js so I need to have a container and something to rotate so you do not need bar and container in your case. Rotation in your case is your etaj1.rotation.z
We first need a function that will map 0-1 to 0-1 again, but not linearly. These are called interpolations, there are many ones, cosine, this, that, most widely used one is a stitched and transformed x^3 and gives a "slow in slow out" affect:
function slowInSlowOut(t){
if(2*t<<0){
return 4*(t-1)*(t-1)*(t-1)+1;
} else {
return 4*t*t*t;
}
}
We have to do something with this function so I will write another animate
function changeSpeed(obj,newSpeed){
var oldSpeed = obj.__oldSpeed || 0;
var startTime;
newSpeed = newSpeed || 0;
obj.__currentFrame && window.cancelAnimationFrame(obj.__currentFrame);//cancel a previous changeSpeed if fired twice
obj.__currentFrame = window.requestAnimationFrame(anim);
function anim(t){
startTime = startTime || t;
var elapsed = t - startTime,
parametric = slowInSlowOut(elapsed/2000);
if(parametric>=1){
obj.__oldSpeed = newSpeed;
} else {
obj.__oldSpeed = newSpeed + (oldSpeed-newSpeed) * (1-parametric);
obj.__currentFrame = window.requestAnimationFrame(anim);
}
}
}
You provide this function an object, and it will attach it a proprietary __oldSpeed property, the newSpeed parameter is measured in means of degrees/17ms (because I am using requestAnimationFrame and that will fire each ~17ms). So in your case the obj is your etaj1.
Within this function you can already update your etaj1.rotation.z if you want. But I will write a third function to fire continuously and listen to __oldSpeed property and take action based on that:
function animate(obj){
rotation += (obj.__oldSpeed || 0);
rotation = rotation % 360;
obj.style.transform = "rotate("+rotation+"deg)";
window.requestAnimationFrame(function(){animate(obj)});
}
Again here, obj is your etaj1 and rotation is etaj1.rotation.z. I am using DOM here, so I need to use the styles. You can adapt it to your case.
Animate won't fire, so we need to fire him:
window.requestAnimationFrame(function(){animate(bar)});
I'll add the eventListeners as you described:
container.addEventListener("mousedown",function(){changeSpeed(bar,0)},false);
container.addEventListener("mouseup",function(){changeSpeed(bar,12)},false);
Now we give it a starting speed:
changeSpeed(bar,12);
There you go. In case you wondered how to do it yourself, this might get you started. I guess people using other technology stacks can also adapt this to their case.
PS:
Someone mentioned a delay, in that case modify the changeSpeed a bit:
function changeSpeed(obj,newSpeed,delay){
delay = delay || 0;
var oldSpeed = obj.__oldSpeed || 0;
var startTime;
newSpeed = newSpeed || 0;
obj.__currentFrame && window.cancelAnimationFrame(obj.__currentFrame);
obj.__currentFrame = window.requestAnimationFrame(anim);
function anim(t){
startTime = startTime || t;
var elapsed = t - startTime;
if(elapsed < delay) {
return obj.__currentFrame = window.requestAnimationFrame(anim);
}
var parametric = slowInSlowOut((elapsed-delay)/2000);
if(parametric>=1){
obj.__oldSpeed = newSpeed;
} else {
obj.__oldSpeed = newSpeed + (oldSpeed-newSpeed) * (1-parametric);
obj.__currentFrame = window.requestAnimationFrame(anim);
}
}
}
Here is a fiddle that has a 2 second delay:
https://jsfiddle.net/ibowankenobi/u4xwnLam/10/

Prevent function from starting second time, if first is not finished

I created a code that throws an element to up with an acceleration(works like gravity) value and falls down when changed_lenght reaches to zero;
"mouseenter" is triggering this function. When mouse leaves and enters quickly the function starts over and over again, and not stoping the previous ones so it looks so bad.
I wonder is there any way to check if previous one was ended before starting the function. Or any other solution that can help me ?
here is my JS codes:
$('#outher').mouseenter(function moveone(){
var t = 0;
var v = 20;
var p = 275;
var a = 5;
var moveoneint = setInterval(jump, 50);
function jump(){
t++;
x = (v*t) - (0.5 * a * t * t);
p = (p - x);
if(x <= 0){
clearInterval(moveoneint);
t = 0;
var movetwo = setInterval(fall, 50);
function fall(){
t++;
x = (0.5 * a * t * t );
p = (p + x);
document.getElementById('inner').style.top = p + 'px';
if(p >= 275){
clearInterval(movetwo);
}
}
}
else{
document.getElementById('inner').style.top = p + 'px';
}
}
});
and Here is a Fiddle:
http://jsfiddle.net/ctarimli/TJte8/
You can simply remove the mouseenter handler until the animation has completed.
JSFIDDLE
Basically,
var $outher = $('#outher');
var onMouseEnter = function() {
$outher.off('mouseenter', onMouseEnter);
// do all your animation logic, and once complete...
$outher.on('mouseenter', onMouseEnter);
};
$outher.on('mouseenter', onMouseEnter);
If you are open to third-party library code, then throttle /debounce will do what you want. Note that the page mentions jQuery, but the library does not actually require jQuery.
In particular, look at the examples for the $.throttle method.
Use jQuery is to check if div is animating
if(!$('.inner').is(':animated'))
{
//Your code
}

Convert jQuery recursive animate to requestAnimationFrame

I need time precision during an animation divided into steps.
The progress bar’s width is increased every 100ms by 20 pixels. I use jQuery’s animate() to animate the width.
Logging Date.now(), it’s clear that the time changes and it is not precise.
http://jsfiddle.net/trustweb/az3aE/23/
function animateProgress() {
console.log(Date.now() - progressTime);
progressTime = Date.now();
progress_width = $(progress).width();
if (progress_width < progressLimit) {
$(progress).animate(
{ width: '+=20' }, '' + progressTime, "linear", animateProgress
);
}
}
I’ve been reading about requestAnimationFrame, and I guess it’s the right way; how can I convert the function above to use requestAnimationFrame, or achieve precise timing in some other way?
You don’t need requestAnimationFrame. Use a transition instead and setInterval:
var progressBar = document.getElementById("progress");
var start = new Date();
var timer = setInterval(function() {
var t = new Date() - start;
if (t < 500) {
progressBar.style.width = (t * 20 / 100 | 0) + "px";
} else {
progressBar.style.width = "100px";
clearInterval(timer);
}
}, 100);
Demo (if I understand you correctly – your jsFiddle doesn’t have any elements involved, so it’s hard to tell)

Javascript - Distance (in pixels) - Decision

I have a simple javascript animation, where two cowboys (iamges) 'race' each other based on a random interval number.
What I can't find out how to do is for the script to take a decision on who is the winner, meaning if a cowboy reaches a pre-defined distance first, the script will know and will show an alert on who won.
Here is a screen shot to show an example:
This is the code I have so far: http://pastebin.com/Cmt4N8c9
Can give me some directions?
Thanks,
Brian
In your move() function you should do something like
if (x >= dest_x) {
alert('player 1 won');
} else if (x2 >= dest_x2) {
alert('player 2 won');
} else {
... continue the loop ...
}
You'd most likely put that behind
document.getElementById("cowboy").style.top = y+'px';
document.getElementById("cowboy").style.left = x+'px';
document.getElementById("cowboytwo").style.top = y2+'px';
document.getElementById("cowboytwo").style.left = x2+'px';
You might want to check your code on duplicate variables too, by the way.
AFAIK dest_x and dest_x2 are both the same for example.
Simple move
/* Y is not relevant since you only move it on X axis */
var position1 = 100;
var position2 = 100;
var dest = 800; //Or any given value
function move() {
var step1 = Math.floor(1 + (10 * Math.random() ) );
var step2 = Math.floor(1 + (10 * Math.random() ) );
position1 += step1;
position2 += step2;
document.getElementById("cowboy").style.left = position1+'px';
document.getElementById("cowboytwo").style.left = position2+'px';
if(position1 < dest && position2 < dest) {
window.setTimeout('move()',100);
} else {
//We have the winner
if(position1 > dest) alert("Winner is Cowboy1");
if(position2 > dest) alert("Winner is Cowboy2");
//Its also possible that both of them pass target value at the same step
}
}

Javascript mouseover/mouseout animated button

I have a button and onmouseover I want it to move right 100ish pixels at 10 pixels a move then stop. The moving isn't a problem its the stopping. I can do this no problem with jquery but I need to learn how to do it from scratch in javascript. this is my script to move right so far.
function rollRight() {
imgThumb.style.left = parseInt (imgThumb.style.left) + 10 + 'px';
animate = setTimeout(rollRight,20);
}
That moves it right just fine so to stop it i tried taking the amount of loops 5x10=50px and wrote it again as
function rollRight() {
var i=0;
while (i < 5) {
imgThumb.style.left = parseInt (imgThumb.style.left) + 10 + 'px';
animate = setTimeout(rollRight,20);
i++;
};
}
Now, I think I'm missing a piece to make it return the [] values for the while function, but I'm not sure how to make it work. Once I have the move right I can apply the same principle to move it back onmouseout.
If anyone can help me fix this that would be great. If you have a better script to do the animation that is just javascript, no libraries, that would be great too.
EDIT: Because leaving it as a comment didn't work well this is my current code
function rollRight() {
var left = parseInt (imgThumb.style.left);
if(left < 50) { // or any other value
imgThumb.style.left = left + 10 + 'px';
animate = setTimeout(rollRight,20);
}
}
function revert() {
var left = parseInt (imgThumb.style.left);
if(left < 50) { // or any other value
imgThumb.style.left = left + -10 + 'px';
animate = setTimeout(rollRight,20);
}
}
In the revert I'm having a problem getting it to move back. It's probably in the if(left<50) part.
var animate = null;
function rollRight() {
if(animate) clearTimeout(animate);
var left = parseInt (imgThumb.style.left);
if(left < 50) { // or any other value
imgThumb.style.left = left + 10 + 'px';
animate = setTimeout(rollRight,20);
}
}
function revert() {
if(animate) clearTimeout(animate);
var left = parseInt (imgThumb.style.left);
if(left > 0) {
imgThumb.style.left = (left - 10) + 'px';
animate = setTimeout(revert,20);
}
}
If you want to stick with setTimeout, you were close on your first one. I moved some hard coded numbers into variables:
var totalPixels = 100;
var increment = 10;
var frameTime = 20;
var numFrames = totalPixels / increment;
var curFrame = 0;
function rollRight() {
imgThumb.style.left = parseInt (imgThumb.style.left) + increment + 'px';
if(curFrame++ < numFrames) {
animate = setTimeout(rollRight,frameTime);
}
}
You could also switch to use setInterval and then every time the interval fires, decide if you should stop the interval based on some incrementing value.
Try this
var i = 0;
var moveInterval = setInterval(function(){
if(i>=5) clearInterval(moveInterval);
rollRight();
i++;
},20);
function rollRight() {
imgThumb.style.left = parseInt (imgThumb.style.left) + 10 + 'px';
}
Once it gets to 5, it will clear the interval out, and be done.

Categories

Resources