简体   繁体   中英

Angular TimelineLite time function doesn't work with OnInit

I try to implement a progress bar and a timer using TimelineLite:

HTML:

<div id="progress"></div>
{{timeline.time()}}

CSS:

#progress {
  width: 100%;
  height: 30px;
  background-color: red;
}

在此处输入图片说明

Here's what I want:
When the page loads, the progress bar starts animating from 100% to 0% width (10 sec. duration). Also, I want to show the result of the time function.
Here's what I did:

public timeline = new TimelineLite();

ngOnInit() {
  this.start();
}

start() {
  const progress = document.getElementById('progress');
  this.timeline.fromTo(progress, 10, {width: '100%'}, {width: 0, ease: Linear.easeNone});
}

So when the page loads, the progress bar works but the timer doesn't. I don't know why. If I try to set a timeout for 3 seconds at ngOnInit , it works:

ngOnInit() {
  setTimeout(() => this.start(), 3000);
}

Also if I create a button than invokes start function at click, it works:

<button (click)="start()">Start</button>

So the problem is that the timer doesn't work if I try to invoke fromTo function from ngOnInit .

Hi you should use @ViewChild to target your elements and use AfterViewInit lifeCycleHook when you want to manipulate | access to your DOMElement (current component or child component).

export class AppComponent implements AfterViewInit {

  @ViewChild('progress') progressElement: ElementRef;

  constructor(private changeRef: ChangeDetectorRef) { } 

  public timeline = new TimelineLite();
  ngAfterViewInit() {
    this.start();
  }

  start() {
    this.timeline.eventCallback('onUpdate', () => {
      this.time = this.timeline.time();
      this.changeRef.detectChanges();
    }, [] );

    // Here i use the ViewChild Binding instead of pure javascript fetching.
    this.timeline.fromTo(this.progressElement.nativeElement, 10, {width: '100%'}, {width: 0, ease: Linear.easeNone});
  }
}

To facilitate your testing i have does Github Pull request . Anyone which read your original question can easily see the difference.


Update 1 : on your view, you call a function which return the current timer from TimelineLite. Because is just function call, it will not be called again and again on each internal TimelineLite update.

To do it, you have to use TimelineLite event binding .

Now, by default your view will not be "updated" each time you update your attribute from following code :

this.timeline.eventCallback('onUpdate', () => {
  // angular is not inform by default than something happen here.
 }, [] );

is why, you should use ChangeDetectorRef to force angular to detect the freshest updated component attribute.

pull requested updated

This is evidently a case of timeline of calling lifecycle methods of angular. Highly likely the problem is in onInit where you don't get DOM element progress immediately. You have made to it to work applying settimeout other way is to keep an setinterval until you find progress .
Seemingly the best way is to write this.start() in ngAfterViewInit() instead of ngOnInit()

Probably, it happens because timeout starts after the template initialized and then the code runs, outside OnInit. I am not sure though.

You may want to use zone.run() here.

Here is a link, which may turn out to be helpful for you: https://medium.com/@MertzAlertz/what-the-hell-is-zone-js-and-why-is-it-in-my-angular-2-6ff28bcf943e

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM