Jump to content
GreenSock

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

GSAP with angular directives

Recommended Posts

I've been fighting a problem for 4 days now. Using ScrollMagic with GSAP to trigger animatons (rather than tying the animation to the scroll), and I cannot get it to consistently fire tweens. I can't figure out if the way scope works in Angular is messing up the scope in TweenMax, or if having two separate timelines targeting the same DOM element is overwriting badly, but I officially need some help. I know you guys aren't super Angular geniuses but I thought maybe you would know how the scope works in TweenMax better than I do.

 

I have two different codepens set up.

 

One without Angular that mostly works:

See the Pen bpomXL by famicomboy (@famicomboy) on CodePen

 

And one set up in Angular that more closely resembles how my code is set up that doesn't work at all:

 

As you scroll down, you should see a red box fade in, then a blue, then red and blue fade out. If you mess with the non-angular demo enough, it will eventually mess it up. But at the very least it calls the timelines correctly.

 

Anyway, I'm unsure what other details you'd need but if anyone wants to help me out, I'd greatly appreciate it/donate to buy beers if I can get any insight into what is failing to make this happen.

See the Pen EKwdBq by famicomboy (@famicomboy) on CodePen

Link to comment
Share on other sites

I hope you didn't spend 4 days messing around with that when all you needed to add was your CSS classes  :oops:

See the Pen aNLQGZ by osublake (@osublake) on CodePen

 

Does that qualify for beer donation?  :D

 

If not, here's a couple of tips. First off, don't use "data" for an element attribute as that might mess up some stuff since data has a specific purpose/meaning. Give your attribute a meaningful name, like scroll-trigger. Angular will automatically camelCase it for you, so in your directive you would register it like...

scope: { scrollTrigger: "=" }

I don't know how to use ScrollMagic, but it looks like all these animations are state based, as in it's either on or off. For animations like that, it might be easier use animation modules with ngAnimate. The most common type animations are...

 

Enter/leave - adding or removing an element from the DOM

See the Pen 5d9d6fe688766b469f5d4db88ef069b2?editors=0010 by osublake (@osublake) on CodePen

 

Add/remove class - adding or removing a CSS class from an element

http://plnkr.co/edit/uuXXzy?p=preview

 

Or maybe a combination of both

http://plnkr.co/edit/cXBEwU?p=preview

 

To get ScrollMagic to trigger an animation, you're probably going to need to use $timeout. This post explains and shows why.

http://greensock.com/forums/topic/9443-gsap-angular-js/?p=42204

  • Like 1
Link to comment
Share on other sites

Thanks for the reply, OSUblake. I looked over your examples and tried out stuff like $timeout and $apply, and in my context, it didn't change anything. I get that you made those recommendations in the context of using ngAnimate. I use ngAnimate in other parts of the site, but it's only really used with pre-baked animation parameters, I couldn't figure out how to use it for the needs of this component. Some elements might have the same intro animation, but would need different outro animations. However, I'd be willing to try that approach if I absolutely cannot get my current approach working reliably today.

 

The first pen you posted (adding the box classes to the directive elements) doesn't work for me because I'm trying to animate elements inside the directive template, rather than the directive element itself.

 

I figured out that if I set a $timeout of about 2 seconds before I initialize my tweens/timelines, the tweens work pretty reliably. This tells me that the DOM elements are not ready at that point in time (in the link function). The biggest problem with that approach is that an arbitrary wait time like that because it's flimsy. It doesn't guarantee that those elements are ready at that point in time. It just happened to be the right amount of time to wait on my local version on my specific computer. I've looked everywhere and cannot find a reliable method of knowing when Angular directive template elements are actually ready on the page. The compile function wasn't it, and doing a scope.$watch to wait for the data to be available isn't it either.

 

A little more background: the site I'm developing is fairly large with lots of directives that all have their own templates and even nested ng-repeat templates inside that. Think of it like a slideshow. I have a directive for 'slideshow' and a number of nested directives inside that for 'slide'.

 

The slides are defined in an object in the parent controller. It has all sorts of data including copy, and animation data. This is so I can use a modular approach to the slide functionality but also customize intro and outro animations per-slide, defining my parameters, target, duration etc in the json.

'tweet-note-default': {
	timelineID: 'tweetNoteDefault',
	triggerHook: 0.01,
	duration: 0,
	type: 'triggerTween',
	indicator: false,


	timelines: {
		intro: [
			{
				action: 'fromTo',
				selector: '.note-wrapper',
				duration: 0.4,
				fromProps: {
					opacity: 0
				},
				toProps: {
					opacity: 1,
					ease: 'Sine.easeInOut',
					onStart: function() {
						console.log('note tween start');
					}
				}
			}
		],
		outro: [ ... ]
	},
},

I built out wrapper functionality to parse this and feed it into TimelineMax as well. This part works as far as I can tell. Maybe it would be possible to pass data like this on to an ngAnimate module and have it work that way instead?

Link to comment
Share on other sites

The main reason for using $timeout like I mentioned is just to let Angular know that there are changes and to prevent errors using $apply. You may not need it, but I always throw that out there when using third-party libraries like GSAP, jQuery, etc.

 

Now using $timeout for an actual timeout like you're doing, that's a different story. But I don't think the amount of time you're waiting has much bearing on when your stuff is going to be ready. It all has to do with computer cycles. Controllers are initialized first, then directives, then Angular runs some updates. After 2 computer cycles, everything should be ready to go. So in your directive, try running your initialization code inside a nested $timeout, which will defer it's execution 2 cycles.

$timeout(function() {
  $timeout(function() {
    // Initialize your stuff in here
  })
});

For creating predefined animations, you should check out this repo. 

https://github.com/ThomasBurleson/angularjs-gsTimelines

 

It was created by some members of the Angular team, so some of the code might be hard to follow, but they setup some pretty cool directives and services using GSAP. You might be able to use some of stuff they already created.

  • Like 2
Link to comment
Share on other sites

So after more testing, I realized the issue is more related to using ng-repeats inside of my directive templates, and I'm trying to target content that those repeats would build, and need some way to know when they're all done. I've seen a couple different solutions (usually around firing a function when you hit the last index). I'm still trying to figure out how to handle that in cases where I'm using multiple ng-repeat directives and need to fire when they all are ready.

Link to comment
Share on other sites

Yep, perhaps checking the $last property in the scope of the ng-repeat in the link function of the directive, change a boolean in the controller and set up some ng-class to evaluate that property in the controller's scope.

 

Although I remember adding a ng-repeat via a directive's template and not running into this issue you mention. Perhaps you could create a plunker (http://plnkr.co/) which offers a bit more flexibility than codepen for this type of issues, as you can create and add the different files and not run everything in a single JS code block.

  • Like 1
Link to comment
Share on other sites

My previous post explains when they are ready. Again, it's all based on cycles and has NOTHING to do with time. The reason your 2 second wait works is because something triggered Angular to run an update somewhere in that 2 second span, probably from some other Angular directive you are using. If that update doesn't happen, you could sit there all day long and it still wouldn't be ready.

 

After 1 cycle your directive/component is compiled. This is when you can start initializing stuff. To hook into ngAnimate and start an animation that way, you would need to wait 2 cycles. 

 

Look what happens when you uncomment the $timeout in your second example? It works.

http://plnkr.co/edit/XVlJqA2OADORO4aWBAWX?p=preview

  • Like 1
Link to comment
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
  • Recently Browsing   0 members

    • No registered users viewing this page.
×