PDA

View Full Version : Animate along path - pause-resume - on hover



robertvanroon
12-10-2011, 09:18 PM
Hi everyone @ DD,

What I've made is something, what I thought, would be very fundamental. A small circle animating along a big circle. (for the circle path I've used the plugin jquery.path) This was no problem and I was really satisfied with the result.

The next idea I had was to pause the small circle on hover. I've found a plugin for that too, jquery.pause

jquery.pause does pause the animation on hover, but on resume (hovering out) the animation starts all over from its starting point in stead of the current position. (the anim has 2 starting points one at the top and one at the bottom)

And that is where my days of puzzling began. I've done tutorials on queue and dequeue, but that isn't what this really is. I was thinking to adjust my anim to the queue function of jquery, but before I do that, I would like to ask you, if there maybe is a way to accomplish my first idea.

So something I thought would be simple has become very complicated for me.

Could you take a look for me and maybe help me in the right direction again?

You can find my anim here: circleanim2 -> robert (http://www.imagenursery.com/circleanim-test/circleanim2-robert.html)

Thx!

Have a nice weekend :)

Robert

jscheuer1
12-10-2011, 11:49 PM
I slowed it down a bit and modified the path plugin to get a real time reference to the degree position of the arc, then found a single arc was preferable. I didn't have to figure out which side of the circle I was on. I changed the settings a little so things lined up better to my eye, used the lowest possible atart and end numbers, and set the created new path.arc() object as a variable that I could access and manipulate for restarting. Gave up on the pause plugin. jQuery's own .stop() is all that's needed to halt an animation in place. To restart it from the same position at a duration proportional to the length of the arc remaining to be transited seemed to me to require custom code. I also made both circles triggers for the pause/resume on hover:


<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Circle-anim 3</title>
<style type="text/css" media="screen">

#circle-anim {
margin-top:-55px;
margin-left:-55px;
height:110px;
width:110px;
position:absolute;
left: 0;
top: 0;
background: transparent url(circle-anim.png) no-repeat left top;
}

#circle-container {
height: 600px;
width: 600px;
position: relative;
left: 250px;
top: 150px;
}
#jj-circle {
height: 400px;
width: 400px;
position:absolute;
background: transparent url(circle.gif) no-repeat left top;
margin: 0;
}
</style>

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<script type="text/javascript">

// * jQuery css bezier animation support -- Jonah Fox
// * version 0.0.1
// * Released under the MIT license.
// * Modified Dec 10 2011 for external reference to degree position of arcs - jscheuer1

// var path = $.path.bezier({
// start: {x:10, y:10, angle: 20, length: 0.3},
// end: {x:20, y:30, angle: -20, length: 0.2}
// })
// $("myobj").animate({path: path}, duration)

;(function($){

$.path = {}


var V = {
rotate: function(p, degrees) {
var radians = degrees * 3.141592654 / 180
var c = Math.cos(radians), s = Math.sin(radians)
return [c*p[0] - s*p[1], s*p[0] + c*p[1] ]
},
scale: function(p, n) {
return [n*p[0], n*p[1]]
},
add: function(a, b) {
return [a[0]+b[0], a[1]+b[1]]
},
minus: function(a, b) {
return [a[0]-b[0], a[1]-b[1]]
}
}

$.path.bezier = function( params ) {
params.start = $.extend({angle: 0, length: 0.3333}, params.start )
params.end = $.extend({angle: 0, length: 0.3333}, params.end )

this.p1 = [params.start.x, params.start.y];
this.p4 = [params.end.x, params.end.y];

var v14 = V.minus(this.p4, this.p1)
var v12 = V.scale(v14, params.start.length)
v12 = V.rotate(v12, params.start.angle)
this.p2 = V.add(this.p1, v12)

var v41 = V.scale(v14, -1)
var v43 = V.scale(v41, params.end.length)
v43 = V.rotate(v43, params.end.angle)
this.p3 = V.add(this.p4, v43)

this.f1 = function(t) { return (t*t*t); }
this.f2 = function(t) { return (3*t*t*(1-t)); }
this.f3 = function(t) { return (3*t*(1-t)*(1-t)); }
this.f4 = function(t) { return ((1-t)*(1-t)*(1-t)); }

/* p from 0 to 1 */
this.css = function(p) {
var f1 = this.f1(p), f2 = this.f2(p), f3 = this.f3(p), f4=this.f4(p)
var x = this.p1[0]*f1 + this.p2[0]*f2 +this.p3[0]*f3 + this.p4[0]*f4;
var y = this.p1[1]*f1 + this.p2[1]*f2 +this.p3[1]*f3 + this.p4[1]*f4;
return {top: y + "px", left: x + "px"}
}
}

$.path.arc = function(params) {
for(var i in params)
this[i] = params[i]

this.dir = this.dir || 1

while(this.start > this.end && this.dir > 0)
this.start -= 360

while(this.start < this.end && this.dir < 0)
this.start += 360


this.css = function(p) {
var a = this.start * p + this.end * (1 - p)
this.degs = a; // create reference to degree position of arc for external use - jscheuer1
a = a * 3.1415927 / 180 // to radians
var x = Math.sin(a) * this.radius + this.center[0]
var y = Math.cos(a) * this.radius + this.center[1]
return {top: y + "px", left: x + "px"}
}

};


$.fx.step.path = function(fx){
var css = fx.end.css(1 - fx.pos)
for(var i in css)
fx.elem.style[i] = css[i];
}
})(jQuery);

</script>
<script>

jQuery(function($){
var $acircle = $('#circle-anim'), rate = 12000, darc = new $.path.arc({
center : [458, 358],
radius : 186,
start : 179,
end : 180,
dir : -1
}),
totaldeg = darc.start - darc.end, ostart = darc.start;
function fullarc(){
$acircle.animate({
path : darc
}, {
duration: rate,
easing : 'linear',
complete: fullarc
});
}

fullarc();

$acircle.add('#jj-circle').hover(function(){
$acircle.stop();
}, function() {
$acircle.animate({
path : $.extend(darc, {start: darc.degs})
}, (darc.degs - darc.end) * rate / totaldeg, 'linear', function(){darc.start = ostart; fullarc();});
}
);
});

</script>
</head>

<body>
<div id="content">
<div id="circle-container">
<div id="jj-circle"> </div>
</div>
<div id="circle-anim" ></div>
</div>
</body>
</html>

Note:

I put the path plugin right on the page, it may be made external again. All script code can go in the head as I have done in the above. If you want to speed it up again, there's just one place to edit that:


jQuery(function($){
var $acircle = $('#circle-anim'), rate = 12000, darc = new $.path.arc({
center : [458, 358],
radius : 186,
start : 1 . . .

I initially went from 3000 to 6000, which is really the same speed (2 arcs at 3000 @ traversing half the distance combined into one arc traversing all of it at 6000 are the same). I doubled that to 12000 because the small circle was, in my opinion moving so fast that it was losing its shape to an unacceptable degree.

robertvanroon
12-11-2011, 09:34 AM
Hi John,

You did it :cool:

I also understand how you managed to do it. (not yet all the details, but globally) Thanks for your clear explanation.

I've put it online: circleanim 3 -> john (http://www.imagenursery.com/circleanim-test/circleanim3-john.html)

;) Yes it was going a bit to fast, I agree with you. It wasn't that important how fast it was going at that moment, because I didn't get it to function as I wanted it to function. As we say in Dutch: "You've placed the dot(point) on the i." :D

Very nice that you trigger on hover for both circles.

Thanks for your help, I really got stuck. Now I am going to see if I can make any additional functionalities.

Thanks!

(I've just bought you a virtual cup of coffee through paypal :D )

Robert

jscheuer1
12-11-2011, 01:31 PM
Thanks.

Two refinements. These are to style, and something to make things a little clearer. The refinements - I found that sometimes you could see part of the small circle in the upper left corner before things got underway. To fix that, in the style:


#circle-anim {
margin-top: -55px;
margin-left: -55px;
height: 110px;
width: 110px;
position: absolute;
left: -1000px;
top: -1000px;
background: transparent url(circle-anim.png) no-repeat left top;
}

That way it starts out completely off the screen.

And I changed the dimensions on #circle-container to 400 x 400. That way there's less likely to be a vertical scrollbar. Otherwise there's no effect. But it just now occurs to me that we might not need #jj-circle, just give it's background to #circle-container. Right, just tried it, seems fine.

The other thing I did doesn't change anything, but I broke out the second .animate() call like the first for clarity, replacing:


$acircle.add('#jj-circle').hover(function(){
$acircle.stop();
}, function() {
$acircle.animate({
path : $.extend(darc, {start: darc.degs})
}, (darc.degs - darc.end) * rate / totaldeg, 'linear', function(){darc.start = ostart; fullarc();});
}
);

with (now changed slightly because I just got rid of #jj-circle):


$acircle.add('#circle-container').hover(function(){
$acircle.stop();
}, function() {
$acircle.animate({
path : $.extend(darc, {start: darc.degs})
}, {
duration: (darc.degs - darc.end) * rate / totaldeg,
easing : 'linear',
complete: function(){
darc.start = ostart;
fullarc();
}
});
}
);

robertvanroon
12-11-2011, 10:18 PM
:)

I've updated the page with your refinements.

(and added one myself too :) a cosmetic enhancement, only thing is that I'll have to preload the image somehow, because now it gives a hickup on load time, will fix that later)

You can check it here (http://www.imagenursery.com/circleanim-test/circleanim3-refine.html)

I'll study your code a bit more and I'll be back with some questions shortly.

Robert

jscheuer1
12-12-2011, 01:58 AM
Cool. I was playing around with it some more and this is my latest version:

http://home.comcast.net/~jscheuer1/circle/

I optimized the large circle image (knocked off a few thousand bytes from it) and made a new small one (also less bytes than it's counterpart - I'm learning The Gimp) with a partially transparent center so you can see the arc of the large circle beneath it.

But those are just cosmetic and loading time things. I also added IE's proprietary alpha image loader filter for IE less than 9 so that all IE (6+) can enjoy this effect. I know IE 7 and up can do alpha channel .png without the filter, but the animation was slow and choppy in it and 8 without the filter, so I figured, "Why not?"

I moved the circle-anim div inside the circle-container div. This necessitated an additional stop() in the hover:


$acircle.add('#circle-container').hover(function(){
$acircle.stop();
}, function() {
$acircle.stop();
$acircle.animate({
path : $.extend(darc, {start: darc.degs}) // darc.degs is from addition to the path plugin, where it was when it stopped
}, {
duration: (darc.degs - darc.end) * rate / totaldeg, // caculate remaining time to complete the arc at the same apparent speed
easing : 'linear',
complete: function(){
darc.start = ostart;
fullarc();
}
});
}
);

Otherwise the resume no longer always picked up where it had left off. Not sure exactly why. Has something to do with a mouseover of one is now also a mouseover of the other without necessarily a mouseout of either.

But this makes it so much easier to calculate what the center is. It's now the center of the circle-container or larger circle image, independent of where on the page it is.

Moved the css and path script off the page, and annotated the on page code a little more.

Think that covers it.

Had a thought with your latest published version, sure a preload would help:


<script>
(function($){
$(new Image()).attr('src', 'circle-anim-hover.png');
})(jQuery);

jQuery(function($){
var $acircle = $('#circle-anim'), rate = 12000, darc = new $.path.arc({
center : [458, 358],
radius : 186,
start : 179,
end : 180,
dir : -1
}),
totaldeg = darc.start - darc.end . . .

As would optimizing it. It's currently at about 12k, here it is at about 7k:

http://home.comcast.net/~jscheuer1/circle/circle-anim-hover.png

You could, if you like have it appear on hover of the larger circle:


$acircle.add('#circle-container').hover(function(){
$acircle.stop().css({backgroundImage: 'url(circle-anim-hover.png)'});
$(this).addClass('red');
}, function() {
$acircle.css({backgroundImage: 'url(circle-anim.png)'}).animate({
path : $.extend(darc, {start: darc.degs})
}, {
duration: (darc.degs - darc.end) * rate / totaldeg,
easing : 'linear',
complete: function(){
darc.start = ostart;
fullarc();
}
});
}
);

robertvanroon
12-13-2011, 01:04 AM
I think your transparent version is very neat :)

Making everything as compact as possible, is something I learned in the old days of webdesign, when bandwidth was limited. I still think it is something important, why using bandwidth when it isn't necessary. With a lot of people using mobile internet (most of the time data limits and ofcourse an accu) it has become very important again.

You made it center in a straightforward way, also cool :D (the centre was indeed hard to find.) I'll take a closer look at it tomorrow, I didn't have much time this evening.

Your tip about hover on the big circle and showing a css transition in the small circle made me think, I'll include it in my next version :) including the preloading.

I really have learned a lot!

Thanks,

Robert