I am new to Angular and have tried to set up smoothScroll: https://github.com/kavil/ng2SmoothScroll
I have a view set up like this:
<piiick-one smoothScroll offset="10" [scrollIf]="step === 1"></piiick-one>
<piiick-two *ngIf="max >= 2" smoothScroll offset="10" [scrollIf]="step === 2"></piiick-two>
<piiick-three *ngIf="max >= 3" smoothScroll offset="10" [scrollIf]="step === 3"></piiick-three>
<piiick-results *ngIf="max >= 4" smoothScroll offset="10" [scrollIf]="step === 4"></piiick-results>
And the component is set up like this:
export class StepsComponent implements DoCheck {
step: number = 0;
max: number = 0;
private path: string;
constructor(
private route: ActivatedRoute
) {
this.route.params.subscribe(params => this.path = params.path)
}
ngDoCheck() {
switch (this.path) {
case 'one': this.changeStep(1); break;
case 'two': this.changeStep(2); break;
case 'three': this.changeStep(3); break;
case 'results': this.changeStep(4); break;
default: this.changeStep(0); break;
}
}
private changeStep(step) {
var current = this.step;
this.step = step;
if (step > current) {
this.max = step;
}
}
}
what should happen, is that when route /steps/three
is called, it will smoothScroll to the div with the id is equal to three.
This was working, until I turned on page transition animations.
I now have this code in my app.component
:
import { Component } from '@angular/core';
import { query, style, animate, trigger, transition, group } from '@angular/animations';
const routerTransition = trigger('routerTransition', [
transition('* => home', [
query(':enter, :leave', style({ position: 'fixed', width:'100%' })
, { optional: true }),
group([
query(':enter', [
style({ transform: 'translateX(-100%)' }),
animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))
], { optional: true }),
query(':leave', [
style({ transform: 'translateX(0%)' }),
animate('0.5s ease-in-out', style({ transform: 'translateX(100%)' }))
], { optional: true }),
])
]),
transition('* => steps', [
group([
query(':enter, :leave', style({ position: 'fixed', width:'100%' })
, { optional: true }),
query(':enter', [
style({ transform: 'translateX(100%)' }),
animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))
], { optional: true }),
query(':leave', [
style({ transform: 'translateX(0%)' }),
animate('0.5s ease-in-out', style({ transform: 'translateX(-100%)' }))
], { optional: true }),
])
])
])
@Component({
selector: 'piiick-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
animations: [routerTransition]
})
export class AppComponent {
prepareRouteTransition(outlet) {
return outlet.activatedRouteData.state;
}
}
when animations
is commented out, the smoothScroll works; but when it is enabled, smoothScroll does not fire.
Does anyone know why?
I think I found the answer to this. If found a post where someone was showing how you can fire events after an animation had completed. So I created an AnimationService like this:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class AnimationService {
private animatingSubject = new Subject<boolean>();
animating = this.animatingSubject.asObservable();
constructor() { }
loading(state: boolean) {
this.animatingSubject.next(state);
}
}
which is used to set the animation state. I created an Animations component, which was simply like this:
<div [@routerTransition]="prepareRouteTransition(outlet)" (@routerTransition.start)="handleStart()" (@routerTransition.done)="handleDone()">
<router-outlet #outlet="outlet"></router-outlet>
</div>
and this was put in my App.Component like this:
<piiick-header></piiick-header>
<piiick-animations></piiick-animations>
<piiick-spinner></piiick-spinner>
The component code looks like this:
import { Component } from '@angular/core';
import { query, style, animate, trigger, transition, group } from '@angular/animations';
import { AnimationService } from '../../core/services/animation.service';
const routerTransition = trigger('routerTransition', [
transition('* => home', [
query(':enter, :leave', style({ position: 'fixed', width:'100%' })
, { optional: true }),
group([
query(':enter', [
style({ transform: 'translateX(-100%)' }),
animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))
], { optional: true }),
query(':leave', [
style({ transform: 'translateX(0%)' }),
animate('0.5s ease-in-out', style({ transform: 'translateX(100%)' }))
], { optional: true }),
])
]),
transition('* => steps', [
group([
query(':enter, :leave', style({ position: 'fixed', width:'100%' })
, { optional: true }),
query(':enter', [
style({ transform: 'translateX(100%)' }),
animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))
], { optional: true }),
query(':leave', [
style({ transform: 'translateX(0%)' }),
animate('0.5s ease-in-out', style({ transform: 'translateX(-100%)' }))
], { optional: true }),
])
])
])
@Component({
selector: 'piiick-animations',
templateUrl: './animations.component.html',
styleUrls: ['./animations.component.scss'],
animations: [routerTransition]
})
export class AnimationsComponent {
constructor(private animationService: AnimationService) { }
prepareRouteTransition(outlet) {
return outlet.activatedRouteData.state;
}
handleStart(e) {
this.animationService.loading(true);
}
handleDone(e) {
this.animationService.loading(false);
}
}
As you can see, when the animation starts, it updates the AnimationService and sets the animation property to true, when it completes the animation, it just update the service again and set the property to false.
Now was just getting it to work with the smoothScroll. At first I could not get this to work, then I figured that it was not just the animation I had to wait for, but the content too; so I added a property to the StepsComponent which was updated after the view has initialized.
I updated the HTML to this:
<div *ngIf="scrollIf">
<piiick-one smoothScroll offset="10" [scrollIf]="step === 1"></piiick-one>
<piiick-two *ngIf="max >= 2" smoothScroll offset="10" [scrollIf]="step === 2"></piiick-two>
<piiick-three *ngIf="max >= 3" smoothScroll offset="10" [scrollIf]="step === 3"></piiick-three>
<piiick-results *ngIf="max >= 4" smoothScroll offset="10" [scrollIf]="step === 4"></piiick-results>
<piiick-navigation></piiick-navigation>
</div>
and then changed the code to this:
export class StepsComponent implements DoCheck, AfterViewInit {
step: number = 0;
max: number = 0;
scrollIf: boolean = false;
private path: string;
private afterViewInit: boolean;
private viewAnimating: boolean;
constructor(
private route: ActivatedRoute,
private animationService: AnimationService
) {
this.route.params.subscribe(params => this.path = params.path);
this.animationService.animating.subscribe(animating => this.viewAnimating = animating);
}
ngAfterViewInit() {
this.afterViewInit = true;
}
ngDoCheck() {
if (this.afterViewInit) {
switch (this.path) {
case 'one': this.changeStep(1); break;
case 'two': this.changeStep(2); break;
case 'three': this.changeStep(3); break;
case 'results': this.changeStep(4); break;
default: this.changeStep(0); break;
}
this.scrollIf = this.afterViewInit && !this.viewAnimating;
}
}
private changeStep(step) {
var current = this.step;
this.step = step;
if (step > current) {
this.max = step;
}
}
}
so now the animation and the smoothScroll work. The only issue I have with this way around, is that the view is not shown until it has initialized. I am not sure if that is a problem yet.