Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
myradon

How-to apply different easing of animation normal and reverse

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Hey guys, I'm playing a bit with GSAP to use for my future website. Gonna drop Snapsvg and only use jQuery along with GSAP. I was wondering how I could ease an animation in reverse differently? So let's say anim.play() will use ease:Elastic.easeInOut and the reverse() would use eg. ease:power1.easeOut. Is this possible?

 

Here is a code-axample which I'm currently playing with;

var animateThumbs = function() {
	var $anchors 		= $( 'div.col-md-4' ).children( 'a' ),
		ellipse 		= $($anchors[0]).find( 'ellipse'),
		duration		= .25,
		ellipseProps	= {
			from: {
				cy: ellipse.attr('cy'),
				rx: ellipse.attr('rx'),
				ry: ellipse.attr('ry')
			},
			to: {
				cy: -500,
				rx: 625,
				ry: 725
			}
		};

	//Let's set on every a-element a timeline as method
		$anchors.each( function(index, elem) {

		var ellipse 	= $(elem).find( 'ellipse' ),
			animateSvgBG= new TimelineMax({repeat: 0, paused: true });

		animateSvgBG.fromTo(ellipse, duration,
				//from
				{ attr: { 
					cy: ellipseProps.from.cy,
					rx: ellipseProps.from.rx,
					ry: ellipseProps.from.ry
				}},
				//to
				{ attr: { 
					cy: ellipseProps.to.cy,
					rx: ellipseProps.to.rx,
					ry: ellipseProps.to.ry
				}}
			);
		//animateSvgBG.to(ellipse, 1, { attr: { cy: ellipseProps.from.cy, rx: ellipseProps.from.rx, ry: ellipseProps.from.ry }}, "fadeOut");

		//labelsArray = animateSvgBG.getLabelsArray();
		//console.info(labelsArray);
		elem.animate = animateSvgBG;
	})

	$anchors.on({
		mouseenter: function(event) {
			// from "fadeIn" to "fadeOut"
			//TweenMax.fromTo(animateSvgBG, duration, {time:labelsArray[0].time}, {time:labelsArray[1].time});
			//console.info( "animateSvgBg: " +  animateSvgBG, " labelsArray[0].time: " + labelsArray[0].time, " labelsArray[1].time: " + labelsArray[1].time );
			this.animate.play();
		},
		mouseleave: function(event) {													
			// nfrom "fadeOut" to "fadeIn"
			//TweenMax.fromTo(animateSvgBG, duration, {time:labelsArray[1].time, ease:Ease.easeOut}, {time:labelsArray[0].time}); 
			this.animate.reverse();
		}	
	})
}	

Share this post


Link to post
Share on other sites

Hi,

 

You're pretty close actually. What you can do is create all your instances with Linear.easeNone, then tween the time or progress property of the master timeline using a different easing function for each direction:

var $anchors = $( 'div.col-md-4' ).children( 'a' ),
    ellipse = $($anchors[0]).find( 'ellipse'),
    duration = .25,
    ellipseProps = {
		from: {
			cy: ellipse.attr('cy'),
			rx: ellipse.attr('rx'),
			ry: ellipse.attr('ry')
		      },
		to: {
			cy: -500,
			rx: 625,
			ry: 725
		    }
		};

//Let's set on every a-element a timeline as method
$anchors.each( function(index, elem) {
  var ellipse = $(elem).find( 'ellipse' ),
      animateSvgBG= new TimelineMax({repeat: 0, paused: true });
      animateSvgBG.fromTo(ellipse, duration,
      //from
      { attr: { 
	  cy: ellipseProps.from.cy,
  	  rx: ellipseProps.from.rx,
	  ry: ellipseProps.from.ry
	}},
      //to
      { attr: { 
	  cy: ellipseProps.to.cy,
	  rx: ellipseProps.to.rx,
	  ry: ellipseProps.to.ry
	}}
    );
  elem.animate = animateSvgBG;
})

$anchors.on({
  mouseenter: function(event) {
    TweenLite.to(this.animate, this.animate.duration(), {progress:1, ease:Back.easeOut});
  },
   mouseleave: function(event) {							    TweenLite.to(this.animate, this.animate.duration(), {progress:0, ease:Elastic.easeOut});
  }	
});

// you can also tween the time property
$anchors.on({
  mouseenter: function(event) {
    TweenLite.to(this.animate, this.animate.duration(), {time:this.animate.duration(), ease:Back.easeOut});
  },
   mouseleave: function(event) {							    TweenLite.to(this.animate, this.animate.duration(), {time:0, ease:Elastic.easeOut});
  }	
});

You can see it here:

 

See the Pen Ddoki by rhernando (@rhernando) on CodePen

 

Rodrigo.

  • Like 2

Share this post


Link to post
Share on other sites

Aha! Thanx! This library is sooo neat! As a designer it's like in the candystore. Awesome

 

  1. So the 'progress: 1' means normal direction of timeline and 'progress: 0' is reversed. I couldn't find it in the documentation under TweenLite/Max.
  2. You also wrote{time:0, ease:Back.EaseOut}. When I check the docs it states writing like Back.easeInOut or BackInOut. The first one causes wierd glitching of animation and the second just throws an error. Could you explain this?
  3. I also tried the easing of time. but it doesn't seem to have any effect. All the easingsa look the same ; TweenMax.to(this.animate, duration, {time:this.animate.duration(), ease:Quint.EaseOut});

Share this post


Link to post
Share on other sites

So the 'progress: 1' means normal direction of timeline and 'progress: 0' is reversed. I couldn't find it in the documentation under TweenLite/Max.

 

Actually progress is a setter or getter of the current position of the playhead, not it's direction, that's given by either play(forward) or reverse(backwards). It measures the advance state in a percentage basis from zero to one, being one => 100%, 0.5 => 50% and so on. You can see the API reference about progress:

 

http://api.greensock.com/js/com/greensock/core/Animation.html#progress()

 

 

 

You also wrote{time:0, ease:Back.EaseOut}. When I check the docs it states writing like Back.easeInOut or BackInOut. The first one causes wierd glitching of animation and the second just throws an error. Could you explain this?

 

Ah yes I was holding the shift key, is lower case, ease:Back.easeOut, I corrected my previous reply. As for the weird glitch, that's because the easing function is taking the time property back to 0, and when it reaches 0 it keeps going backwards generating a negative time property (which basically means that Jack just disrupted the SpaceTime continuum, call Stephen Hawking right now!! ;)). When that happens the tween/timeline goes to it's end and keeps going backwards until the ease function ends, then it goes back to 0. In the codepen you can see it, when you take the mouse out of the Time button after the red box gets to the left, the three boxes appears to the right of the container and the green one slides a bit to the left and finally the three boxes rest at the left of the container.

 

 

 

I also tried the easing of time. but it doesn't seem to have any effect. All the easingsa look the same ; TweenMax.to(this.animate, duration, {time:this.animate.duration(), ease:Quint.EaseOut});

 

Yep, that's basically the same syntax issue I had in my first reply, just change it to this:

TweenMax.to(this.animate, duration, {time:this.animate.duration(), ease:Quint.easeOut});

Check the easing package docs in order to get a better grasp of all the possibilities. Also I'd recommend using the Power syntax, is simpler and more intuitive, bigger the number stronger the effect on the tween.

 

http://api.greensock.com/js/com/greensock/easing/package-detail.html

 

Rodrigo.

  • Like 1

Share this post


Link to post
Share on other sites

haha :mrgreen: 

 

I checked my animations and it didn't feel like a natural bounce or elastic and I gave it a thought. Then it became clear to me that easing the time will not influence overshooting of the attributes in the timeline.  So basically what I want the animating ellipse look like it's bouncing in the ground it will get compressed. For animation this means the 'ry' will let's say go from 200 to 600 then 575 then 595 and so on. For the reverse direction I don't want this because the ellipse will disappear from the SVG-canvas (negative cy-coordinate), so any complex animation is a waste of CPU-power

 

Is this still possible with one timeline?

Share this post


Link to post
Share on other sites

Thanks for the fiddles, very helpful.

 

The core issue is that when you reverse() a tween or timeline it just plays that animation backwards respecting all the timing/easing that is built into that animation.

 

If you were to apply a different ease you would get some really odd results and jumps especially if the user did mouseenter/leave operations while your animation was still running.

 

Lets suppose you had an animation that moved the css left of an object from 0 to 600.

Suppose playing forward an ease of Linear.easeNone was applied. If that animation stopped halfway through  at progress(0.5) the left value would be 300.

 

If you then somehow tried to reverse that same animation but with a different ease, the object would have to jump to a new position. See how this basic CodePen demo that applies progress(0.5) on two similar tweens has drastically different results based on the ease:

 

http://codepen.io/GreenSock/pen/nwbIi

 

---

 

In most cases, Rodrigos suggestion to start with Linear eases on all animations and then tween the time() or progress() of that animation with a custom ease works beautifully. As you realized though, using Elastic or Back eases where you overshoot the maximum value or go under the minimum values causes things to get a little messy. 

 

Also with TimelineMax's tweenTo() method its very easy to apply an ease to an entire timeline in any direction to any time, but again, Back and Elastic eases won't give good results if time is less than 0 or greater than the duration of the timeline.

 

As illustrated in your snapSVG demo above, the best way to apply unique eases (especially using Back and Elastic) to an animation on mouseenter/leave is to simply create new animations in response to these mouseevents. 

  • Like 2

Share this post


Link to post
Share on other sites

Hi Carl, Thanks for your reply. So basically less is more. you mean just code it like;

$anchors.each( function(index, elem) {
   elem.ellipse = $(elem).find( 'ellipse' );
});			

$anchors.on({
   mouseenter: function(event) {
      TweenLite.to(this.ellipse, duration, { attr: { cy: ellipseProps.to.cy, rx: ellipseProps.to.rx, ry: ellipseProps.to.ry }, ease:Elastic.easeOut });
   },
   mouseleave: function(event) {
      TweenLite.to(this.ellipse, duration / 3, { attr: { cy: ellipseProps.from.cy, rx: ellipseProps.from.rx, ry: ellipseProps.from.ry }, ease:Back.easeIn });
   }	
});

The above code works like a charm.

Share this post


Link to post
Share on other sites

Exactly. Glad it works for you.

Share this post


Link to post
Share on other sites

How about performance? I was thinking when assiging functions as methods to the jQuery-object in a loop it will have a smoother experience because not everything has to be done on the actual event.

var moveDown = function(elem) {
   var moveDown = new TimelineMax({repeat: 0, paused: true, overwrite: "all"});
   moveDown.to(elem, duration,
   //to
   { attr: { 
      cy: ellipseProps.to.cy,
      rx: ellipseProps.to.rx,
      ry: ellipseProps.to.ry
      }, ease:Elastic.easeOut }
   );
   return moveDown;
}

var moveUp = function(elem) {
   var moveUp  = new TimelineMax({repeat: 0, paused: true, overwrite: "all"});
   moveUp.to(elem, duration,
   //from
   { attr: {
      cy: ellipseProps.from.cy,
      rx: ellipseProps.from.rx,
      ry: ellipseProps.from.ry
      }, ease:Back.easeIn }
   );
   moveUp.timeScale(3);
   return moveUp;
}

$anchors.each( function(index, elem) {
   elem.ellipse 	= $(elem).find( 'ellipse' );
   elem.moveDown 	= moveDown(elem.ellipse);	
   elem.moveUp = moveUp(elem.ellipse);
});

$anchors.on({
   mouseenter: function(event) {
      this.moveDown.play();
      this.moveUp.progress(0).pause();
   },
   mouseleave: function(event) {
      this.moveUp.play();
      this.moveDown.progress(0).pause();
   }	
}); 

I thought it would be better with performance in mind but it doesn't behave as I would expect. so I gave up.

Share this post


Link to post
Share on other sites

On some level, yes, creating the tweens in advance might be better but I really wouldn't worry about it. The overhead for creating a tween or timeline of tweens is very small. I doubt you would ever notice the difference.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 


  • Recently Browsing   0 members

    No registered users viewing this page.

×