Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS transition events extremely slow on IOS

I am developing a single-page Javascript application that runs on desktop browsers and also on mobile devices via Cordova/Phonegap.

I have a slide-out menu that is implemented using CSS transitions. I noticed that it works well on desktop browsers and android. However, on IOS there are serious performance issues. The transition does not appear to start on time, but once it starts the rendering and duration looks fine. The time between starting the transition and the transitionend event is way higher on IOS than other platforms. For example, the duration of the transition is 300ms but I'm not getting the transitionend event for 1500ms. On all other platforms, I get the transitionend event in 325-350ms.

Transitionend Event:

  • Expected: 350ms
  • Actual: 1500ms

Platforms:

  • Cordova 6.3.1
  • Xcode 8.1 GM Seed
  • IOS 10.1

Here is the CSS for the menu div. To slide-out the menu, I add the 'open' class. To close the menu, I remove the 'open' class. I've tried transitioning on the 'left' property and 'transform' property, but the results are identical.

/* Nav Menu */

#navmenu {
    position: absolute;
    top: 0;
    width: 90%;
    max-width: 400px;
    z-index: 20;
    height: auto;
    background-color: white;
    /*
    -webkit-transform: translate3d(-100%,0,0);
       -moz-transform: translate3d(-100%,0,0);
        -ms-transform: translate3d(-100%,0,0);
            transform: translate3d(-100%,0,0);
    -webkit-transition: -webkit-transform 300ms ease;
       -moz-transition:    -moz-transform 300ms ease;
        -ms-transition:     -ms-transform 300ms ease;
         -o-transition:      -o-transform 300ms ease;
            transition:         transform 300ms ease;
    */
    left: -100%;
    -webkit-transition: left 300ms ease;
       -moz-transition: left 300ms ease;
        -ms-transition: left 300ms ease;
         -o-transition: left 300ms ease;
            transition: left 300ms ease;
}

#navmenu.open {
    /*
    -webkit-transform: translate3d(0,0,0);
       -moz-transform: translate3d(0,0,0);
        -ms-transform: translate3d(0,0,0);
            transform: translate3d(0,0,0);
    -webkit-transition: -webkit-transform 300ms ease;
       -moz-transition:    -moz-transform 300ms ease;
        -ms-transition:     -ms-transform 300ms ease;
         -o-transition:      -o-transform 300ms ease;
            transition:         transform 300ms ease;
    */
    left: 0;
    -webkit-transition: left 300ms ease;
       -moz-transition: left 300ms ease;
        -ms-transition: left 300ms ease;
         -o-transition: left 300ms ease;
            transition: left 300ms ease;
}

Question: What might be causing the delay in starting the transition, only on IOS platforms? Are there any known solutions to circumvent the problem or speed things up? I have other transitions in the app that take over 5s to start, making the app unusable. I'm hoping the menu solution will apply throughout the app. Thanks for any help or ideas you can provide.

Here is the instrumented Javascript code that I use to open/close the menu...

utilities.addEventListeners(navMenuButtonDiv, function () {
    var start = Date.now();
    var menuDiv = navMenu.getDiv();
    if (menuDiv.classList.contains('open')) {
        menuDiv.classList.remove('open');
    } else {
        menuDiv.classList.add('open');
    }
    var handler = function (event) {
        console.log('Transition: ' + (Date.now() - start));
        menuDiv.removeEventListener('webkitTransitionEnd', handler, true);
    };
    menuDiv.addEventListener('webkitTransitionEnd', handler, true);
 };
like image 890
jmelvin Avatar asked Oct 28 '16 03:10

jmelvin


1 Answers

When moving elements around the screen, you want to maximize performance. Instead of transitioning the left property, you're better off using translation. Using translation, the device will use its GPU to render the onscreen change, on a layer above the DOM. This will result in a smoother, more performant transition.

Have a look at this example. Besides using a transform instead of changing the left property, notice that I removed a bit of redundancy. You don't need to redeclare the transition on the active state.

var open = document.getElementById("open"),
    close = document.getElementById("close"),
    nav = document.getElementById("navmenu");

open.addEventListener("click", function() {
  nav.classList.add("open");
}); 

close.addEventListener("click", function() {
  nav.classList.remove("open");
});
#navmenu {
  position: absolute;
  top: 0;
  left: 0;
  width: 90%;
  max-width: 400px;
  z-index: 20;
  height: auto;
  background-color: white;
  transform: translate3d(-100%, 0, 0);
  -webkit-transition: transform 300ms ease-in-out;
     -moz-transition: transform 300ms ease-in-out;
      -ms-transition: transform 300ms ease-in-out;
       -o-transition: transform 300ms ease-in-out;
          transition: transform 300ms ease-in-out;
}

#navmenu.open {
  transform: translate3d(0, 0, 0);
}

button {
  margin-top: 100px;
}
<div id="navmenu">stuff in here</div>
<button id="open">Open Menu</button>
<button id="close">Close Menu</button>
like image 144
Andy Hoffman Avatar answered Oct 19 '22 00:10

Andy Hoffman