Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ionic 4 Hide Toolbar on scroll

I'm trying to implement a directive to hide the Toolbar while scrolling. I tried using this tutorial: https://medium.com/@gregor.srdic/ionic3-hidding-header-on-footer-on-content-scroll-15ab95b05dc5

This worked with Ionic 3, but doesn't work with Ionic 4.

On the following code I get the error:

private adjustElementOnScroll(ev) {
    if (ev) {
        console.log(ev);
        ev.domWrite(() => {
            let scrollTop: number = ev.scrollTop > 0 ? ev.scrollTop : 0;
            let scrolldiff: number = scrollTop - this.lastScrollPosition;
            this.lastScrollPosition = scrollTop;
            let newValue = this.lastValue + scrolldiff;
            newValue = Math.max(0, Math.min(newValue, this.config.maxValue));
            this.renderer.setStyle(this.element.nativeElement, this.config.cssProperty, `-${newValue}px`);
            this.lastValue = newValue;
        });
    }
}

Error:

ev.domWrite is not a function

I checked, and ev in Ionic 4 is a CustomEvent, not a ScrollEvent.

Any suggestions?

like image 587
amitairos Avatar asked Oct 10 '18 16:10

amitairos


People also ask

How do I hide the header in ionic?

Simply there is the attribute hide-nav-bar set to "true" on <ion-nav-view> element and on each <ion-view> .

When placed within ion content toolbars will scroll with the content?

When a toolbar is placed in an <ion-header> it will appear fixed at the top of the content, and when it is in an <ion-footer> it will appear fixed at the bottom. Fullscreen content will scroll behind a toolbar in a header or footer.

How do I remove shadow from Ion header?

Add an option / attribute to the ion-header / ion-toolbar to remove the shadow for a full flat header which can be combined with the page content (not line / shadow).


3 Answers

Above solutions no longer works. Ionic 4 beta APIs has changed a lot in recent months.

you have to import IonContent instead of Content.

//scroll-hide.directive.ts
import { IonContent, DomController } from '@ionic/angular';
import { Directive, ElementRef, Input, Renderer2, SimpleChanges } from '@angular/core';

import the directive where you want to use it, instead of app.module.ts

for example in relevant module,

//ex: home.module.ts
import { ScrollHideDirective } from '../../directives/scroll-hide.directive';

@NgModule({
    ...
  declarations: [...,ScrollHideDirective],
    ...
})


then in the ts file,


//ex: home.page.ts
import { ScrollHideConfig } from '../../directives/scroll-hide.directive';


export class HomePage implements OnInit {

...
    footerScrollConfig: ScrollHideConfig = { cssProperty: 'margin-bottom', maxValue: undefined };
    headerScrollConfig: ScrollHideConfig = { cssProperty: 'margin-top', maxValue: 54 };
...

}

reversed version of directive

//ex: scroll-hide.directive.ts    

import { IonContent, DomController } from '@ionic/angular';
import { Directive, ElementRef, Input, Renderer2, SimpleChanges } from '@angular/core';

    @Directive({
      selector: '[scrollHide]'
      })
export class ScrollHideDirective {

  @Input('scrollHide') config: ScrollHideConfig;
  @Input('scrollContent') scrollContent: IonContent;

  contentHeight: number;
  scrollHeight: number;
  lastScrollPosition: number;
  lastValue: number = 0;

  constructor(private element: ElementRef, private renderer: Renderer2, private  domCtrl: DomController) {
  }

  ngOnChanges(changes: SimpleChanges) {
    if(this.scrollContent && this.config) {
      this.scrollContent.scrollEvents = true;

      let scrollStartFunc = async (ev) => {
        const el = await this.scrollContent.getScrollElement();
        this.contentHeight = el.offsetHeight;
        this.scrollHeight = el.scrollHeight;
        if (this.config.maxValue === undefined) {
          this.config.maxValue = this.element.nativeElement.offsetHeight;
        }
        this.lastScrollPosition = el.scrollTop;
      };

      if(this.scrollContent && this.scrollContent instanceof IonContent) {
        this.scrollContent.ionScrollStart.subscribe(scrollStartFunc);
        this.scrollContent.ionScroll.subscribe(async (ev) => this.adjustElementOnScroll(ev));
        this.scrollContent.ionScrollEnd.subscribe(async (ev) => this.adjustElementOnScroll(ev));

      } else if(this.scrollContent instanceof HTMLElement) {
        (this.scrollContent as HTMLElement).addEventListener('ionScrollStart', scrollStartFunc);
        (this.scrollContent as HTMLElement).addEventListener('ionScroll',async (ev) => this.adjustElementOnScroll(ev));
        (this.scrollContent as HTMLElement).addEventListener('ionScrollEnd',async (ev) => this.adjustElementOnScroll(ev));
      }
    }
  }

  private adjustElementOnScroll(ev) {
    if (ev) {
      this.domCtrl.write(async () => {
        const el = await this.scrollContent.getScrollElement();
        let scrollTop: number = el.scrollTop > 0 ? el.scrollTop : 0;
        let scrolldiff: number = scrollTop - this.lastScrollPosition;
        this.lastScrollPosition = scrollTop;
        let newValue = this.lastValue + scrolldiff;
        newValue = Math.max(0, Math.min(newValue, this.config.maxValue));
        this.renderer.setStyle(this.element.nativeElement, this.config.cssProperty, `-${newValue}px`);
        this.lastValue = newValue;
      });
    }
  }
}

export interface ScrollHideConfig {
  cssProperty: string;
  maxValue: number;
}

the html ex: page.html (No Change)

<ion-header [scrollHide]="headerScrollConfig" [scrollContent]="pageContent">
  ...
</ion-header>

<ion-content #pageContent fullscreen>
  ...
</ion-content>

<ion-footer [scrollHide]="footerScrollConfig" [scrollContent]="pageContent">
...
</ion-footer>

hope it helps.

like image 88
Sankha Karunasekara Avatar answered Oct 08 '22 05:10

Sankha Karunasekara


Modified directive for Ionic 4.

import { Content, DomController } from '@ionic/angular';
import { Directive, ElementRef, Input, Renderer2, SimpleChanges } from '@angular/core';

@Directive({
    selector: '[scrollHide]'
})
export class ScrollHideDirective {

    @Input('scrollHide') config: ScrollHideConfig;
    @Input('scrollContent') scrollContent: Content;

    contentHeight: number;
    scrollHeight: number;
    lastScrollPosition: number;
    lastValue: number = 0;

    constructor(private element: ElementRef, private renderer: Renderer2, private domCtrl: DomController) {
    }

    ngOnChanges(changes: SimpleChanges) {
        if (this.scrollContent && this.config) {
            this.scrollContent.ionScrollStart.subscribe(async (ev) => {
                const el = await this.scrollContent.getScrollElement();
                this.contentHeight = el.offsetHeight;
                this.scrollHeight = el.scrollHeight;    
                if (this.config.maxValue === undefined) {
                    this.config.maxValue = this.element.nativeElement.offsetHeight;
                }
                this.lastScrollPosition = el.scrollTop;
            });
            this.scrollContent.ionScroll.subscribe((ev) => this.adjustElementOnScroll(ev));
            this.scrollContent.ionScrollEnd.subscribe((ev) => this.adjustElementOnScroll(ev));
        }
    }

    private adjustElementOnScroll(ev) {
        if (ev) {
            this.domCtrl.write(async () => {
                const el = await this.scrollContent.getScrollElement();
                let scrollTop: number = el.scrollTop > 0 ? el.scrollTop : 0;
                let scrolldiff: number = scrollTop - this.lastScrollPosition;
                this.lastScrollPosition = scrollTop;
                let newValue = this.lastValue + scrolldiff;
                newValue = Math.max(0, Math.min(newValue, this.config.maxValue));
                this.renderer.setStyle(this.element.nativeElement, this.config.cssProperty, `-${newValue}px`);
                this.lastValue = newValue;
            });
        }
    }
}
export interface ScrollHideConfig {
    cssProperty: string;
    maxValue: number;
}
like image 34
Yuyinitos Avatar answered Oct 08 '22 05:10

Yuyinitos


The event may have changed in Ionic 4, but you can still import the DomController from @ionic/angular

import {..., DomController } from "@ionic/angular";

and inject it in the constructor

constructor(
    // ...
    private domCtrl: DomController
) { }

And then use the write() method like this:

private adjustElementOnScroll(ev) {
    if (ev) {
        this.domCtrl.write(() => {
            // ...
        });
    }
}

The DomController is just a way for Ionic to create a queue for callbacks that would write or read the DOM in order to use the window.requestAnimationFrame() method behind the scenes.

For more information, please visit:

  1. DomController source code
  2. requestAnimationFrame MDN
like image 36
sebaferreras Avatar answered Oct 08 '22 06:10

sebaferreras