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

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.

Related

CSS change animation duration without jumping

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>

How to make animation() endless

I have an element which should be animated all the time.
The animated element has such CSS properties:
#world {
height: 100%;
max-height: 100%;
position: relative;
display: flex;
background :red;
}
I can only move the element to a particular way, like this:
$('#world').animate({right: "2000px", easing: "linear"}, 2000);
But this will just animated for 2000px my element has an endless width.
UPDATE:
ALL 7.5 Sec. #world become bigger.
FIDDLE
You can have a recursive function:
var anim;
anim = function(times) {
$('#world').animate({
right: 2000 * times
}, 2000, 'linear');
return anim(times + 1);
};
anim(1)
This will continue to move #world to the right, 1 pixel per millisecond.
With a step callback:
var anim, my_step_callback;
my_step_callback = function() {
return $('body').append("<p>Hello</p>");
};
anim = function(times) {
$('#world').animate({
right: 2000 * times
}, {
duration: 2000,
easing: 'linear',
step: my_step_callback
});
return anim(times + 1);
};
Good luck!
Why not use right: 100%?
Assuming im reading your question correctly.
You could always do something like this, Where the function calls itself over and over again each time the animation ends.
function moveMe() {
$('#world').animate({width: '+=100px', easing: "linear"}, 2000, moveMe);
}
Why not set the righta value to the width of the element
var width = $('#world').width();
$('#world').animate({right: width, easing: "linear"}, 2000);
You could also increment the value of right as follows
DEMO http://jsfiddle.net/meqk662p/
var counter = 0;
setInterval(function () {
++counter;
}, 100);
$('#world').animate({right: counter+"px", easing: "linear"}, 2000);
The above example would be infinite
try increment the right and call in the callback the same function
var world_right = 2000;
function endlessWorldStep(){
$('#world')
.animate({right: world_right + "px", easing: "linear"}, 2000, endlessWorldStep);
world_right = world_right + 100;
}

Slide Up Linear Easing First Step Does Nothing

I'm developing an accordion plugin, and it's mostly done except for one bug where for the first few steps of the slideUp/slideDown, the accordion is 1px taller than it's meant to be, causing a visual bug. I've narrowed it down to the fact that the first step in the slideUp animation doesn't do anything, and I can't figure out why. Here's an example:
console.log('Start');
var diff = 0;
var upNow = 100;
var downNow = 0;
$.fx.interval = 1000;
var duration = $.fx.interval * 100;
$("#div1").slideUp({
easing: 'linear',
duration: duration,
step: function(now) {
if (now != 0 && now > 90) {
console.log("Slide Up: " + now);
upNow = now;
}
}
});
$("#div2").slideDown({
easing: 'linear',
duration: duration,
step: function(now) {
if (now != 0 && now < 10) {
downNow = now;
diff = 100 - (upNow + downNow);
console.log("Slide Down: " + now);
console.log("Slide Difference:" + diff);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style='height: 100px; background-color: red;' id='div1'>
</div>
<div style='height: 100px; background-color: blue; display: none;' id='div2'>
</div>
http://jsfiddle.net/hbh6U/
The problem is that I need these to be in sync, and I can't figure out why they're not, or how to get them in sync. One idea I've had is to skip the first step of the slideDown animation, but I'm not sure how to do that either. Has anyone got any ideas, or faced this bug before?
The problem comes down to this line in jQuery's internal defaultPrefilter method:
tween.start = prop === "width" || prop === "height" ? 1 : 0;
This causes the animation for the second div (from 1px to 100px) to be shorter than that of the first div (from 0 to 100px).
To solve this modify your step function like this:
function linearStep(now, animation){
var animationStart = animation.start;
if (animationStart === 1){
animationStart = 0;
}
animation.now = (animation.end - animationStart ) * animation.pos + animationStart;
}
It overwrites the calculated now value by doing the same calculation with a fixed animationStart, which is 0 instead of 1.
This will break if the animation actually starts at 1, but there'd be other ways to handle it then.
Side-by-side Fiddle: http://jsfiddle.net/Nd3w2/3/
i don't exactly know where is this issue coming from... Sunday morning... not too much time to investigate... But i found two possible solution based on your fiddle...
First one was to wrap these two DIVs in another DIV with overflow:hidden.
Second one... probably more appropriate is to call "slide" function only on one of the divs and then update the size of second one in callback, something like that:
console.log('Start');
var diff = 0;
var upNow = 100;
var downNow = 0;
$.fx.interval = 1000;
var duration = $.fx.interval * 100;
$("#div1").slideUp({ easing: 'linear', duration: duration, step: function(now)
{
if(now != 0 && now > 90)
{
console.log("Slide Up: " + now);
upNow = now;
}
$("#div2").height(100- $("#div1").height());
}});
Also remove "disply:none" form div2 styles...
It fixes the issue and is a bit more elegant solution in my opinion... Calling two separate animation functions can lead to possible sync problems... Hope that helps...

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.

JavaScript shrinking/growing circle transition

My first question here. :)
I'm looking for a transitions between two images where the image first shrinks in a circle shape and then the circle grows again containing the other image. It's hard to explain, and I may be using the wrong words, because I can't find anything about it on the Interwebz.
I'm talking about an effect like the Loony Toons ending.
http://www.youtube.com/watch?v=ZuYIq-J5l9I
That shrinking-to-black, can it be done in JavaScript/JQuery?
TL:DR
- Cross-browser: [**See a working demo
here**](http://jsfiddle.net/lthibodeaux/8DSjz/).
Well, mostly working... and
cross-browser. Could do worse. ;]
- Purely CSS3 Solution: [**See a working demo
here**](http://jsfiddle.net/lthibodeaux/8DSjz/16/)
How do I even begin to describe this one? It would be a lot easier if the CSS 2 clip standard supported anything besides a "rect" value, namely a "circle" or "ellipse" but... since that doesn't exist, I've done my best to piece something together that will do what you're asking. The caveats are many. One is that this is only going to work on something with a solid color background in the event you wanted the picture to clip to the background. Another is that while I've tried to account for the CSS update timing across browsers, the rendering still isn't "perfect." My initial approach was to simply animate the clip on the image that was getting replaced, but that didn't work due to the way updates were made to the clipping via the easing function in the plugin I located. The final approach is below.
The Approach
The concept is to set the image as a background-image property of a container like a <div> with a background-position of center center, and the position of the container to relative, or anything non-static. The next is to generate the clipping elements as children of the container. The first is a position: absolute clipping circle image of the color of your background, either transparent PNG or GIF (I prefer the former), and the next four are divs, also with absolute positions that have left, right, top, and bottom attributes set to 0 for each of the respective sides they will clip. The idea is to animate the top, left, width, and height of the clipping circle image and synch up the width and height of the clipping divs using the step callback option of the .animate() call by matching them to the current left and top values. Between animations, you change the background-image of the container to the new image and then start the animation back in the opposite direction.
This required a little finessing in IE7, 8, and Webkit browsers as the animation clipped much more cleanly in Firefox and IE9. This would be the adjust variable you'll see in the working demo.
The sample code is below:
The Markup
<div class="imageContainer image1">
<img class="clip" src="clipCircle.png" />
<div class="top fill"></div>
<div class="left fill"></div>
<div class="right fill"></div>
<div class="bottom fill"></div>
</div>
The CSS
div.imageContainer
{
background-position: center;
width: 300px;
height: 300px;
position: relative;
}
img.clip
{
width: 100%;
height: 100%;
position: absolute;
}
div.fill
{
position: absolute;
background-color: White;
}
div.left, div.right
{
height: 100%;
top: 0;
width: 0;
}
div.left
{
left: 0;
}
div.right
{
right: 0;
}
div.top, div.bottom
{
width: 100%;
left: 0;
height: 0;
}
div.top
{
top: 0;
}
div.bottom
{
bottom: 0;
}
The Script
var speed = 1000;
$clip = $("img.clip");
$clip.animate({
top: $clip.parent().height() / 2,
left: $clip.parent().width() / 2,
width: 0,
height: 0
}, {
duration: speed,
step: function(now, fx) {
switch (fx.prop) {
case "top":
$("div.top").css("height", now);
$("div.bottom").css("height", now + adjust);
break;
case "left":
$("div.left").css("width", now);
$("div.right").css("width", now + adjust);
}
},
complete: function() {
$(this).parent().addClass("image2");
$(this).animate({
top: 0,
left: 0,
width: $clip.parent().width(),
height: $clip.parent().height()
}, {
duration: speed,
step: function(now, fx) {
switch (fx.prop) {
case "top":
$("div.top").css("height", now);
$("div.bottom").css("height", now + adjust);
break;
case "left":
$("div.left").css("width", now);
$("div.right").css("width", now + adjust);
}
},
complete: function() {
$("div.imageContainer > *").removeAttr("style");
}
});
}
});
EDIT:
The CSS3 Solution
When cross-browser compatibility is less of a concern, CSS3 is an option (although I'd probably suggest seeing what can be done with the new HTML5 Canvas for this kind of animation). There are a couple things to note:
The image must be inside a container in order to allow us to clip toward its center rather than its top left corner.
The border-radius attribute will not clip the child images inside a container. For this reason, the image must become the background-image attribute of the container.
jQuery does not currently animate border-radius correctly. You can either replace the current jQuery animate functionality for that attribute or build a custom border-radius animation object to make jQuery more well-behaved. I have opted for the latter. Each corner's border-radius must be animated separately.
The animation in or out consists of two separate segments, and as a result the "linear" easing function is probably best used for cleanest results.
The method is commented inline below:
The Markup
<div class="imageContainer image1">
</div>
The CSS
div.imageContainer
{
background-position: 0px 0px;
background-repeat: no-repeat;
width: 300px;
height: 300px;
position: absolute;
top: 0;
left: 0;
}
div.image1
{
background-image: url(/images/myFirstImage.png);
}
div.image2
{
background-image: url(/images/mySecondImage.png);
}
The Script
// Total animation speed in or out will be speed * 1.5
var speed = 600;
// Store a reference to the object to be clipped
var $clip = $("div")
// A function to build a mapping object for border radius parameters
var buildRadiusObj = function(value) {
// Dimension an option object
var opts = {};
// Use specialized Mozilla CSS attributes when needed
var attributes = $.browser.mozilla ?
["-moz-border-radius-topleft",
"-moz-border-radius-bottomleft",
"-moz-border-radius-topright",
"-moz-border-radius-bottomright"] :
["border-top-left-radius",
"border-bottom-left-radius",
"border-top-right-radius",
"border-bottom-right-radius"];
// Build the option object
$.each(attributes, function(i, key) {
opts[key] = value;
});
// Return the result
return opts;
}
$clip.animate(buildRadiusObj($clip.width() * 0.5), { // Animate the border radius until circular
duration: speed * 0.5,
easing: "linear"
}).animate({ // Resize and reposition the container
width: 0,
left: $clip.width() / 2,
height: 0,
top: $clip.height() / 2
}, {
duration: speed,
easing: "linear",
step: function(now, fx) { // Synch up the background-position
if (fx.prop == "top") {
$(this).css("background-position", "-" + $(this).css("top") + " -" + $(this).css("left"));
}
},
complete: function() { // Swap the image
$(this).addClass("image2");
}
}).animate({ // Restore position and size
width: $clip.width(),
left: 0,
height: $clip.height(),
top: 0
}, {
duration: speed,
easing: "linear",
step: function(now, fx) { // Synch the background-position
if (fx.prop == "top") {
$(this).css("background-position", "-" + $(this).css("top") + " -" + $(this).css("left"));
}
},
complete: function() { // Remove inline styles but reapply border-radius
$(this).removeAttr("style").css(buildRadiusObj($clip.width() * 0.5));
}
}).animate(buildRadiusObj(0), { // Restore border-radius to block
duration: speed * 0.5,
easing: "linear",
complete: function() {
$(this).removeAttr("style"); // Remove inline styles
}
});
Again, the demo is located here.
I came this across, I hope it is interesting: http://www.netzgesta.de/transm/. The transition circles_out with one circle could do the job I think.
Here you go. http://jquery.malsup.com/cycle/ Check out the zoom. Something can be worked out with the circle part.
I tried some more and came up with the idea of using a <canvas> element.
Please see the result at: http://jsfiddle.net/3MG8e/2/.
var cv = $('canvas')[0];
var ctx = cv.getContext('2d');
ctx.fillStyle = 'black';
var int = null;
var t = -1;
var amount = 50;
var time = 1000;
var size = 0;
var im = new Image();
im.src = "http://burzak.com/proj/fxcanvas/docs/images/mario2.png";
im.onload = function() {
size = im.width;
int = setInterval(update, time / amount);
}
function update() {
if(++t >= amount) {
clearInterval(int);
}
ctx.fillRect(0, 0, cv.width, cv.height);
ctx.beginPath();
ctx.arc(size/2, size/2,
size/2 - t * (size/2) / amount,
0, Math.PI*2,
false);
ctx.clip();
ctx.drawImage(im, 0, 0, size, size);
}

Categories