Ionic-framework: Animation on page transition on iOS is broken

Created on 1 Mar 2019  ·  83Comments  ·  Source: ionic-team/ionic-framework

Bug Report

Ionic version:


[x] 4.x

Current behavior:
Page transition is breaking on iOS. Android is working good.

ezgif com-video-to-gif

Expected behavior:
Transition smooth onto next page.

Steps to reproduce:

Related code:

Typically I'd use this.navCtrl.navigateForward(['route', id]); to navigate to the next page, but 90% of time the animation would stop.

Other information:

Ionic info:

Ionic:

   ionic (Ionic CLI)             : 4.10.3 (/Users/andjelicnikola/.nvm/versions/node/v10.7.0/lib/node_modules/ionic)
   Ionic Framework               : @ionic/angular 4.0.2
   @angular-devkit/build-angular : 0.11.4
   @angular-devkit/schematics    : 7.2.2
   @angular/cli                  : 7.2.2
   @ionic/angular-toolkit        : 1.2.1

Cordova:

   cordova (Cordova CLI) : 7.1.0
   Cordova Platforms     : android 7.1.0, ios 4.4.0
   Cordova Plugins       : cordova-plugin-ionic-keyboard 2.1.3, cordova-plugin-ionic-webview 3.1.2, (and 23 other plugins)

System:

   ios-deploy : 2.0.0
   NodeJS     : v10.7.0 (/Users/andjelicnikola/.nvm/versions/node/v10.7.0/bin/node)
   npm        : 6.8.0
   OS         : macOS Mojave
   Xcode      : Xcode 10.1 Build version 10B61
core bug

Most helpful comment

My current guess is the performance issue you were seeing is actually related to this bug in WebKit: https://bugs.webkit.org/show_bug.cgi?id=201048

Animations in the Shadow DOM tend to freeze/be janky when the layout is invalidated. In this case Node.insertBefore causes the issue. Unfortunately, Angular can sometimes call that when evaluating an ngIf, so the issue is easier to dig up in Angular apps.

I am discussing with the team as to whether there may be an appropriate workaround we could go with for now.

All 83 comments

There may be an open PR for this already:
https://github.com/ionic-team/ionic/pull/17224

Hi there,

Thanks for the issue! Would you be able to provide a repository with the code required to reproduce this issue? Additionally, which devices/iOS versions have you been testing on?

Thanks!

It seems like upgrade to Ionic 4.1.0 solved the issue.

If it means anything I tested on iPhone 8 Plus iOS 12.1.4

I'm still seeing this on 4.1.0. You can sometimes see the issue if you limit the CPU power and navigate into a page using Chrome DevTools. Finding it hard to consistently reproduce the issue though.

I am willing to help but I'd have a problem reproducing error now that my code works. Maybe if @rossholdway shares code and I can see if there is a difference to my code.

I just wanted to confirm that it started to happen again... not sure why it was working good for 1 day. I will try to create repository that reproduces the problem.

First I added RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }) to app-routing.module.ts.

When loading pages I usually protect components from showing up before data is loaded with variable isLoading: number and I place the variable inside component like this: *ngIf="!isLoading".

When I removed *ngIf statement or replaced it with [hidden] it stopped flickering.

It'd be great if @rossholdway or somebody else to test this. I am not able to reproduce it on a fresh app yet, but I did not try to make API call. I tried to simulate loading with setTimeout which is not the same.

@liamdebeasi I can substitute ion-buttons to be just a div and the problem persists. I would like to think that any element within the toolbar should transition with the rest of the elements in that toolbar. It looks peculiar to see a header and the rest of the ion-content do a slide transition while the buttons appear with no slide and sit statically while the transitions occurs.

@liamdebeasi @jayordway It's not a problem just with toolbar. when I put <my-custom-component *ngIf="expression"></my-custom-component> anywhere inside ion-content the transition breaks.

@aces-tm Are you testing with the new development branch of ionic though?

@jayordway no, should I update to @latest or something else?

Ah, I was looking at the proposed PR for this and read this note:
https://github.com/ionic-team/ionic/pull/17224#issuecomment-473033208

Hi everyone,

We have shipped some updates to the page transitions on iOS. Please try updating to Ionic 4.1.2 (npm i @ionic/core@latest @ionic/angular@latest), and let me know if you are still running into any issues.

Thanks!

@liamdebeasi I didn't realize I needed to respond... yes, I still experience the same issue. I am currently on 4.2.0

Ok thanks! I'll check this issue out some more.

If there is anything that calls controller like {{ myVariable }} the transition will breakdown. However I have plenty of pages where I will be making the same calls and transition would work fine. I load the pages the same way.

I am trying to debug this, but no luck

Any update on this issue ? I have the same problem ..

@liamdebeasi Did you have any luck with tracking this one down? I'm still seeing this issue in v4.4.0. It really impacts the UX when all of the page transitions are like this. I find that it frequently happens when using Angulars ngOnInit method to load in data.

Hi @rossholdway,

Do you have a code reproduction for this issue that I can use?

Thanks!

@liamdebeasi When I open this page from side menu the animation breaks.
Let me know if you are able to use this code. It requires app-version plugin

about.zip

Hi there,

Can you provide a full repository? It doesn't need to be your entire app's code, but knowing how the sidemenu is setup and how you are presenting the page would be helpful.

Thanks!

Here it is. It broke down on my phone (iPhone X). I believe that you only need to install app version plugin

ionic cordova plugin add cordova-plugin-app-version
npm install @ionic-native/app-version

Go to Side menu and open page about

app.zip

Thanks for the follow up. I was able to reproduce this issue. We will look into a fix!

I was going insane trying to figure out why this was happening, great to see someone else having this same exact issue and that the ionic team was able to reproduce. For the time being, I created my own page transition animations which work okay, but would rather use the native animations! I will be able to sleep happy now that I know a fix will be on the way :)

This is happening because you are assigning subscription variables and/or subscribing in ngOnInit(). I was able to overcome this issue by using ionViewDidEnter() instead of ngOnInit(). However, when you do this, the page looks empty at first. If you don't want that, you can use ghost elements to fill out the page and then when your API comes through, just display the correct items.

Addition: I didn't know ionic already has something similar to ghost elements. Please see https://ionicframework.com/docs/api/skeleton-text

If anybody wants to reproduce the problem, just assign an API subscription to a value in ngOnInit() then subscribe it in the ion-content ngIf with async.

In my case, it was:

TS:

ngOnInit() {
 this.subCategoryData$ = this._fireStoreService.getExampleData();
}

HTML:

<ion-content padding>
  <ng-container *ngIf="subCategoryData$ | async as category$">
    <div class="content-header">
      <div class="page-title">{{ category$.name }} Selections</div>
    </div>
    .
    .
    .
  </ng-container>
</ion-content>

FIX (replace ngOnInit() with ionViewDidEnter()):

ionViewDidEnter() {
 this.subCategoryData$ = this._fireStoreService.getExampleData();
}

It would be great if this was fixed asap.

@etonyali this is interesting and explains why i see it less on wifi (because the data loads before the transition even begins), I wonder why a subscription affects a transition animation anyways..

@etonyali Thanks! I've seen this happen as well when I call async functions inside of resolvers.

Are there any updates on this issue?

Hi guys, any news on this one? We're experiencing the same issue...

Experiencing the same issue in ionic 4.5.0

Hi, some new with this problem, I have a critical application that will go to production and I have this problem.

Hi everyone,

This is still on our radar, but we have been focused on shipping Ionic w/ the Stencil One update. Now that the Stencil One update has been released, I updated the example app (https://github.com/ionic-team/ionic/issues/17649#issuecomment-493106320) to Ionic 4.6.1. (Tested on iPhone 7)

Compared to previous versions of Ionic, I am noticing an improvement in performance with the iOS transition. I do notice a little bit of jank, but it's not as noticeable as before.

Can other people update their apps to 4.6.1 and confirm whether or not updating reduces the severity of this issue? I am still interested in resolving any remaining jank, but am curious if the update helps at all.

Thanks!

A small improvement, but it still needs to get better. is not performing well at all.
Tested on iPhone X, Ionic 4.6.1
glitch

Can you provide a code example of the GIF you sent?

still the problem persists, we are about to launch a banking application, please I need your help.

@jtsc22 Can you provide a repo with the code required to reproduce this issue? For issues like this it's helpful to have several examples so we can ensure we "cover our bases" so to speak when working on a fix.

I did more testing and it seems that 4.6.2 version does not resolve the problem with page transition.

Here is the code that I put in the footer and on transition, it is not performing as epxected

<ion-footer>
  <ion-toolbar class="no-border" color="white">
    <ion-textarea
      #textarea
      autoGrow
      rows = "1"
      autocorrect="on"
      autocomplete="on"
      autocapitalize="on"
      placeholder="Send message...">
    </ion-textarea>

    <ion-buttons slot="end">
      <ion-button [disabled]="broadcasting" color="dark" (click)="broadcastMessage(textarea.el.textContent)">
        <ion-icon name="send" slot="icon-only"></ion-icon>
      </ion-button>
    </ion-buttons>

  </ion-toolbar>
</ion-footer>

Hi @aces-tm,

I am no longer able to reproduce the performance issues you are describing after updating to the latest version of Ionic. Are you able to provide another reproduction?

Also, when you say "it is not performing as expected" do you mean that the animation is not smooth, or that the footer is not animating at all?

Thanks!

@liamdebeasi I just updated the app to ionic 4.7.1 and angular 8.1.2 and the problem is the same. The problem is the same as what I posted in my first, original post. The transition is breaking up and flickering. I have 3 apps and the same things happen on all of them.

Here is my package.json

"dependencies": {
    "@angular/common": "^8.1.2",
    "@angular/core": "^8.1.2",
    "@angular/forms": "^8.1.2",
    "@angular/platform-browser": "^8.1.2",
    "@angular/platform-browser-dynamic": "^8.1.2",
    "@angular/router": "^8.1.2",
    "@auth0/angular-jwt": "^3.0.0",
    "@ionic-native/contacts": "^5.11.0",
    "@ionic-native/core": "^5.11.0",
    "@ionic-native/device": "^5.11.0",
    "@ionic-native/screen-orientation": "^5.11.0",
    "@ionic-native/sms": "^5.11.0",
    "@ionic-native/splash-screen": "^5.11.0",
    "@ionic-native/status-bar": "^5.11.0",
    "@ionic/angular": "^4.7.1",
    "@ionic/storage": "^2.2.0",
    "@mobiscroll/angular": "file:./src/lib/mobiscroll-package/mobiscroll-angular-4.7.2.tgz",
    "cordova-android": "8.0.0",
    "cordova-android-support-gradle-release": "^3.0.1",
    "cordova-ios": "^4.5.5",
    "cordova-plugin-contacts": "^3.0.1",
    "cordova-plugin-device": "^2.0.2",
    "cordova-plugin-ionic-keyboard": "^2.1.3",
    "cordova-plugin-ionic-webview": "^4.1.0",
    "cordova-plugin-screen-orientation": "^3.0.1",
    "cordova-plugin-splashscreen": "^5.0.2",
    "cordova-plugin-statusbar": "^2.4.2",
    "cordova-plugin-whitelist": "^1.3.3",
    "cordova-sms-plugin": "^1.0.0",
    "cordova-sqlite-storage": "^3.2.0",
    "core-js": "^2.5.4",
    "es6-promise-plugin": "^4.2.2",
    "libphonenumber-js": "^1.7.21",
    "moment": "^2.24.0",
    "ngx-moment": "^3.4.0",
    "ngx-socket-io": "^2.1.1",
    "rxjs": "~6.5.1",
    "rxjs-compat": "^6.5.2",
    "tslib": "^1.9.0",
    "zone.js": "~0.9.1"
  },
  "devDependencies": {
    "@angular-devkit/architect": "^0.802.0",
    "@angular-devkit/build-angular": "^0.802.0",
    "@angular-devkit/core": "^8.2.0",
    "@angular-devkit/schematics": "^8.2.0",
    "@angular/cli": "~8.2.0",
    "@angular/compiler": "~8.2.0",
    "@angular/compiler-cli": "~8.2.0",
    "@angular/language-service": "~8.2.0",
    "@ionic/angular-toolkit": "~2.0.0",
    "@types/jasmine": "~3.3.8",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "~8.9.4",
    "codelyzer": "~4.5.0",
    "jasmine-core": "~3.4.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.1.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "~2.0.1",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.4.0",
    "protractor": "~5.4.0",
    "ts-node": "~8.2.0",
    "tslint": "~5.17.0",
    "typescript": "~3.5.3"
  },

Is there any updates on this? I'm still seeing this problem even in 4.8.0

@liamdebeasi I want to confirm that the transition is working fine for me after I upgraded all my dependances. The only problem remaining is that the footer is transparent during the animation. Here is my package.json

"dependencies": {
        "@angular/common": "~8.1.2",
        "@angular/core": "~8.1.2",
        "@angular/forms": "~8.1.2",
        "@angular/platform-browser": "~8.1.2",
        "@angular/platform-browser-dynamic": "~8.1.2",
        "@angular/router": "~8.1.2",
        "@auth0/angular-jwt": "^3.0.0",
        "@ionic-native/app-version": "^5.12.0",
        "@ionic-native/badge": "^5.12.0",
        "@ionic-native/call-number": "^5.12.0",
        "@ionic-native/camera": "^5.12.0",
        "@ionic-native/clipboard": "^5.12.0",
        "@ionic-native/core": "5.12.0",
        "@ionic-native/document-viewer": "^5.12.0",
        "@ionic-native/email-composer": "^5.12.0",
        "@ionic-native/file": "^5.12.0",
        "@ionic-native/file-chooser": "^5.12.0",
        "@ionic-native/file-path": "^5.12.0",
        "@ionic-native/file-picker": "^5.12.0",
        "@ionic-native/file-transfer": "^5.12.0",
        "@ionic-native/geolocation": "^5.12.0",
        "@ionic-native/google-analytics": "^5.11.0",
        "@ionic-native/image-resizer": "^5.12.0",
        "@ionic-native/in-app-browser": "^5.12.0",
        "@ionic-native/ionic-webview": "^5.12.0",
        "@ionic-native/photo-library": "^5.12.0",
        "@ionic-native/push": "^5.12.0",
        "@ionic-native/screen-orientation": "^5.12.0",
        "@ionic-native/social-sharing": "^5.12.0",
        "@ionic-native/splash-screen": "5.12.0",
        "@ionic-native/status-bar": "^5.12.0",
        "@ionic-native/video-editor": "^5.12.0",
        "@ionic/angular": "^4.8.0",
        "@ionic/core": "^4.8.0",
        "@ionic/storage": "^2.2.0",
        "@mobiscroll/angular": "file:./src/lib/mobiscroll-package/mobiscroll-angular-4.7.3.tgz",
        "@ngx-translate/core": "^11.0.1",
        "@ngx-translate/http-loader": "^4.0.0",
        "animate.css": "^3.7.0",
        "call-number": "^1.0.1",
        "chart.js": "^2.8.0",
        "cordova-android": "^8.0.0",
        "cordova-clipboard": "^1.3.0",
        "cordova-ios": "4.5.5",
        "cordova-plugin-add-swift-support": "^2.0.2",
        "cordova-plugin-app-version": "^0.1.9",
        "cordova-plugin-badge": "^0.8.8",
        "cordova-plugin-camera": "^4.0.3",
        "cordova-plugin-device": "^2.0.2",
        "cordova-plugin-document-viewer": "^0.9.10",
        "cordova-plugin-email-composer": "^0.9.2",
        "cordova-plugin-file": "^6.0.1",
        "cordova-plugin-file-transfer": "^1.7.1",
        "cordova-plugin-filechooser": "^1.2.0",
        "cordova-plugin-filepath": "^1.5.1",
        "cordova-plugin-filepicker": "^1.1.5",
        "cordova-plugin-geolocation": "^4.0.1",
        "cordova-plugin-google-analytics": "^1.8.6",
        "cordova-plugin-inappbrowser": "^3.0.0",
        "cordova-plugin-ionic-keyboard": "^2.1.3",
        "cordova-plugin-ionic-webview": "^4.0.1",
        "cordova-plugin-screen-orientation": "^3.0.1",
        "cordova-plugin-splashscreen": "^5.0.2",
        "cordova-plugin-statusbar": "^2.4.2",
        "cordova-plugin-video-editor": "^1.1.3",
        "cordova-plugin-whitelist": "^1.3.3",
        "cordova-plugin-x-socialsharing": "^5.4.4",
        "core-js": "^2.5.4",
        "countries-and-timezones": "^1.0.1",
        "emulate": "1.0.2",
        "es6-promise-plugin": "^4.2.2",
        "info.protonet.imageresizer": "^0.1.1",
        "moment": "^2.23.0",
        "moment-timezone": "^0.5.23",
        "mx.ferreyra.callnumber": "~0.0.2",
        "ng2-charts": "^1.6.0",
        "ngx-moment": "^3.4.0",
        "phonegap-plugin-multidex": "^1.0.0",
        "phonegap-plugin-push": "^2.2.3",
        "rxjs": "~6.5.2",
        "rxjs-compat": "^6.5.2",
        "time-ago-pipe": "^1.3.2",
        "zone.js": "~0.9.1"
    },
    "devDependencies": {
        "@angular-devkit/architect": "^0.802.0",
        "@angular-devkit/build-angular": "^0.802.0",
        "@angular-devkit/core": "^8.2.0",
        "@angular-devkit/schematics": "^8.2.0",
        "@angular/cli": "~8.2.0",
        "@angular/compiler": "~8.2.0",
        "@angular/compiler-cli": "~8.2.0",
        "@angular/language-service": "~8.2.0",
        "@ionic/angular-toolkit": "~2.0.0",
        "@ionic/lab": "1.0.21",
        "@types/jasmine": "~3.3.8",
        "@types/jasminewd2": "~2.0.3",
        "@types/node": "~8.9.4",
        "codelyzer": "~5.1.0",
        "jasmine-core": "~3.4.0",
        "jasmine-spec-reporter": "~4.2.1",
        "karma": "~4.1.0",
        "karma-chrome-launcher": "~2.2.0",
        "karma-coverage-istanbul-reporter": "~2.0.1",
        "karma-jasmine": "~1.1.2",
        "karma-jasmine-html-reporter": "^0.2.2",
        "protractor": "~5.4.0",
        "ts-node": "~8.1.0",
        "tslint": "~5.16.0",
        "tslint-ionic-rules": "0.0.21",
        "typescript": "~3.5.3"
    },

@liamdebeasi i'm on 4.8.1 and i still have the issue: if i use ngIf within ion-content the page transition animation stutters. If i remove the ngIf everything runs fine. I don't use the async pipe by the way.

fwiw: the problem also exists with

  • angular@next
  • on production builds (however to a lesser extent)
  • ChangeDetectionStrategy.OnPush

@liamdebeasi from my tests it looks like the problem only happens if the ngIf expression changes during the transition animation. So probably removing/adding nodes from the DOM somehow messes with the animation.

@chrisgriffith This is a good catch and it makes a lot of sense from what I have experienced.

Are you using Angular 8? My problems disappeared when I upgraded my dependences to the latest versions.

@chrisgriffith i'm tried angular@8 as well as @9 (next).

i edited my comment above (https://github.com/ionic-team/ionic/issues/17649#issuecomment-526617719) since my initial test was inaccurate (@liamdebeasi). The problem happens for me when an ngIf in ion-content triggers during page animation.

I do not know if this is related.

I have an ionic 4 application where on the main page it has animation - a row of three blinking LEDs (blinking pattern dependent on state).

When I first get to the main page the LEDs blink appropriately. When I leave the main page and go to another page and then come back to the main page the LEDs are not blinking. The animation is no longer working.

I route to and from pages via a hamburger menu set up in my app.component.ts where page.url is for example ‘/main’

this.router.navigate([page.url]);

NOTE: I only see this on ios devices. Android works with no issue.

This was all working in ionic 3.

Below is the html

    <div class="leds">
        <div class="led-container">
            <div class="led led-red" [ngClass]="redLedState"></div>
            <div class="led led-yellow" [ngClass]="yellowLedState"></div>
            <div class="led led-green" [ngClass]="greenLedState"></div>
        </div>
    </div>

This is the .scss:

.leds {
    $led-size: 32px;
    $led-green: #33cd5f;
    $led-yellow: #ffc900;
    $led-red: #ef473a;

    @mixin on($color) {
      box-shadow: 0 0 4px 0 darken($color, 5%);
    }

    .led-container {
      text-align: center;
      border-bottom: 1px solid #111;
      width: 100%;
      display: block;
      padding: 10px 0px 6px 0px;
    }

    .led {
      display: inline-block;
      border-radius: 50%;
      width: $led-size;
      height: $led-size;
      margin-right: $led-size;
      opacity: 0.2;
      box-shadow: none;

      &:last-child {
        margin-right: 0;
      }

      &.led-green {
        background-color: $led-green;
        &.on, &.slow-flash, &.fast-flash {
          @include on($led-green);
        }
      }

      &.led-yellow {
        background-color: $led-yellow;
        &.on, &.slow-flash, &.fast-flash {
          @include on($led-yellow);
        }
      }

      &.led-red {
        background-color: $led-red;
        &.on, &.slow-flash, &.fast-flash {
          @include on($led-red);
        }
      }

      &.on {
        opacity: 1;
      }
    }

    .slow-flash {
      -webkit-animation: flash linear 1.75s infinite;
      animation: flash linear 1.75s infinite;
      -webkit-transition: none !important;
      transition: none !important;
    }

    .fast-flash {
      -webkit-animation: flash linear 0.4s infinite;
      animation: flash linear 0.4s infinite;
    }

    .slow-flash-add .fast-flash-add {
      -webkit-animation: none !important;
      animation: none !important;
      -webkit-transition: none !important;
      transition: none !important;
    }

    @-webkit-keyframes flash {
      0% {
        opacity: 0.2;
      }
      60% {
        opacity: 0.2;
      }
      62% {
        opacity: 1;
      }
      98% {
        opacity: 1;
      }
    }

    @keyframes flash {
      0% {
        opacity: 0.2;
      }
      60% {
        opacity: 0.2;
      }
      62% {
        opacity: 1;
      }
      98% {
        opacity: 1;
      }
    }
  }

Here is my ionic info:

Ionic:

```
Ionic CLI : 5.2.8 (/Users/.nvm/versions/node/v12.6.0/lib/node_modules/ionic)
Ionic Framework : @ionic/angular 4.9.0
@angular-devkit/build-angular : 0.802.2
@angular-devkit/schematics : 8.3.3
@angular/cli : 8.3.3
@ionic/angular-toolkit : 2.0.0

Cordova:

Cordova CLI : 9.0.0 ([email protected])
Cordova Platforms : android 8.0.0, ios 5.0.1
Cordova Plugins : cordova-plugin-ionic-keyboard 2.2.0, cordova-plugin-ionic-webview 4.1.1, (and 10 other plugins)

Utility:

cordova-res : 0.6.0
native-run : 0.2.8

System:

ios-sim : 8.0.1
NodeJS : v12.6.0 (/Users/.nvm/versions/node/v12.6.0/bin/node)
npm : 6.11.3
OS : macOS Mojave
Xcode : Xcode 10.3 Build version 10G8

Here are my dependencies:

"dependencies": {
"@angular/common": "^8.2.5",
"@angular/core": "^8.2.5",
"@angular/forms": "^8.2.5",
"@angular/platform-browser": "^8.2.5",
"@angular/platform-browser-dynamic": "^8.2.5",
"@angular/router": "^8.2.5",
"@ionic-native/app-version": "^5.13.0",
"@ionic-native/ble": "^5.13.0",
"@ionic-native/core": "^5.13.0",
"@ionic-native/diagnostic": "^5.13.0",
"@ionic-native/in-app-browser": "^5.13.0",
"@ionic-native/insomnia": "^5.13.0",
"@ionic-native/splash-screen": "^5.13.0",
"@ionic-native/sqlite": "^5.13.0",
"@ionic-native/status-bar": "^5.13.0",
"@ionic/angular": "^4.9.0",
"@ngrx/effects": "^8.3.0",
"@ngrx/store": "^8.3.0",
"@ngx-translate/core": "^11.0.1",
"@ngx-translate/http-loader": "^4.0.0",
"@types/d3": "^5.7.2",
"@types/text-encoding": "0.0.35",
"angular": "^1.7.8",
"cordova-android": "8.0.0",
"cordova-ios": "5.0.1",
"cordova-plugin-app-version": "^0.1.9",
"cordova-plugin-ble-central": "1.2.2",
"cordova-plugin-compat": "1.2.0",
"cordova-plugin-device": "^2.0.3",
"cordova-plugin-inappbrowser": "^3.1.0",
"cordova-plugin-insomnia": "^4.3.0",
"cordova-plugin-ionic-keyboard": "2.2.0",
"cordova-plugin-splashscreen": "^5.0.3",
"cordova-plugin-statusbar": "^2.4.3",
"cordova-plugin-whitelist": "^1.3.4",
"cordova-sqlite-storage": "^3.3.0",
"cordova.plugins.diagnostic": "^5.0.0",
"core-js": "^3.2.1",
"d3": "^5.12.0",
"i": "^0.3.6",
"lodash": "^4.17.15",
"npm": "^6.11.3",
"rxjs": "^6.5.3",
"tslib": "^1.10.0",
"zone.js": "^0.9.1"
}

```

Again it may or may not be related to the issue described above but I was able to duplicate my issue (animation on main page stops working when leaving main page and coming back to main page) in a simple ionic 4 project I created. It can be viewed at the following repo:

https://github.com/ASHBAW/animation.git

Any update on this issue? Changing from ngOnInit() to ionViewDidEnter() fixes the issue, but isn't the ideal solution due to the fact that the API call or firestore query is done a little later in the transition.

I am having the same problem (interruption of page transition animation) with the latest versions of angular and ionic-angular.

The setup:

  • loading data from a GraphQL server (in ngOnInit):
ngOnInit() {
   this.id = this.route.snapshot.paramMap.get('id');
   this.client$ = this.backendService.loadClient({id: this.id});
}
  • template:
<div *ngIf="client$ | async as client; else loading">
... big chunk of template html ...
</div>
<ng-template #loading>
  <app-loading-spinner></app-loading-spinner>
</ng-template>

My observations are in line with https://github.com/ionic-team/ionic/issues/17649#issuecomment-526617719:

  • if the data is received during the transition animation, the animation is interrupted, presumably because a big chunk of template html is added to the DOM (however: if the big chunk of template html is actually not big, the interruption is barely noticable)
  • if the data is received after the transition animation, then there is no interruption.
  • workaround: loading the data in ionViewDidEnter. The transition is smooth, but the data loads later. In addition, when the data is loaded very quickly (from cache after the first request), then this takes longer than necessary.
  • another workaround: don't use the else block and subscribe to the observable in ngOnInit

workaround

@iherger as stated above (and as far as i could understand/reproduce it) the problem happens whenever something is added or removed from the DOM during the page transition. migrating all ngIf to [hidden] removes the issue for me without having to delay the display of the data (since [hidden] doesn't remove or add anything). obviously this is not a fix (although i suppose this should make the fix not too difficult to find for someone familiar with the ionic internals @liamdebeasi)

@chriswep , thanks for the confirmation. That makes sense.

I am trying to keep the subscription to observables in the template (using*ngIf and the async pipe), and in this case, [hidden] doesn't work, if I am not mistaken.

I am trying to keep the subscription to observables in the template (using*ngIf and the async pipe), and in this case, [hidden] doesn't work, if I am not mistaken.

correct, if you use [hidden] you need to manage the subscription yourself (subscribing, setting the value on the component, unsubscribing onDestroy). apart from that [hidden] obviously is the boolean negative of *ngIf. since i don't know when this will be fixed (it's a long running issue already) if bit the bullet to not be stuck updating my app.

To add to this... I changed my projects to work with Capacitor and I am still experiencing the same problem.

This issue seems like something that is better handled at the application level than at the framework level.

Using ionViewDidEnter instead of ngOnInit works because ionViewDidEnter is run after the animation has been completed, whereas ngOnInit is fired when the page component is created (before the animation has started).

Doing constant rerenders via an *ngIf or a pipe is going to cause the browser to do a lot of work. This is often referred to as “layout thrashing” . While the navigation animations are handled off the main thread, your JavaScript code and animations do share certain resources. These rerenders are causing the browser to constantly use these resources, leaving fewer resources/computation time for the animation itself.

Another point to mention is toggling the *ngIf state is going to continually add/remove elements from the DOM, which is a _very_ expensive operation in general. It’s important to remember that your code is oftentimes going to cause Angular to run more code, which is going to cause the browser to have to do even more work (some people have already mentioned this, I just wanted to reiterate it).

From the examples that you all have provided, there is quite a lot of work being done while the animation is still transitioning. I would recommend that this work gets done after the animation is done by using ionViewDidEnter. If you absolutely need the view to be “ready” when the animation starts you will likely need to preload/cache your data, throttle how often your pipes/data causes the view to rerender, or ensure that your elements are not constantly being removed from or added to the DOM.

As I said previously, I think this is an issue that would be better handled at the application level via code optimizations than at the framework level. Any thoughts or questions? Thanks!

@liamdebeasi Thanks for the explanation. It is helpful to know that this cannot be resolved and that we take this as the final solution.

Yeah I can understand this might be frustrating. Angular's change detection can certainly be tricky and sometimes work to your disadvantage.

Also you might have slightly better results with using the OnPush change detection strategy, so you might want to experiment with that as well: https://netbasal.com/a-comprehensive-guide-to-angular-onpush-change-detection-strategy-5bac493074a4

ionViewDidEnter works very good and for me, there is no need to look at anything else.

@liamdebeasi i'm not really convinced that this is the real issue here. this was working before in the same app without any issue (can't tell for sure now when it started, it definitely still worked with Ionic 3). also it doesn't look like only being about performance - it looks more like the animation is interrupted at some point, like if some events or animations interfere with each other. additionally the issue doesn't go away on a super fast dev machine.

using *ngIf on a transitioning page should work and did work. it's not just about preloading data but about how we setup angular templates and components. delaying observables, async-pipes / ngIf until a page has finished entering (or even being forced to not use those basic things anymore if you want a fast UI) can't really be the solution for Ionic going forward.

@liamdebeasi , thank you for the detailed response.

I understand the general concept, but I still think there might be an issue here. In my case, I am e.g. switching from a list of clients to the detailed view of a client. Client data (about 20 datapoints, so nothing major) is fetched in a GraphQL query. However, as soon as the data is received, the animation interrupts (if I using observables and the async pipe). The client detail template is not large, and there is exactly one *ngIf which subscribes to the observable.

@chriswep So for some context as to what's happening under the hood -- We are using CSS Animations/Web Animations (depending on browser support) to create these navigation animations. We then pass that work off to the browser to run/render. We do not run these animations in a requestAnimationFrame loop like some animation libraries do, so there's really not a lot of "stuff" that Ionic is doing apart from creating the animation and telling the browser to play it.

Additionally, the iOS navigation transition animates the transform and opacity properties which are properties that can be handled on separate threads (usually called "compositor threads" in Chrome). I'm not sure what else we could do to optimize these animations as we are already passing off most of the work to the browser.

I agree that there's a lot more than just using *ngIf/a hidden class. My examples were a subset of different optimizations you can make (I don't remember them all off the top of my head 🙂).

If you can provide a working example of this running well in an Ionic 3 app I'm more than happy to take a look.

edit: I also wanted to clarify that I didn't intend to suggest you _never_ use things like *ngIf, I just meant that there's a time and a place for everything. For example, if you don't need an element on the page it's a great idea to use *ngIf to remove it from the DOM; however, the act of removing an element from the DOM is an expensive operation by itself so it's important to be mindful of what you're doing as well as how often you're doing it.

@iherger Can you provide a Timeline profile from your app? It's hard to say what's going on without seeing the data itself. In Safari Dev Tools, click the "Timelines" tab and click the red circle in the top left to start recording. When done, click the square button in the top left. Then, click the "Export" button and attach the file to this thread.

I'm mostly curious to see if there are any additional optimizations we could make in Ionic Framework apart from the animations.

Of course a working reproduction would be ideal, but it sounds like there's a server side component here so I'm not sure if that would be possible from your end.

@liamdebeasi thanks for looking into this.

Attached is a timeline. There was noticeable interruption in the transition.

localhost-recording.json.zip

@iherger Thanks for the timeline data! I’ve identified two points during the transition that likely cause your lag:

image

The call trees for “Click Event Dispatched” and “Load Event Dispatched” both take quite a long time. For context, for a 60fps animation, each frame should take ~16.7ms, and the entire operations for these are taking 86.11ms and 29.02ms respectively. As a result, these tasks are being spread out across multiple frames since they cannot be completed in 16.7ms. This is where your animation lag is going to come into play.

You can also visualize this in terms of frames:

image

Notice that there are 3 times that frames drop below 30fps. Presumably, these are the 3 events you see in the first image.

For context, the 2nd event “Full Garbage Collection” is just cleanup and appears to happen after the animation has completed, so I’m not too concerned about that (I expect there needs to be some cleanup once navigation has completed). I am also assuming that the network requests (the light blue parts on the timeline) are your GraphQL results.

If you right click the first event and click “Expand All” you will notice quite a bit of processing is happening on the Angular side. I won’t post the screenshot here since the call tree is quite large, but you should notice calls from Angular zone, Angular Core, Angular Router, utility functions, and others. I did not see any Ionic-specific tasks taking up more than ~1ms of processing time. Ionic Angular is typically the fesm5.js file you will see in there. I am seeing similar results with the second “Load Event Dispatched” event.

You can also see that after the first event (which is the biggest purple block on the timeline) there are several Paint, Composite, and Style recalculations going on (likely due to Angular change detection trying to rerender components).

It’s hard to make definitive conclusions without seeing the actual code and running the app, but as of now nothing from the Ionic side sticks out as contributing significantly to the lag you are experiencing. Hope this clears things up!

@liamdebeasi thank you for all the background information. i'm also sure that Ionic does a great job at optimising everything for good performance. i was just suggesting the possibility that this is not related to performance or (missing) optimisation but that this is (simply) a bug. of course i might be wrong here. however since i feel i can't simply settle on this being the status quo now, i took the effort to create two blank Ionic projects, one with Ionic 4 and one with version 3. I added a second page "Page2" and a button on the home page that navigates there. "Page2" has this in the controller:

  ngOnInit() {
    setTimeout(() => {
      this.showDiv = true;
    },50)
  }

and this in the template:

<ion-content>
  <div *ngIf="showDiv">Delayed content</div>
</ion-content>

The Ionic 4 version clearly shows the issue (tested on Safari Reponsive Design mode on a fast dev machine, as well as iOS Sim), the Ionic 3 version works just fine.

Oh yeah, there certainly could be a bug! Are you able to share those 2 projects with me?

Oh yeah, there certainly could be a bug! Are you able to share those 2 projects with me?

IosPageTransitionBug_Ionic3vs4.zip

Thanks for the follow up. I am able to reproduce the lag in the v4 version of the app in Safari.

The lag appears to come from NgZone running in the timeout. The v3 app uses Angular 5.x, but the v4 app uses Angular 8.x so it's hard to say what is different in Angular/NgZone that is causing this change.

To demonstrate that this lag is related to Angular/NgZone, you can try the following:

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

...

constructor(private zone: NgZone) {}

...

ngOnInit() {
  this.zone.runOutsideAngular(() => {
    setTimeout(() => {
      this.showDiv = true;
    }, 50);
  });
}

By running outside of NgZone the lag should disappear entirely. I'm still digging through all of the NgZone traces when running a Timeline recording on Safari, so I'll post any additional information here when I have it.

By running outside of NgZone the lag should disappear entirely.

That is probably because then the *ngIf is not triggered which is what effectively causes the ionic animation to be interrupted. Also, if you leave the controller code as is and just replace *ngIf with [hidden], all is fine.

Right, so ngIf will add/remove the div from the DOM whereas the hidden directive will just add "display: none" to the div. The DOM removal/addition is typically more expensive than just changing the display.

The one thing that’s interesting is I can't reproduce this in Chrome (or any other browser) on the same machine. I wonder if we're hitting some edge case in Safari? Or maybe since Chrome supports css containment it's not as much of an issue?

(Just to add to this, I have been able to repo this in Chrome in my own app by dropping the CPU performance to its slowest in devtools)

Ah interesting. I have CPU set to 6x slowdown in dev tools and the performance still seems pretty good. I'll keep digging around.

So I wanted to try one thing. Can you do the following and let me know if the performance issue goes away or persists in Safari?

  1. Install this dev build: npm i @ionic/[email protected]
  2. Add the following CSS to src/global.scss:
ion-content {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  .inner-scroll {
    padding-top: 44px;
    background: white;
    height: 100%;
  }
}
  1. Try the test again using the v4 app @chriswep provided.

You app content will look a little funky, but I'm mostly interested in seeing if the performance issue went away. If it does, I have a theory as to why.

but I'm mostly interested in seeing if the performance issue went away

the transition runs without issues with this (at least on Safari)

Agreed. Tested this in Safari on iOS with the example @chriswep provided. The transition runs without issue after installing the dev build (and adding the CSS). 🎉 What's your theory / fix @liamdebeasi

My current guess is the performance issue you were seeing is actually related to this bug in WebKit: https://bugs.webkit.org/show_bug.cgi?id=201048

Animations in the Shadow DOM tend to freeze/be janky when the layout is invalidated. In this case Node.insertBefore causes the issue. Unfortunately, Angular can sometimes call that when evaluating an ngIf, so the issue is easier to dig up in Angular apps.

I am discussing with the team as to whether there may be an appropriate workaround we could go with for now.

Any update Liam? Thanks!

Liam, as developers, we can't move forward with iOS builds until this issue gets resolved. There are a few bandaids available but the deterioration in the performance of the app makes it almost unusable for clients. This issue has been hanging out there for 9 months now, when can we give birth to a permanent and complete solution?

Hi @pvroom,

Unfortunately this is not an issue with Ionic, but rather an issue in WebKit/Safari. We have reached out to some contacts on the WebKit team to see if they can take a look at the issue.

The best thing I can recommend is to hold off on updating the view until the animation ends. I realize this is not an ideal workaround, but there is not much we are able to do from the Ionic Framework-side as of now. I will update this thread when I have more info to share. Thanks!

Hi Liam,
Thx for getting back on this. You say that this is not an Ionic issue but Ionic is the one responsible for changing their Ionic 4 platform to a PWA-based approach heavily reliant upon Shadow Dom / Webkit, etc. We don't have any of these issues with Ionic 3 builds. So I hope you can appreciate my frustration when you say "this is not an issue with Ionic." Over the last year, Ionic has largely ceased ongoing support for Ionic 2/3, which was working well for our mobile apps and Ionic is now concentrating on a PWA approach with Ionic 4. So we were pretty much forced to move to the use of Ionic 4, only to discover that Ionic 4 brings with it serious performance issues like this. It doesn't matter to us that it's a webkit issue, what matters to us is that Ionic has put us in this position by relying on external factors that are apparently beyond your control. After 9 months of waiting for a fix, we are getting pretty discouraged.

We appreciate the feedback! We moved to the Shadow DOM because of the performance and encapsulation improvements it brings. This move also allowed us to dynamically theme your app at runtime without having multiple CSS bundles. Having multiple bundles slowed down build times, startup times, etc. You can learn more about the benefits of the Shadow DOM here: https://developers.google.com/web/fundamentals/web-components/shadowdom.

I understand that this is frustrating, and we are eager to find a good resolution to this issue. Additionally, I have provided a workaround above that should allow you to avoid this performance issue entirely.

I am going to lock this thread for now. I think the current state of the issue is pretty clear, and I don't want to add any additional unwanted emails to the inboxes of other users who have interacted with this thread. Thanks!

Hi everyone,

I wanted to provide an update regarding this issue. I am closing this thread as the main issue has been resolved in WebKit (https://bugs.webkit.org/show_bug.cgi?id=201048).

The Ionic Team does not know exactly when this fix will be available as that is not under our control. For any other bugs, please open a new issue.

Thanks!

Quick update: The WebKit issue I linked to has been fixed and released as part of the latest Safari Technology Preview (release 101 at the time of writing). As it turns out, this bug was not solely related to the Shadow DOM as the WebKit team was able to reproduce this issue outside of the Shadow DOM.

We do not have a timeline as to when the fix will become available for all users as that release schedule is controlled by Apple.

Was this page helpful?
0 / 5 - 0 ratings