z80crew
z80crew

Reputation: 1200

Ionic App jerky while smooth in Mobile Browser

I've written an Ionic app, where nine images are positioned inside a DIV. Here's the relevant snippet from my CSS file:

.mydiv {
    position: relative;
    overflow: hidden;
}
.mydiv > img {
    position: absolute;
    box-shadow: 6px 6px 8px #aaaaaa;
}

I'm using $ionicGesture.on('drag', function(e) {}) to move these images around, following the move of my finger. That's the relevant part from my controller.js:

jQuery('.mydiv > img').each(function(i, myImg) {
    jQuery(myImg).css('left', initLeft + e.gesture.touches[0].pageX - initX);
    jQuery(myImg).css('top', initTop + e.gesture.touches[0].pageY - initY);
}

So, basically, every time the drag event occurs, I move the images by changing their 'left' and 'top' attribute.

The problem is though, that this solutions works wonderfully in a browser, but is horribly lagging as an Ionic app on my iPhone 5. When I put the contents of the Ionic 'www' folder on a web server and access the web app with my iPhone's web browser, the movement is extremely smooth. So, obviously, it's not a performance issue, but Ionic is doing something that makes the app stuttering. Using the Chrome profiler on my Mac, I learned nearly all of the time is used by internal Ionic functions and not by my client code. This seems to be another hint, that this is some kind of Ionic problem.

Why is my app, that runs perfectly smooth in my mobile browser on the same device gets jerky as hell when packed as an Ionic app?

Additional information:

Those images I drag along are scaled with jQuery(myImg).width(). I now replaced them with pre-scaled image files and got rid of the box-shadow style. Now the lagging is reduced but still worse than in the mobile browser. Is it possible, that the mobile Safari on my iPhone uses the GPU for Javascript induced style changes, while for a Cordova app the GPU is not used?

Upvotes: 1

Views: 441

Answers (3)

z80crew
z80crew

Reputation: 1200

Using transform: translate3d() does the trick. Animations are now extremely smooth. Because I had some trouble to convert my code from the CSS left-top-style to using translate3d(), here is what I did as a reference for other users:

jQuery('.mydiv > img').each(function(i, myImg) {
    var x = initLeft + e.gesture.touches[0].pageX - initX;
    var y = initTop + e.gesture.touches[0].pageY - initY;
    jQuery(myImg).css({
        '-webkit-transform' : 'translate3d('+x+'px, '+y+'px, 0px)',
        'transform'         : 'translate3d('+x+'px, '+y+'px, 0px)'
    });
}

I even can switch on box-shadow again and the animation stays silky smooth.

Upvotes: 0

Vlad Bezden
Vlad Bezden

Reputation: 89517

Looks like you have animation issue. Here is an article from Ionic team that explains on how to deal with this.

What’s happening is that as you try to animate the left property of the items, the web view will have to go back and try to recalculate that item’s position. This happens for a lot of CSS properties.

Thankfully, we have other ways to animate these items.

Let’s change that left property to transform and set the value to use translate3d. Translate3d is a special CSS value that will actually move the animation to the device’s GPU. This is called hardware accelerated animation.

@-webkit-keyframes slideIn {
  0% {
    left: -100%;
  }
  100% {
    left: 0;
  }
}

@-webkit-keyframes slideInSmooth {
  0% {
    -webkit-transform: translate3d(-100%,0,0);
  }
  100% {
    -webkit-transform: translate3d(0,0,0);
  }
}


.slide-in{
   -webkit-animation: slideInSmooth ease-in 1; 
  animation: slideInSmooth ease-in 1;
  -webkit-animation-fill-mode: forwards;
  animation-fill-mode: forwards;
  -webkit-animation-duration: 750ms;
  animation-duration: 750ms;
}

Upvotes: 1

Bangkokian
Bangkokian

Reputation: 6644

I haven't seen your animation or your code but my guess is that the device GPU isn't carrying the weight it should.

Obviously without seeing your app, I'm doing some guessing here -- but from what you've described this may be the issue:

There are a couple ways to get the GPU to kick in, but the most basic is not to animate using "top" and "left". Using standard CSS positioning like top/left to animate DOM elements works, but it's the slowest way to go and as you've discovered: performance tends to suck in many environments. (Particularly when you're using complex images or blurs/shadows).

A far better way to go is to use translateX / translateY which engages the GPU. That should give you silky smooth performance. Using translateX and translateY will also allow for subpixel rendering and give a far smoother appearance than using top/left which blips along pixel-by-pixel.

There's an excellent discussion of this by a Chrome team member here: https://www.youtube.com/watch?v=NZelrwd_iRs

Lastly, if for some reason you must use top/left -- a quick and somewhat hacky method to get the GPU to kick in is to set the parent DIV to translate3d(0, 0, 0);

This forces the parent layer into the hands of the GPU (via 3D transforms) during the browser-paint, and the subsequent standard CSS top/left changes that you're using should now render using the GPU. Needless to say, this second method isn't the best way to go in terms of best-practices, so go with CSS transforms first if that's an option.

Upvotes: 1

Related Questions