7

During my work on a CSS image zooming feature I encountered a performance problem in mobile Chrome.

Description: If I try to scale images by adding the CSS transform property directly to the image everything works well. The zooming transition is smooth like butter with 60fps (JSFiddle).

<img src="http://placehold.it/1000x1500" style="transform:matrix(2, 0, 0, 2, 0, 0);" />

The Problem: But if I wrap the image in a div container and try to transform the container the transition is very laggy (JSFiddle). The transition starts with a big delay and isn't smooth. It seems to be a mobile Chrome only problem because it doesn't happen in other browsers like Firefox on Android, just on my mobile device (Nexus 5) and some other Android devices.

<div style="transform:matrix(2, 0, 0, 2, 0, 0);">
    <img src="http://placehold.it/1000x1500" />
</div>

Does someone know whats wrong with the CSS or HTML structure?

Johann
  • 594
  • 5
  • 12
  • I think I've found out what the problem is. It seems to be an general Chrome issue, not just an mobile Chrome issue. If you check this [example](http://johann-ulbrich.de/zoom/2016-10-28/) you can see that the zooming (especially zooming out) isn't just laggy in mobile Chrome. If there is a big image and parts of it are not in the viewport, the browser doesn't have the whole image in his internal rasterization cache. So the image will be processed when necessary. – Johann Oct 28 '16 at 16:17

4 Answers4

1

Greensock plugin is by far the best in terms of performance I've seen. Some time ago I make a deep study and some tests, and it's been by far, one of the best plugins to animate elements.

It's lightweight, fast and easy to use.

What about performance? Take a look on your own: https://www.greensock.com/js/speed.html

Here's your example using gsap library:

var content = document.getElementById('zoom-container').children[0];

document.getElementById('zoom-in').addEventListener('click',
  function zoomIn() {
  TweenLite.to(content, 1.5, {scale:1});
  }, false);

document.getElementById('zoom-out').addEventListener('click',
  function zoomOut() {
   TweenLite.to(content, 1.5, {scale:0.2});
  }, false);
* {
  margin: 0;
  padding: 0;
}

img {
  display: inline-block;
}

html,
body {
  height: 100%;
  width: 100%;
}
#zoom-container div {
  position: relative;
  /*some prefix*/-transform-origin: 0 0;
  transform-origin: 0 0;
}
#zoom-toolbar {
  position: absolute;
  top: 0;
  left: 0;
  
}

.zommIn {
  
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script>

  <div id="zoom-container" style="height:100%; width:100%;">
    <div>
      <img src="http://placehold.it/1000x1500" />
    </div>
  </div>

  <div id="zoom-toolbar">
    <button id="zoom-in">Zoom in</button>
    <button id="zoom-out">Zoom out</button>
  </div>
Jordi Flores
  • 2,080
  • 10
  • 16
  • Hi, i created a [fiddle](https://jsfiddle.net/br03Lrpq/1/) to test it on mobile Chrome. I can see a difference between the CSS transition and the transition with the plugin. With the plugin the end of the transition looks better but it seems to be more bumpy in in general. It's not smooth, unfortunately. – Johann Oct 28 '16 at 16:41
1

Use JavaScript to set classnames, but let CSS completely handle the transitions and trigger GPU acceleration.

I would suggest using the onclick HTML event attribute, to set triggers for two functions, zoomIn and zoomOut.

HTML

    <div id="zoom-container">
      <img src="http://placehold.it/1000x1500" />
    </div>

    <div id="zoom-toolbar">
      <button id="zoom-in" onclick="zoomIn()">Zoom in</button>
      <button id="zoom-out" onclick="zoomOut()">Zoom out</button>
    </div>

You now have two functions, that set desired CSS classnames.

JavaScript

    function zoomIn() {
      var element = document.getElementById("zoom-container");
      element.className = 'zoomed-in';
    };

    function zoomOut() {
      var element = document.getElementById("zoom-container");
      element.className = 'zoomed-out';
    };

To achieve the desired animation, the CSS can now be much simpler as well.

CSS

    #zoom-toolbar {
      position: absolute;
      top: 0;
    }

    #zoom-container.zoomed-out {
      transition: transform 0.3s ease-in-out;
      transform: matrix(0.2, 0, 0, 0.2, 0, 0);
    }

    #zoom-container.zoomed-in {
      transition: transform 0.3s ease-in-out;
      transform: matrix(2, 0, 0, 2, 0, 0);
    }

From there, you can introduce additional CSS and test whether subsequent changes are breaking.

I've created a CodePen example to demonstrate.

A good article on smooth CSS transitions is here.

jacefarm
  • 6,747
  • 6
  • 36
  • 46
  • It's a good idea to use classes to transform things but I can't use it. The example is just the beginning of a bigger project with dynamic zoom levels. So it's necessary to add the transformation matrix via javascript. – Johann Oct 28 '16 at 16:22
  • Ah, I see. I would suggest modifying your question and bounty comments to reflect this requirement. – jacefarm Oct 28 '16 at 16:24
  • Just to test if your idea can improve the transformation performance I've updated my [example script](http://johann-ulbrich.de/zoom/2016-10-28-2/). But it's even laggy. But meanwhile I found out something new (see the comment below my initial question). – Johann Oct 28 '16 at 17:04
  • @Johann Desktop Chrome seems pretty smooth to me, but the lag is evident in both Chrome and Firefox mobile. I suspect the image size / weight is affecting the transform. Try cases for both a smaller and even larger image... – jacefarm Oct 28 '16 at 17:43
0

I tried out your code in chrome and the transformations were definitely lagging. Yes there is a problem with your code and frankly it's pretty simple. Simply write the transition again after the first transition, but add the prefix -webkit- to the start of it.

Please note that -webkit- only works for Android, Google, and Safari.
-moz- works for Mozilla Firefox.
-o- works for Opera.
-ms- works for Internet Explorer

NathanielSantley
  • 299
  • 1
  • 16
  • Hi thanks for your reply. I've checked out your improvements (see https://jsfiddle.net/sd4f2cdj/2/) but nothing had changed. Im pretty sure that Chrome supports the unprefixed _transition_ and _transform_ property for quite a while. – Johann Oct 23 '16 at 12:13
  • srry. i meant to say transform – NathanielSantley Oct 23 '16 at 20:06
  • Thanks for the update. Have you checked out my fiddle? I've added _transform_ prefix without success, unfortunately. – Johann Oct 24 '16 at 08:48
0

You can

  • try to put will-change: transform on the container element.
  • Make the container element position: relative and the image position: absoulte and fill the container completely. Might involve less calculations each frame for the browser.
Red Mercury
  • 3,971
  • 1
  • 26
  • 32
  • Thanks, I tried it out (https://jsfiddle.net/sd4f2cdj/3/) but brought no improvement, unfortunately. – Johann Oct 25 '16 at 09:05