CSS change animation duration without jumping - javascript

I have a simple animation of a wheel spinning. I am trying to control the speed of the spinning wheel using a slider (input range). I have managed to do this, but every time I change the animation the animation restarts (it jumps). Looking for a solution to create a smooth increase of the speed. As the user increases the value of the slider, the wheel rotates with an increased speed.
In the code below, #loading is the spinning wheel.
$(document).on('input', '#slider', function() {
var speed = $(this).val();
$('#speed').html(speed);
$("#loading").css("animation-duration", 50 / speed + "s");
});
#loading {
position: absolute;
width:100px; height:100px; background-color:black;
left: 0;
right: 0;
margin: auto;
transform-origin: 50% 50%;
animation: rotateRight infinite linear;
animation-duration: 0;
}
#keyframes rotateRight {
100% {
transform: rotate(360deg);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div id="loading"></div>
<input type="range" min="0" max="100" value="0" class="slider" id="slider">
<p>Speed: <span id="speed"></span></p>

Classic Question
(with jumping.. now yet )
Version with jQuery
var lasttime = 0, lastduration = 0, angle = 0;
$(document).on('input', '#slider', function(event) {
var speed = $(this).val();
$('#speed').html(speed);
var el = $("#loading");
var duration = (speed > 0) ? 50 / speed : 0;
var currenttime = event.originalEvent.timeStamp / 1000;
var difftime = currenttime - lasttime;
el.removeClass("enable_rotate").show();
if (!lastduration && duration)
el.css("transform", "");
else
angle += (difftime % lastduration) / lastduration;
if (duration){
el.css("animation-duration", duration + "s")
.css("animation-delay", -duration * angle + "s")
.addClass("enable_rotate");
}
else
el.css("transform", "rotate(" + 360 * angle + "deg)");
angle -= angle | 0; //use fractional part only
lasttime = currenttime;
lastduration = duration;
});
.anime_object {
width:100px; height:100px; background-color:black;
position: absolute;
left: 0;
right: 0;
margin: auto;
}
.enable_rotate {
animation: rotateRight infinite linear;
}
#keyframes rotateRight {
100% {
transform: rotate(360deg);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<img id="loading" class="anime_object">
<input type="range" min="0" max="100" value="0" id="slider">
<p>Speed: <span id="speed"></span></p>
Work draft
save variable of currentanimation
http://www.w3.org/TR/css-animations-1/#interface-animationevent-attributes
SO
css-tricks.com

I'm not entirely sure that this will be possible without a different approach that doesn't use CSS animations. The issue is that the animation does not normalize whenever you change the speed. It is always animating from 0% of the animation to 100% of the animation. Every time you adjust the animation-duration, you're going to re-interpolate using the current position in the animation.
In other words, if you change from animation-duration: 25 to 50 at t=12, well the animation was halfway finished (180 degrees); now it's only a quarter finished (90 degrees). You can't control t though, that's the browser's. If you could, you would want to set t to remain where it was in the interpolation, in this example, t=25, so that you remain at the same percentage complete of the animation that you were, but you stretch the remaining time.
I modified your script a little to try and show what I'm describing a little better. It will increment the speed by 0.25 every second between speed 0 and 5. You can kind of see how the problem is that the browser controlled t is the issue.
You can rewrite this in order to control t yourself with JavaScript, but I think you'll have to drop the CSS animations.
To talk a little bit more to the point of this browser controlled t variable, take a look at this article on CSS-Tricks: Myth Busting: CSS Animations vs. JavaScript
Some browsers allow you to pause/resume a CSS keyframes animation, but
that's about it. You cannot seek to a particular spot in the
animation, nor can you smoothly reverse part-way through or alter the
time scale or add callbacks at certain spots or bind them to a rich
set of playback events. JavaScript provides great control, as seen in
the demo below.
That's your problem, you want to be able to change the duration of your animation, but then also seek to the correct spot in the animation.
$(function() {
var speed = parseInt($("#slider").val(), 10);
$("#speed").html(speed);
$("#loading").css("animation-duration", 50 / speed + "s");
var forward = true;
setInterval(function() {
speed += (forward ? 0.25 : -0.25);
if (speed >= 5) {
forward = false;
} else if (speed <= 0) {
forward = true;
}
$("#loading").css("animation-duration", 50 / speed + "s");
$("#slider").val(speed);
$("#speed").html(speed);
}, 1000);
});
#loading {
position: absolute;
left: 0;
right: 0;
margin: auto;
transform-origin: 50% 50%;
animation: rotateRight infinite linear;
animation-duration: 0;
}
#keyframes rotateRight {
100% {
transform: rotate(360deg);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<img id="loading" src="//placehold.it/100">
<input type="range" min="0" max="100" value="0" step=".25" class="slider" id="slider" value="0">
<p>Speed: <span id="speed"></span></p>

TL;DR
No, you can't (according to my tests)
Firs of all, let's get rid of the animation declaration from your css and move it up to Javascript so the animation loop doesn't start (even if you can't visually see it running).
Have you noticed that even when you move your slider from the initial position the box appears to start from a random position? that's because the animation loop has actually been running.
Now, you can actually get the current transform value applied to your box at any given time by your animation, using getComputedStyle(loadingElement).getPropertyValue('transform'); this will return a matrix which doesn't give you much just like that but we can calculate the angle of the rotation from that matrix:
(using some maths explained here)
Math.round(Math.atan2(b, a) * (180/Math.PI));
Now that we have this value we have to normalize it to have only positive values for the angle, and then we can apply this angle as the base value for transform: rotate(Xdeg)
so far so good, you can see this working in the code snippet, however even when you do this, and increment/decrement the speed value, the animation loop is already running with a set time scale and you can't reset this loop.
My answer so far is so someone else with a deeper understanding of the animation loop can build from, and maybe come up with a working code.
If you are still reading this you might think, well just drop the animation with loadingElement.style.removeProperty('animation') and the assign it again, tried it doesn't work. And what about starting the animation again with a setInterval(...,0) so it runs in the next loop, won't work either.
$(document).on('input', '#slider', function() {
var speed = $(this).val();
var loadingElement = document.querySelector("#loading")
$('#speed').html(speed);
//get the current status of the animation applied to the element (this is a matrix)
var currentCss = getComputedStyle(loadingElement).getPropertyValue('transform');
if (currentCss !== 'none'){
//parse each value we need from the matrix (there is a total of 6)
var values = currentCss.split('(')[1];
values = values.split(')')[0];
values = values.split(',');
var a = values[0];
var b = values[1];
var c = values[2];
var d = values[3];
//here we make the actual calculation
var angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
//normalize to positive values
angle = angle < 0 ? angle + 360 : angle;
loadingElement.style.removeProperty('animation'); //remove the property for testing purposes
$("#loading").css('transform', 'rotate(' + angle + 'deg)');
}
else{ //else for testing purposes, this will change the speed of the animation only on the first slider input change
$("#loading").css('animation', 'rotateRight infinite linear'); //see how the animation now actually starts from the initial location
$("#loading").css("animation-duration", 50 / speed + "s");
}
});
#loading {
position: absolute;
left: 0;
right: 0;
margin: auto;
transform-origin: 50% 50%;
/*I removed the initialization of the animation here*/
}
#keyframes rotateRight {
100% {
transform: rotate(360deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img id="loading" src="https://placehold.it/100">
<input type="range" min="0" max="100" value="0" class="slider" id="slider">
<p>Speed: <span id="speed"></span></p>

Yes, we can!
Here's the approach we'll take:
On input change, get the new speed value
Grab the current transform value of the element, which is returned as a matrix()
Convert the matrix() to a rotate value in degrees
Remove the existing animation
Create a new animation based on the current speed and rotate value
Apply the new animation to the element
The main issue we need to overcome is creating a new animation keyframes based on the current rotate value - we need to create a new animation with a starting value equal to the current rotate value, and an end value equal to the current rotate value + 360.
In other words, if our element is rotated 90deg and the speed is changed, we need to create a new #keyframes of:
#keyframes updatedKeyframes {
0% {
transform: rotate(90deg);
},
100% {
transform: rotate(450deg); // 90 + 360
}
}
To create the dynamic animation I'm using the jQuery.keyframes plugin to create dynamic #keyframes.
While this solution works, I don't believe it's super performant based on how the jQuery.keyframes plugin works. For every new animation keyframes the plugin appends an inline <style>. This results in potentially dozens, hundreds, or even thousands of keyframes being defined. In the example below I'm using the speed variable to create the #keyframe names, so it will create up to 100 unique #keyframes styles. There are a few optimizations we could make here but that's outside the scope of this solution.
Here's the working example:
$(document).on('input', '#slider', function() {
var speed = $(this).val();
$('#speed').html(speed);
var transform = $("#loading").css('transform');
var angle = getRotationDegrees(transform);
$("#loading").css({
"animation": "none"
});
$.keyframe.define([{
name: `rotateRight_${speed}`,
"0%": {
"transform": "rotate(" + angle + "deg)"
},
"100%": {
'transform': "rotate(" + (angle + 360) + "deg)"
}
}]);
if (speed === "0") {
$("#loading").css("transform", "rotate(" + angle + "deg)");
} else {
$("#loading").playKeyframe({
name: `rotateRight_${speed}`,
duration: 50 / speed + "s",
timingFunction: "linear",
iterationCount: "infinite"
});
}
});
function getRotationDegrees(matrix) {
if (matrix !== 'none') {
var values = matrix.split('(')[1].split(')')[0].split(',');
var a = values[0];
var b = values[1];
var angle = Math.round(Math.atan2(b, a) * (180 / Math.PI));
} else {
var angle = 0;
}
return angle;
}
#loading {
position: absolute;
left: 0;
right: 0;
margin: auto;
transform-origin: 50% 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://rawgit.com/jQueryKeyframes/jQuery.Keyframes/master/jquery.keyframes.js"></script>
<img id="loading" src="http://via.placeholder.com/100x100">
<input type="range" min="0" max="100" value="0" class="slider" id="slider">
<p>Speed: <span id="speed"></span></p>

Related

Element transform scale maintained after transform rotation occurrs

I have an element I am zooming in (one button) and then rotating (another button)
When I zoom in on the element the scale gets updated, then when I go to rotate element the scale is not kept from the zoom (but it really is, I can see the value is maintained browser debug) but it zooms back out to the initial scale when rotate is clicked.
let spin = 0;
let zoom = 1;
$('#SpinRight').click(function(){
//I know for a fact scale keeps value here, but zooms back out to original size when I //click spin
spin += 25;
$('#element').css({'transform' : 'rotate(' + spin + 'deg'});
})
$('#Magnify').click(function(){
zoom += 0.1;
$('#element').css({'transform' : 'scale(' + zoom + ')'});
})
transform rules don't stack, so if you do:
transform: rotate(45deg);
transform: scale(0.5);
The scale transform replaces the rotate rule, it doesn't add to it. If you want both you need to specify them both in a single rule:
transform: rotate(45deg) scale(0.5);
You could keep track of the rotation and scale in css properties and then have a rule that applies them both. (Or just apply them the way you're already doing it, but do both for each update.)
const demo = document.querySelector('.demo');
let rotate = 0;
let scale = 1;
document.querySelector('.rotate').addEventListener('click', () => {
demo.style.setProperty('--rotate', `${rotate += 10}deg`);
});
document.querySelector('.scale').addEventListener('click', () => {
demo.style.setProperty('--scale', scale += 0.1);
});
.demo {
--rotate: 0;
background: skyblue;
width: 100px;
height: 100px;
transform: rotate(var(--rotate, 0)) scale(var(--scale, 1));
}
button {
position: relative; /* just so the buttons stay on top */
}
<div class="demo"></div>
<button class="rotate">Rotate</button>
<button class="scale">Scale</button>

lag between jquery animation and css animation

I'm stacking 5 images by attributing to all of them an absolute position with the same top and left coordinates and a different z-index.
My goal is to rotate, translate and turn to 0 the opacity of the image having the highest z-index and increasing by 1 the z-index of all the other images.
I'm animating the image having the highest index with CSS transfomations and I change the z-index of the other images with jquery.
My code is the following:
var i = 1;
function swypeStyle(){
var imageLength = $('.my-image').length;
$('.my-image').each(function(){
$(this).removeClass('rotated');
});
$("#image"+i).addClass('rotated');
setTimeout(function(){
for(var j = 1; j <= imageLength; j++){
var currentZindex = $('#image'+j).css('z-index');
if(currentZindex == imageLength){
currentZindex = 1;
}else{
currentZindex++;
}
$('#image'+j).css('z-index',currentZindex);
}
}, 1100);
if(i == imageLength){
i = 1;
}else{
i++;
}
}
window.setInterval(function () {
swypeStyle();
}, 3000);
.my-image{
width: 144px;
left: 0px;
top: 0px;
position: absolute;
}
.my-image.rotated {
left: -150px;
top: 25px;
-webkit-transform: rotate(-30deg);
opacity: 0;
-webkit-transition: all 1s ease-in-out;
}
<img src="https://www.otop.sg/retailer/images/image1.jpg" class="my-image" id="image1" style="z-index: 5;">
<img src="https://www.otop.sg/retailer/images/image2.jpg" class="my-image" id="image2" style="z-index: 4;">
<img src="https://www.otop.sg/retailer/images/image3.jpg" class="my-image" id="image3" style="z-index: 3;">
<img src="https://www.otop.sg/retailer/images/image4.jpg" class="my-image" id="image4" style="z-index: 2;">
<img src="https://www.otop.sg/retailer/images/image5.jpg" class="my-image" id="image5" style="z-index: 1;">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
My animation works well, the problem is when I switch and spend some time on an other chrome tab and when I go back on my script chrome tab there's a lag between my css animation and my jquery animation.
The issue is that when your tab isn't active, Chrome will suspend items like animations, to reduce resource utilization.
My recommendation would be to do all of the transitions/animations in CSS only and use Javascript to only add/remove classes; this way the animations will remain synchronized.
If you really can't give up your jQuery animations, you could try wrapping your animations in a requestAnimationFrame() loop.
As MDN explains:
Like CSS transitions and animations, requestAnimationFrame() pauses when the current tab is pushed into the background.
Here is an example:
Usage:
// Get element.
const elem = document.querySelector( '#elementID' )
// Get current size.
const startSize = elem.width
// Set end size.
const endSize = startSize * 2 // Double the size of the element.
// Set animation duration.
const duration = 1000 // 1000ms -> 1s
animate( ratio => {
setWidth( elem, lerp( startSize, endSize, ratio ))
}, duration )
Implementation:
// Resolves a promise once an animation is complete.
// Allows you to specify an animation duration, start and end values,
// and automatically handles frame interpolation.
function animate( callback, duration ) {
return new Promise( resolve => {
let
// Amount of time in ms since last frame
delta = 0,
// Used to calculate delta
lastTimestamp = 0,
// Ratio for use in linear interpolation.
// Passed through to the callback and incremented
// with respect to duration.
// The ratio determines how far along the start and end values we are.
ratio = 0,
// Speed is an inverse function of time, since ratio is normalized.
speed = 1 / duration
// Start the update loop
update( performance.now())
// Update loop
function update( timestamp ) {
if ( ratio < 1 ) {
// Calculate frame delta and update last frame's timeStamp.
// In the first frame, set lastFrame to this frame's timeStamp.
delta = timestamp - ( lastTimestamp || timestamp )
lastTimestamp = timestamp
// Update ratio as a function of speed and frame delta
ratio = clamp( ratio + ( speed * delta ))
// Execute callback
callback( ratio )
// Request next frame
requestAnimationFrame( update )
}
// Make good on the promise
else resolve()
}
})
}
/** Linear interpolation between two scalars */
function lerp( min, max, t ) {
/*
* Get difference, multiply by `t` and add min.
*
* The ratio `t` represents the percentage of that distance
* we add back to min, so that a fraction of 1 adds 100%
* of the distance, which equals the max value.
*
* Precise form of linear interpolation,
* ensures t=1 always equals max.
*/
return ( 1 - t ) * min + t * max
}
// Set width of an element
function setWidth( element, width ) {
element.style.width = `${ width }px`
}
More about requestAnimationFrame()
I used css to do my animation because you can't animate rotation with the jQuery animate function.
Thanks to this post CSS rotation cross browser with jquery.animate() I found a work around and now my animation is 100% jQuery and there is no lag anymore.
var i = 1;
function AnimateRotate(angle,elem) {
$({deg: 0}).animate({deg: angle}, {
duration: 700,
step: function(now) {
elem.css({
transform: 'rotate(' + now + 'deg)'
});
}
});
}
function swypeStyle(){
var imageLength = $('.my-image').length;
$('.my-image').each(function(){
$(this).css({
"left": 0,
"top": 0,
"opacity": 1,
WebkitTransform: 'rotate(0deg)',
'-moz-transform': 'rotate(0deg)'
})
});
AnimateRotate("-30",$("#image"+i));
$("#image"+i).animate({
"left": "-150px",
"top": "25px",
"opacity": 0
},700);
setTimeout(function(){
for(var j = 1; j <= imageLength; j++){
var currentZindex = $('#image'+j).css('z-index');
if(currentZindex == imageLength){
currentZindex = 1;
}else{
currentZindex++;
}
$('#image'+j).css('z-index',currentZindex);
}
}, 1100);
if(i == imageLength){
i = 1;
}else{
i++;
}
}
window.setInterval(function () {
swypeStyle();
}, 3000);
.my-image{
width: 144px;
left: 0px;
top: 0px;
position: absolute;
}
<img src="https://www.otop.sg/retailer/images/image1.jpg" class="my-image" id="image1" style="z-index: 5;">
<img src="https://www.otop.sg/retailer/images/image2.jpg" class="my-image" id="image2" style="z-index: 4;">
<img src="https://www.otop.sg/retailer/images/image3.jpg" class="my-image" id="image3" style="z-index: 3;">
<img src="https://www.otop.sg/retailer/images/image4.jpg" class="my-image" id="image4" style="z-index: 2;">
<img src="https://www.otop.sg/retailer/images/image5.jpg" class="my-image" id="image5" style="z-index: 1;">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

How can I make my setTimeouts get stopped here?

The behavior I'm trying to achieve is this:
On hover/mouseenter, change the background image from the placeholder to a gif whose positions changes in order to achieve an animated effect, then go back to the placeholder when the mouse leaves.
My code is
$('.filmstrip').mouseenter(function(){
var $that = $(this),
w = $that.width(),
fr = $that.attr('data-framerate');
$that.css('background-image','url('+$that.attr('data-gifurl')+')');
for ( var i = 1, n = $that.attr('data-ticks'); i <= n; ++i )
{
(function(j){
setTimeout(function(){
$that.css('background-position-x','-'+(w*j)+'px');
}, j*fr);
})(i);
}
$that.bind('mouseleave',function(){
$that.css('background-image','url('+$that.attr('data-placeholder')+')').css('background-position-x','0');
});
});
and the bug I'm having is that if the gif hasn't finished animating, then the
.css('background-position-x','0')
part of
$that.css('background-image','url('+$that.attr('data-placeholder')+')').css('background-position-x','0');
});
doesn't work because the background position is still being moved by the animation. So I need some way to first stop the setTimeout stuff if it isn't finished running. Any idea how I can do that?
This may be something better done with CSS rather than javascript.
Option #1 - Use an actual GIF
You could compile the frames which you want animated into an actual GIF file, and then have the background image change based on hover:
<div class="filmstrip"></div>
And then CSS
.filmstrip { background:transparent url('static_image.jpg') no-repeat 0 0 }
.filmstrip:hover { background-image:url( 'animated_image.gif' ) }
Option #2 - Use CSS3 Animation
You could keep the animated image as a strip of frames (of a known length) and then use something like:
<div class="filmstrip"></div>
With CSS
.filmstrip { background:transparent url('static_image.jpg') no-repeat 0 0 }
#keyframes animate-bg {
0% { background-position: 0 0 }
100% { background-position: -1000px 0 }
/* where 1000px is the length of the strip */
}
.filmstrip:hover { animation: animate-bg 5s steps(50) infinite }
/* where 5s is the overall loop length time and 50 is the number of frames in the strip */
Option #3 - Use Spritely
Spritely is a jQuery plugin which seems to manage all elements of turning a filmstrip/sprite image into an animation, including being able to start/stop the animation, reset to the first frame, change FPS, etc.
Add a stop variable :
$('.filmstrip').mouseenter(function(){
var isStopped = false;
var $that = $(this),
w = $that.width(),
fr = $that.attr('data-framerate');
$that.css('background-image','url('+$that.attr('data-gifurl')+')');
for ( var i = 1, n = $that.attr('data-ticks'); i <= n && !isStopped; ++i )
{
(function(j){
setTimeout(function(){
if (!isStopped) {
$that.css('background-position-x','-'+(w*j)+'px');
}
}, j*fr);
})(i);
}
$that.bind('mouseleave',function(){
isStopped = true;
$that.css('background-image','url('+$that.attr('data-placeholder')+')').css('background-position-x','0');
});
});
If isStopped is not accessible (because not tested) from the timeout, then just create a new variable in a inner scope which you affect isStopped value.
You can use an interval based solution like
$('.filmstrip').mouseenter(function() {
var $that = $(this),
w = $that.width(),
fr = +$that.attr('data-framerate'),
ticks = +$that.attr('data-ticks');
$that.css('background-image', 'url(' + $that.attr('data-gifurl') + ')');
var counter = 0;
var interval = setInterval(function() {
$that.css('background-position-x', '-' + (w * ++counter) + 'px');
if (counter >= ticks) {
clearInterval(interval);
}
}, fr);
$(this).data('bg-interval', interval)
}).mouseleave(function() {
clearInterval($(this).data('bg-interval'));
$(this).css('background-image', 'url(' + $(this).attr('data-placeholder') + ')').css('background-position-x', '0');
});
.filmstrip {
height: 64px;
border: 1px solid grey;
background-position: right;
background-position-y: inherit;
display: inline-block;
width: 64px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="filmstrip" data-framerate="400" data-ticks="10" data-gifurl="//cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg?v=bc7c2f3904bf">
</div>
U can use clearTimeout to stop setTimeOut.
Working Demo-
var myVar;
function myFunction() {
myVar = setTimeout(function(){ alert("Hello"); }, 3000);
}
function myStopFunction() {
clearTimeout(myVar);
}
<p>Click the first button to alert "Hello" after waiting 3 seconds.</p>
<p>Click the second button to prevent the first function to execute. (You must click it before the 3 seconds are up.)</p>
<button onclick="myFunction()">Try it</button>
<button onclick="myStopFunction()">Stop the alert</button>
More-
Mozilla Developer Network
and
W3School

Web technique to resize an image over a given time interval

Is there a technique to resize an image over a given time interval?
What I want to do is have an image and when the mouse rolls overs it, it should resize the image making it larger. All I can find are simple rollover scripts that instantly resize the image. I want to do it over a period of about a second.
And as a must it cannot lag and destroy the visual experience. I am looking for an approach in javascript, jQuery, or HTML5 if it's possible; other suggestions appreciated but no flash.
It's very easy with CSS3 Transitions:
.myImg
{
width: 200px;
transition-duration: 1s;
-webkit-transition-duration: 1s;
}
.myImg:hover
{
width: 300px;
}
Demo: jsfiddle.net/yyDd4
You can do it in jQuery in this way.
var factor = 2;
$('#foo').mouseover(function() {
$(this).animate({
top: '-=' + $(this).height() / factor,
left: '-=' + $(this).width() / factor,
width: $(this).width() * factor
});
});
and the other techniques are here.
You can do this in plain javascript, though animation is always surprisingly complicated, especially if you want the image to shrink back after the mouse moves off it. Making an object to store the state is possibly the best solution and is also quite adaptable (other images, other types of animation).
http://jsfiddle.net/VceD9/6/
new GrowingImage('myImage', 2, 1000);
function GrowingImage(id, factor, duration) {
var el = document.getElementById(id),
originalWidth = el.offsetWidth,
originalHeight = el.offsetHeight,
timer,
stage = 0,
frameRate = 17,
maxStage = duration / frameRate;
el.onmouseover = function () {
animate(1);
};
el.onmouseout = function () {
animate(-1);
};
function animate(direction) {
clearInterval(timer);
timer = setInterval(function() {
stage += direction;
if (stage <= 0) {
stage = 0;
clearInterval(timer);
} else if (stage >= maxStage) {
stage = maxStage;
clearInterval(timer);
}
var scale = 1 + (factor - 1) * stage / maxStage;
el.style.width = originalWidth * scale + 'px';
el.style.height = originalHeight * scale + 'px';
}, frameRate);
}
}
If exact timing is important to you, you may need to adjust this so that it keeps track of the amount of time that the current animation has been running.

How do you speed up an animation mid animation with jQuery?

Suppose you have an animation running with a certain time like this:
$('span').animate({opacity : 1}, 10000);
The animation is quite long so the user tries clicking the button again. The animation will be a certain amount of time through the animation already, which is probably going to be different every time.
On the second click is it possible to update the animation process keeping the opacity of the object when the user clicks, just changing the time it will take to finish?
Basically I want to update the animation process mid way through the animation.
You can use the step option of animate to keep track of how far along the animation is. Then with that information, you can calculate the time remaining in the animation. Then stop the current animation and start a new one with half the duration.
http://jsfiddle.net/MdD45/
EDIT
It looks like the 2nd parameter passed to step contains a property named pos which tells you what percentage along in the animation you are. That can simplify things further.
http://jsfiddle.net/MdD45/1/
var startVal = 0;
var endVal = 1;
var duration = 10000;
var howfar = 0;
$('span').css("opacity",startVal)
.animate({
opacity : endVal
}, {
duration: duration,
step: function(now, fx){
howfar = fx.pos; // between 0 and 1, tells how far along %
}
});
$("button").click(function(){
// calculate the new duration as half of the remaining duration
var timeRemaining = duration - (howfar * duration);
duration = timeRemaining / 2;
$('span').stop().animate({
opacity : endVal
}, {
duration: duration,
step: function(now, fx){
howfar = fx.pos; // between 0 and 1, tells how far along %
}
});
});
​
I put something together yesterday to skip in jQuery animations, here's the code, it should be pretty easy to modify for your use-case:
EDIT: Modified version:
Demo: http://jsfiddle.net/SO_AMK/fJyKM/
jQuery:
var time = 10000;
var opacity = 1;
var currentTime = 0;
$("#square").animate({
opacity: opacity
}, {
duration: time,
step: function(now, fx) {
currentTime = Math.round((now * time) / opacity);
},
easing: "linear"
});
$("#hurry").click(function() {
$("#square").stop().animate({
opacity: opacity
}, {
duration: ((time - currentTime) / 4), // Get remaining time, divide by 4
step: function(now, fx) {
currentTime = Math.round((now * time) / opacity);
},
easing: "linear"
});
});​
It also works for other properties, like width. The only catch is that if it is a decreasing value than you need to use a different script.
yes you can ..
You have to stop the animation with "stop()" method then start a new animation against the same node on the same property and as a target value, the original one.
I'm not fully sure if this would work, but consider doing like this:
<div id="box"></div>
<style type="text/css">
div#box {
-webkit-transition: width 10s;
-moz-transition: width 10s;
transition: width 10s;
background: #000;
width: 100px;
}
div#box.speedy {
-webkit-transition: width 5s;
-moz-transition: width 5s;
transition: width 5s;
}
</style>
<script style="text/javascript">
$(function() {
$('div#box').css('width', '400');
setTimeout(function() {
$('div#box').addClass('box');
}, 2000);
});
</script>
Basicly let css animate it, and add another class that speeds the transition up.

Categories