Jump to content
Search Community

Dynamic element update

dmitryBz test
Moderator Tag

Go to solution Solved by dmitryBz,

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

Hello, friends!

 

I am working on Angular2 powered project and have some issue with tweening dynamic elements.

Everything works fine alone, but if i do parallel animation of a couple elements - something brakes: inner text of tweened div is not updating until some user-event, like click/touch/mousewheel. The same for css transitioned elements, that dont used by any gsap tools.

 

The problem is not stable on desktop (chrome/opera), as i think, it starts to bugging when i go to other tab, do something and go back to my tab.

But it stable in iOs Safari 8.3/10.2.

 

As for Angular side - i'm doing all along 'best practise':

  • all targets are element refs
  • all tweens are emitted after view init lifecycle hook and subscripiton on target (check that it is exist)

At this moment all animations are by TweenMax.

What can cause the problem? 

Link to comment
Share on other sites

Creating an example to reproduce the problem is very complicated in this case.

This thread was a cry from the heart after a few days trying to fix this problem.

 

Now i think i found the problem source.

 

Over the past few days i did a component elements refs checker, because i start tweening immidately after app loads, that looks like:

@ViewChild('projectsWrapper') projectsWrapper:ElementRef;
@ViewChildren('qprojectsWrapper') qprojectsWrapper: QueryList<any>;
ngAfterViewInit() {
	this.qprojectsWrapper.changes.subscribe((res:QueryList<any>) => {
	    console.log('VI: PROJECTS WRAPPER', this.projectsWrapper.nativeElement.children);
	    this.cmpCount();
	})
	//...same for each checked element		
};
cmpToLoad = 3; //ELEMENTS TO CHECK
cmpCount() {
	if(this.cmpToLoad > 1) {
	    this.cmpToLoad--;
	    console.log('PROJECTS-CMP: to load: ' + this.cmpToLoad);
	} else {
	    this.afterViewLoaded();
	}
}
afterViewLoaded() {
//do GSAP stuff
};

I did a few tests and found that it worked as i want, but NO.

 

Today i wrap afterViewLoaded() to setTimeout function with delay of 1s and everything start to works fine.

 

Now my question is how to correctly delay my GSAP tools to wait for app fully loads?

Link to comment
Share on other sites

You can wrap your custom GSAP code in the following to make sure the DOM and window are fully loaded and ready before animating anything

// wait until DOM is ready
document.addEventListener("DOMContentLoaded", function(event) {

     // wait until CSS stylesheets, images, JS scripts, links, and other external assets are loaded
     window.addEventListener("load", function(){
     
            // place custom code here
              
     }, false);

});

Also the GSAP equivalent to setTimeout() is delayedCall()

 

https://greensock.com/docs/#/HTML5/GSAP/TweenMax/delayedCall/

  • delayedCall()
    Provides a simple way to call a function after a set amount of time (or frames).
//calls myFunction after 1 second and passes 2 parameters:
TweenMax.delayedCall(1, myFunction, ["param1", "param2"]);

function myFunction(param1, param2) {
    //do stuff
}

Happy Tweening :)

  • Like 2
Link to comment
Share on other sites

You're right, but:

  1. Adding event listener to component is not an 'Angular2 way'.
  2. I have a load bar here, that subscribed and animating by another component callbacks and don't want to wait untill DOM is loaded. I have to wait when only specific elements resolves.

.delayedCall() is fine, but just another implementation of setTimeout, that can't provide expected result for all possible cases. (e.g. slow device/slow connection...etc)

Link to comment
Share on other sites

My bad im sorry .. i didnt know you were using Angular2 .. im not sure what the equivalent would be.. ether way you are waiting for the element to be ready or resolved before animating them.

 

In the forums we have to stay more focused on answering questions on how to use the GSAP API.

 

I'm not sure what that equivalent in Angular2 is for waiting for elements to resolve.

  • Maybe you can check the Angular2 API documentation?
  • Maybe stackoverflow or someone else knows how to wait when only specific elements are resolved in Angular2?

I was just showing the equivalent to using setTimeout() since you mentioned it above. The GSAP delayedCall() is better option than using setTimeout() when using GSAP.

  • Like 2
Link to comment
Share on other sites

  • Maybe you can check the Angular2 API documentation?
  • Maybe stackoverflow or someone else knows how to wait when only specific elements are resolved in Angular2?

Yes, i did that, and then did that one more time before posting thread here  :-)

All my code is based on similar questions on stackoverflow.

But i can't decide - is my way correct or not...

 

Can dynamic property binding of Angular2 brake something in already tweened element? Because without setTimeout first tweens iteration are just fine (on page load), but next are done again with previous text binding (on changing slide and description in my project). After tweening - bindings are not updated until click/touch/mousewheel.

Link to comment
Share on other sites

Can dynamic property binding of Angular2 brake something in already tweened element? Because without setTimeout first tweens iteration are just fine (on page load), but next are done again with previous text binding (on changing slide and description in my project). After tweening - bindings are not updated until click/touch/mousewheel.

 

Maybe. It depends on what you're doing. I don't know how binding works in Angular 2, but in Angular 1 you would have to call $apply or $timeout before changes would be recognized. Not sure if there is an Angular 2 equivalent of that.

  • Like 2
Link to comment
Share on other sites

As logic, my tween's are very simple:

  1. var selectedIndex =0;
  2. Slide in img[selectedIndex] and text[selectedIndex]
  3. On mousewheel - fade out text, slide out current img
  4. onComplete: promise selectedIndex++, then slide In img/text[selectedIndex]..etc

As single tween everything working just fine. But all this complicated with some other tweens, routing change, callbacks..

 

 

You're right about $scope.$apply() in Angular 1.x, in Angular 2 zone.js do all this things for us.

 

You reminded me...callbacks... Is it normal to use it like this?

TweenMax.to(element, 0.5, { vars, onComplete: () => {this.callFunction()} });

I do that, because i cant call component functions iside the normal onComplete: function(), onCompleteParams: [{}]. This is because onComplete has a different scope, as i think. 

 

If it is normal, my second thought - what if my stucking variable is changing in different scope, that zone is not watching or get bugs over there? 

Link to comment
Share on other sites

You can use a callback like that as arrow functions aren't scoped, so 'this' will refer to your component. Or you can set a scope for your animation like this...

TweenLite.to(this.myElement, 1, {
  x: 400,
  onComplete: this.someMethod,
  callbackScope: this
});

See the Pen 94fcc417cfe8614b788eede2262950d1?editors=1010 by osublake (@osublake) on CodePen

 

.

  • Like 4
Link to comment
Share on other sites

If something is not being watched correctly, you could setup something like in this thread to detect and react to changes. The thread is about canvas, but I know other libraries like Vue.js use that approach with getters/setters for their data object.

https://greensock.com/forums/topic/16051-onupdate-on-timelinelite/?p=70446

 

Also, here's a good article about animating with Angular 2. There is some GreenSock stuff in there. It's focused on SVG and canvas, but the concept is still the same.

https://teropa.info/blog/2016/12/12/graphics-in-angular-2.html

 

.

  • Like 1
Link to comment
Share on other sites

OSUblake, i think, you made my night!

Last article talking about ngZone and perfomance of excluding something with ngZone.runOutsideAngular().

It got me thinking about ngAfterViewChecked hook, that I didn't used before.
In my posts i didn't say that i have a Draggable instance here, because i did a lot of test variations and thought that it not causes problems, I was wrong.. my bad..

Back to ngZone: When i create a draggable instance - afterViewChecked event fires in a loop! When you leave the browser tab - it stops and when you come back after cople of time - it doesn't. And it doesn't fire on tweens anymore.  I think that represent a part of my problem.

I removed draggable from my component and now all seems to work fine.
By the way, tweening fires viewChecked event too, i think, because of RequestAnimationFrame(), should i run all my tweens outside the Angular?

Now i leave to play with ngZone and gather back my test-scattered code.

Link to comment
Share on other sites

I noticed this in the comments. It's about another library, but is probably still applicable to GSAP.

 

What might be a little tricky is to get animations running outside the Angular Zone, as it doesn't look like you'll be in charge of actually invoking requestAnimationFrame here. It might be as easy as calling the animation method with runOutsideAngular but I'm not sure since I'm not familiar with the library.

 

There's really no way to manually tell GSAP to call requestAnimationFrame, which means your tweens and Draggable might be running inside a zone. That might be the reason your afterViewCheck event is constantly firing. But that's just a guess.

 

The person who asked that question has also been posting on this forum for help with Draggable and Angular 2. You might want to send him a PM to see if he knows what's going on.

 

born2net

 

I haven't spent a lot of time with Angular 2 because I've been waiting for them to properly support 3rd party animations. I saw this issue, but it looks like it hasn't been addressed yet.

https://github.com/angular/angular/issues/9668#issuecomment-229208736

.

  • Like 1
Link to comment
Share on other sites

  • Solution

OSUblake, dear, you saved my nerves by finding this article!

 

I am watching on this github issue from start using angular2. Animations..Few week's they said...

So I started to use greensock power again.

 

At this moment problem seems to be fixed. Running gsap tools outside the Angular works fine for me. No more tonns of viewUpdates and not updated bindings.

 

For persons, who have similar problems, i just leave it here:

import { ..., NgZone} from '@angular/core';

constructor(private ngZone: NgZone) {}

//Run things outside angular
this.ngZone.runOutsideAngular(() => {
  this.draggable(); // Draggable.create(..etc)
  this.myTweens();  // Tweenmax.to(..etc)
});

//Tell angular to fire view check (in somewhere outside function)
this.ngZone.run(()=>{
  this.someVar = true; //change var and tell angular to update it
});

That look's not good, but as OSUblake noticed- at this time angular2 not properly supports 3rd party animations.

Thank you all for answers, especially OSUblake.

  • Like 2
Link to comment
Share on other sites

Nice! This is really good stuff. I had no idea running GSAP inside a zone did that, but I'm glad you figured it out. I'll be sure to let people know about this finding the next time somebody asks about doing animations with Angular.

 

But be carefull with that kind of help. I am still not sure that my implementation is a good practise.

I can't now test it on other setups of Angular (At this time i use angular-cli@1.0.0-rc.0, angular@2.4.8, typescript 2.2.1), but in plunker (Angular 2.4.1) i cant reproduce Draggable viewCheck loop. In clean angular-cli app (from my setup) problem exist.

Link to comment
Share on other sites

Locally now i am importing gsap to build:

import { TweenMax, TimelineMax } from 'gsap';import Draggable from 'gsap/Draggable';
It is another problem, that i resolve by updating to typescript 2.2.1. New typescript fixes the missing imports. (Before i was importing with require). But my thread-main problem was in both ways.

 

I made a repo with clean test app, that reproduces the afterViewUpdate problem.

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.
×
×
  • Create New...