Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4 animations not working with Safari iOS 9.3

I'm currently testing my app in all the possible browsers and I got to see that the angular animations don't behave as expected in Safari iOS 9.3. After hours and hours trying to solve it I come asking for a help in hand. Thanks in advance.

My code reads:

package.json

"dependencies": {
    "@angular/animations": "^4.3.2",
    "@angular/cdk": "^2.0.0-beta.8",
    "@angular/common": "^4.0.0",
    "@angular/compiler": "^4.0.0",
    "@angular/core": "^4.0.0",
    "@angular/forms": "^4.0.0",
    "@angular/http": "^4.0.0",
    "@angular/material": "^2.0.0-beta.8",
    "@angular/platform-browser": "^4.0.0",
    "@angular/platform-browser-dynamic": "^4.0.0",
    "@angular/router": "^4.0.0",
    "@ngx-meta/core": "^0.4.0-rc.2",
    "@ngx-translate/core": "^7.1.0",
    "@ngx-translate/http-loader": "^1.0.1",
    "angular2-moment": "^1.6.0",
    "core-js": "^2.4.1",
    "express": "^4.15.4",
    "lodash": "^4.17.4",
    "ng2-pdf-viewer": "^1.1.1",
    "ng2-translate": "^5.0.0",
    "rxjs": "^5.4.1",
    "web-animations-js": "^2.3.1",
    "zone.js": "^0.8.14"
},

landing.component.ts

import { Component, HostBinding, ChangeDetectionStrategy } from '@angular/core';
import { stateTransition } from '../routing.animations';

@Component({
  selector: 'cp-landing',
  templateUrl: './landing.component.html',
  styleUrls: ['./landing.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  animations: [ stateTransition() ]
})
export class LandingComponent {
  @HostBinding('@stateTransition') '';
}

routing.animations.ts

import { trigger, state, animate, style, transition } from '@angular/animations';

export function stateTransition() {
  return trigger('stateTransition', [
    state('void', style({
      transform: 'translateY(50%)',
      opacity: 0
    })),
    state('*', style({
      transform: 'translateY(0%)',
      opacity: 1
    })),
    // enter animation
    transition('void => *', animate('.5s 500ms')),
    // exit animation
    transition('* => void', animate('.5s'))
  ]);
}

I tried coming up with a Fiddle but I'm not being able to reproduce the error.

like image 302
Roman Oxman Avatar asked Aug 22 '17 17:08

Roman Oxman


3 Answers

The web animations API requires a polyfill on Safari, I believe. And its not enabled by default. You'll just need to install the polyfill and then add a line or two to your polyfills.ts file in your app's /src folder.

See here: How to add Web Animations API polyfill to an Angular 2 project created with Angular CLI

(copying the best answer below)

Adding the polyfill with the newer, Webpack version of Angular CLI is easier:

Install with

npm install web-animations-js --save

Add to polyfills.ts:

 require('web-animations-js/web-animations.min');

It also worked if I do

 import 'web-animations-js/web-animations.min';
like image 157
diopside Avatar answered Nov 20 '22 09:11

diopside


The source of this issue is ngx-pdf-viewer depending on an old 1.x version of pdf.js which incorrectly polyfills web-animations on all versions of iOS (https://github.com/mozilla/pdf.js/blob/6521d2fd941f26a1b7ae9b97f71842e49f5ff241/src/shared/compatibility.js#L571-L575). This was done to fix a bug in older iOS versions but causes newer iOS versions that have a working requestAnimationFrame to break.

like image 4
E. Gregious Avatar answered Nov 20 '22 09:11

E. Gregious


Finally the error was quite too specific, the ngx-pdf-viewer library was causing this error at the moment of loading the component.

What I did to solve it was to compile the component dynamically only when the user is in desktop and I took another approach for mobile.

My code:

const template = '<pdf-viewer [src]="src"' +
                 '            [show-all]="true"' +
                 '            [zoom]="2">' +
                 '</pdf-viewer>';

const tmpCmp = Component({template: template})(class {});
const tmpModule = NgModule({
  declarations: [tmpCmp, PdfViewerComponent],
  imports: [CommonModule]}
)(class {});

this._compiler.compileModuleAndAllComponentsAsync(tmpModule)
.then((factories) => {
  const f = factories.componentFactories[0];
  const cmpRef = f.create(this._injector, [], null, this._m);
  cmpRef.instance.src = this.data.src;
  this.pdfViewerContainer.insert(cmpRef.hostView);
});
like image 2
Roman Oxman Avatar answered Nov 20 '22 10:11

Roman Oxman