angular / material

Material design for AngularJS
https://material.angularjs.org/
MIT License
16.55k stars 3.39k forks source link

Angular Material very slow in IE 11 #8329

Closed mtorres0612 closed 8 years ago

mtorres0612 commented 8 years ago

I've overhauled our application using world's finest Angular Material. It works fast on Chrome and Firefox.

Below is the gif which illustrates the behavior of our application if run in Chrome: animation_chrome

Below is the gif which illustrates the behavior of our application if run in IE 11: animation

Issue Details:

Angular Versions / Relevant Script used:

Additional Information:

berndvdveen commented 8 years ago

+1. Very low performance on any animated transitional behaviour

graphefruit commented 8 years ago

@mtorres0612 Have a look here #8218 and here https://github.com/angular/material/issues/864#issuecomment-200325064

This works wonders on my side for performance problems.

Try to disable animations aswell as ripple effects. They can be disabled on the root element <body md-no-ink>

mtorres0612 commented 8 years ago

@berndvdveen - thanks for the info. found any workaround on this? @graphefruit - thank you for the information! In line with this, what am I expecting? a 100% lag-free AM website in IE11?

If in that case yes, then here are the details of my test:

What else should I apply?

Thank you in advance guys.

graphefruit commented 8 years ago

@mtorres0612 100% lag-free? I don't know, try it and you'll see. I reduced my load times from 11 seconds to 4-5 seconds on page navigation in my cordova-app on WP8.

With this fixes you disable all transitions (which I disabled in this CSS, need to be changed in RC-4 because of private classes). Also you disable all ripple-effects which is causing terrible performance problems. Transitions you can find with the Google-Chrome debugger "Animations".

For sure you'll get performance, and thats what you want right?

Update: Also make sure you decimate watchers (one-time-bindings) and disable the debug-information: $debugInfo With this line you cant access the scope via JavaScript angular.element(document).scope()

This takes lot of times but will pay.

@mtorres0612 Here is my CSS fix, also box-shadows remove improving the performance drastically, because the box-shadows need to be rerenderd. If you want box-shadows take borders. This animations don't contain all elements maybe you need to find some more alone.


/*
 ******************************************
 ******************************************
   Disable animations
 ******************************************
 ******************************************
*/

/** disable transitions and animations for tab-contents**/
md-tab-content.md-right:not(.md-active) {
    animation: none;
    -webkit-animation: none;
}

md-tab-content.md-left:not(.md-active) {
    animation: none;
    -webkit-animation: none;
}

md-tab-content.md-left:not(.md-active) * {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

md-tab-content.md-right:not(.md-active) * {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

.md-tab {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

/**disable slider animation for thumbs **/
md-slider .md-focus-thumb {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
    animation: none !important;
    -webkit-animation: none !important;
}
md-slider ._md-thumb{
     transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}
md-option{

     transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}
/*Disable transition for opening menu.*/

/**disable on dialogs**/
md-dialog._md-transition-out {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

md-dialog._md-transition-in {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

/**Disable on ripple buttons**/
.md-ripple.md-ripple-placed {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

.md-ripple-container, .md-ripple-placed {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

/**Disable checkbox **/
md-checkbox ._md-icon {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

md-tabs-wrapper md-next-button, md-tabs-wrapper md-prev-button
{
     transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

md-pagination-wrapper {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

._md-subheader-wrapper:not(.md-sticky-no-effect) {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

._md-sticky-clone[sticky-state=active]:not(.md-sticky-no-effect) ._md-subheader-inner {
    -webkit-animation: none !important;
    animation: none !important;
}

/**Disable on all other buttons**/
.md-button {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

/**Disable fading labels**/
md-input-container label {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

/** Disable slider animations**/
md-slider ._md-thumb-container, ._md-focus-ring, ._md-track-fill, ._md-thumb {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

md-input-container .md-char-counter, md-input-container .md-input-message-animation {
    transition: none !important;
}

/*  Disable dropdown animations*/

md-select-menu {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

md-select-menu md-content {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

._md-select-menu-container._md-leave {
    transition: none !important;
    transition-duration: 0ms !important;
    transition-delay: 0ms !important;
}

md-tab-content {
    -webkit-transition: none !important;
    -moz-transition: none !important;
    -ms-transition: none !important;
    -o-transition: none !important;
    transition: none !important;
}

md-ink-bar.md-right {
    -webkit-transition: none !important;
    -moz-transition: none !important;
    -ms-transition: none !important;
    -o-transition: none !important;
    transition: none !important;
}

md-ink-bar.md-left {
    -webkit-transition: none !important;
    -moz-transition: none !important;
    -ms-transition: none !important;
    -o-transition: none !important;
    transition: none !important;
}

md-tab-content.md-right {
    -webkit-transition: none !important;
    -moz-transition: none !important;
    -ms-transition: none !important;
    -o-transition: none !important;
    transition: none !important;
}

md-tab-content.md-left {
    -webkit-transition: none !important;
    -moz-transition: none !important;
    -ms-transition: none !important;
    -o-transition: none !important;
    transition: none !important;
}

/*
 ******************************************
 ******************************************
   Disable box shadows
 ******************************************
 ******************************************
*/

md-card {
    border: 1px solid rgba(0, 0, 0, .14) !important;
    box-shadow: none !important;
    -webkit-box-shadow: none !important;
    -moz-box-shadow: none !important;
}

md-dialog {
    border: 1px solid rgba(0, 0, 0, .14) !important;
    box-shadow: none !important;
    -webkit-box-shadow: none !important;
    -moz-box-shadow: none !important;
}

md-select-menu {
    border: 1px solid rgba(0, 0, 0, .14) !important;
    box-shadow: none !important;
    -webkit-box-shadow: none !important;
    -moz-box-shadow: none !important;
}

.md-button.md-raised {
    border: 1px solid rgba(0, 0, 0, .14) !important;
    box-shadow: none !important;
    -webkit-box-shadow: none !important;
    -moz-box-shadow: none !important;
}

md-toast .md-toast-content {
    /**no border needed here**/
    box-shadow: none !important;
    -webkit-box-shadow: none !important;
    -moz-box-shadow: none !important;
}
mtorres0612 commented 8 years ago

@graphefruit - sorry for the very late reply. I was given other tasks that's why I failed to respond swiftly. Anyway, thank you for your information on this! Will test it immediately with my application. Will let you know the status of this the soonest.

Cheers

smolyakoff commented 8 years ago

+1

mtorres0612 commented 8 years ago

@graphefruit - again, sorry for the long wait. Finally! I've tested again my application after adding above CSS tweaks. Noticeable results:

UI:

Performance:

DEMO: animation

Credits to you @graphefruit on this one. What a great tweak.

While this is great, still hoping that AM fully performs normally in IE11, just as the way it behaves in Chrome and Firefox. IE sucks, says my mentor years ago.

Cheers,

graphefruit commented 8 years ago

@mtorres0612 Your welcome, I'm glad it helped out. Some more points here: Did you overwrote $MD_THEME_CSS aswell with: $provide.constant('$MD_THEME_CSS', ''); And copy the content into an own stylesheet and include it?

Did you also disable the debugInfo?

Also track your watchers and try to reduce them (PS: This is not working if you're disabling the debug information $compileProvider.debugInfoEnabled(true);)

var logWatches = function () {
        setInterval(function () {

            (function () {
                var root = angular.element(document.getElementsByTagName('body'));

                var watchers = [];

                var f = function (element) {
                    angular.forEach(['$scope', '$isolateScope'], function (scopeProperty) {
                        if (element.data() && element.data().hasOwnProperty(scopeProperty)) {
                            angular.forEach(element.data()[scopeProperty].$$watchers, function (watcher) {
                                watchers.push(watcher);
                            });
                        }
                    });

                    angular.forEach(element.children(), function (childElement) {
                        f(angular.element(childElement));
                    });
                };

                f(root);

                // Remove duplicate watchers
                var watchersWithoutDuplicates = [];
                angular.forEach(watchers, function (item) {
                    if (watchersWithoutDuplicates.indexOf(item) < 0) {
                        watchersWithoutDuplicates.push(item);
                    }
                });

                console.log("Watcher:" + watchersWithoutDuplicates.length);
            })();

        }, 1000);
    }

If you want to know what is realy watched have a look on watchersWithoutDuplicates

Also it looks like in your last demo that some ripple/animation effects are still in use. If you want to get rid of them, install chrome and track down the animations. Have a look for it here: https://www.youtube.com/watch?v=U9xfYbKxosI

Update: Also use one-time-bindins in your ng-repeat, the trick is to set your repeat variable to undefined, and define an ng-if. The undefined doesn't dismiss the watcher, when the variable is out of the undefined state, the watcher gets destroyed. ng-if="::(array!=undefined?true:undefined)" ng-repeat="obj in ::array" If you cant use a one-time-binding in your ng-repeat, do it below in your bindings <div>{{::value}}</div>

Also if you got large sets to render use virtual-repeat instead of normal ng-repeat.

If needed we can do a short hangout cast aswell.

Greetings Graphefruit

mtorres0612 commented 8 years ago

@graphefruit - hi! sorry for the year-long response, just busy 9gagging (kidding). Thank you for the information. I won't be providing screencasts since it is hard to illustrate the changes just by showing gifs (in this state).

Observations/Results:

Thoughts:

Again, thank you @graphefruit for your efforts to accommodate inquiries from a newbie.

Cheers,

mtorres0612 commented 8 years ago

@ThomasBurleson @crisbeto - Thank you for acknowledging this one :+1:

graphefruit commented 8 years ago

@mtorres0612 Your welcome, glad I can help. After I'm still digging for performance I found some more points:

  1. JS: Replacing unneeded $interval or $timeout with the normal "native" JavaScript events: setInterval,setTimeout made less promise-calls and speed up.
  2. JS: Using requestAnimationFrame instead setTimeout (speeded some functions up from 140 to 30ms. Care on this 2 methods, maybe you need to trigger $scope.$apply / asyncApply yourself. You'll get more performance because the promises will not trigger all day long. You can debug this very good on IE if you do a breakpoint in your angular.js to see which one triggers
    self.defer = function(fn, delay) {
            var timeoutId;
            outstandingRequestCount++;
            timeoutId = setTimeout(function() {
                delete pendingDeferIds[timeoutId];
                completeOutstandingRequest(fn);
            }, delay || 0);
            pendingDeferIds[timeoutId] = true;
            return timeoutId;
        };
  1. JS: $httpProvider.useApplyAsync(true); https://docs.angularjs.org/api/ng/provider/$httpProvider
  2. AM - CSS: Remove the border-radius, IE has performance issues with repainting here.
.md-button {
    border-radius:0px!important;
}
.md-toast-content {
    border-radius: 0px !important;

md-dialog {
    border-radius: 0px !important;
}
md-card {
    border-radius:0px;
}

Repaint: Specally IE has repaint problems. Your JavaScript can affect this aswell. So maybe you force the layout to repaint all day long (if you set them or get them) e.g. repaint There are 20 more articles for position:fixed etc.

Greetings

PS: Did you found some other things aswell? After you said you came down from 30% to 5-10%?

mtorres0612 commented 8 years ago

@graphefruit - thank you for this wonderful information!

Here's a quick feedback of how I implemented the 4 items you mentioned:

  1. "Replacing unneeded $interval..." - I guess no need to do this one for me; I'm not currently using this.
  2. "Using requestAnimationFrame" - Sorry if my AM level is weak but how should I correctly do this? An example use will do, not the actual code that I will write. I modified the latest angular.js, but this is giving me errors:

BEFORE:

self.defer = function(fn, delay) {
            var timeoutId;
            outstandingRequestCount++;
            timeoutId = setTimeout(function() {
                delete pendingDeferIds[timeoutId];
                completeOutstandingRequest(fn);
            }, delay || 0);//EXISTING
            pendingDeferIds[timeoutId] = true;
            return timeoutId;
        };

AFTER:

self.defer = function (fn, delay) {
      var timeoutId;
      outstandingRequestCount++;
      timeoutId = requestAnimationFrame(repeatOften(timeoutId, fn));//MODIFIED; INSIDE repeatOften ARE THE 2 STATEMENTS INSIDE SETTIMEOUT BEFORE
      pendingDeferIds[timeoutId] = true;
      return timeoutId;
  };
  1. "$httpProvider.useApplyAsync(true);" - DONE
  2. "Remove the border-radius..." - DONE

RESULTS:

THOUGHTS:

@graphefruit - again thank you for this. Big fan here so I've been checking this asap on my wee time

Cheers,

ThomasBurleson commented 8 years ago

@crisbeto - next week, let's discuss how to get the IE perf improvements into ngM1 for v1.1.0.

graphefruit commented 8 years ago

Hello @ThomasBurleson if I can help you here just give me a bump.

ThomasBurleson commented 8 years ago

@graphefruit - can you email with direct message thomasburleson@gmail.com. I have some ideas to suggest...

mtorres0612 commented 8 years ago

Hi,

May I know the current status of this? Just for information purposes.

I'm thankful my project has been deferred, for a while. Not sure when it will commence, but much better if the application will be ready/stable/cross-browser compatible.

Thank you in advance! Happy to be part of this masterpiece :)

Cheers,

graphefruit commented 8 years ago

Hi @mtorres0612 , My actual understanding is that there will be a documentation and small code fixes (ripple effect e.g.) The user needs to insert this data in his app/website himself then. Have a look here: https://github.com/angular/material/commit/ea62bc2de4dc09e0d7095b6f96d0746d20baa14a

ThomasBurleson commented 8 years ago

Tab performance has been significantly improved. Fixed.

david-gang commented 8 years ago

Hi @ThomasBurleson ,

My conclusion was that the main problem at least in my applications are the layout calculations. Somehow, ie11 is overwhelmed by the css of angular material. Even if you make a md-list of 40 items with icons inside them where a click on the icon opens a md-dialog with a hello world string, it will be slow. I am not sure that in the scope of angular material 1 there is something which can be done, but hope that this may be addressed in material 2. Another option would be that microsoft will release a better html engine which will be on the scale of webkit but i am not really optimistic :-)

Thanks a lot, David

crisbeto commented 8 years ago

@david-gang I would really appreciate a codepen/demo of IE being slow, otherwise it's hard to pinpoint the issue. We decided to close this because the only instance I've found IE to be really slow was on 1.0.9 and a certain config of md-tabs, but that was addressed by @topherfangio and isn't an issue in 1.1.

david-gang commented 8 years ago

Hi,

Sure :-)

http://codepen.io/david-gang/pen/RRJjxG

If you open this in ie11 and click on the icons you willl see slowness. This is basically a md-list with not too much content.

Thanks, David

crisbeto commented 8 years ago

Alright, this should be easier to benchmark. I'll reopen it.

graphefruit commented 8 years ago

Just a side notice: I found out that the CSS files realy had more then 55k lines (material + theme) Thats realy a huge factor for the IE11 engine. After I test stripped the CSS down to actually 4k lines (painly road), even my android and iOS app are starting even more faster. Sadly the rendering process for 20 list-items still need more then 2 seconds on a Windows Phone 10.

ThomasBurleson commented 8 years ago

@crisbeto - let's open a new issue with David's codepen. @graphefruit - please submit a new issue with your recent comment/issue. I think the mdList may need an overhaul.