最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Force correct CSS3 transform percentage interpretation on Android - Stack Overflow

programmeradmin1浏览0评论

tl;dr? Get the mechanism demonstrated in the link below to behave with GPU acceleration on Android Chrome & default browser.

UPDATE 2 (2014-01-13 13:25:30Z): According to bref.it's comment below, the reported behaviour is fixed as of Android 4.4 KitKat — but the fix I described below now breaks it! Sod's law.

UPDATE 1 (2012-11-01 17:54:09Z): The buggy behaviour is inferrable from the transformation matrix as reported by the computed style of the transformed element, which returns a pixel value. I'll try to write a Modernizr test for this to pave the way for any possible solutions.

I've developed a mechanism for sliding a container to reveal full-width, horizontally arranged sub-sections. Sliding tabs, basically. Because there's a lot of performance intensive stuff and elaborate Javascript going on, I want to keep JS to the minimum and do as much of the purely stylistic in CSS. I think I've done pretty well, considering — JS just changes an attribute on the wrapper:

(w/ left)

/ (mobile devices can append /show/ to these fiddle URLs to see the results by themselves)

A word on how this works: treating the tabs as inline-blocks allows me to specify white-space: nowrap (the rest of the code in the last couple of rules essentially collapses the whitespace between the tabs) and allow them to stack horizontally without clearing / returning, all the while each maintaining the full width of their parent. From there, setting a negative left offset to the wrapper does the magic. Cool, huh?

Now, a bit of context: the interface I'm developing is to be run in native mobile applications — the application's core functionality relies on cutting edge mobile-specific technology (don't ask — NDA) — via a UIWebView, and the only platform that currently supports the technology in question is Android.

Here my problem is twofold: transform: translate works a /lot/ smoother (translate3d even more so) than left or margin-left transitions, to a point that is really really desirable, borderline crucial — especially on Android, seeing as non-translated transitions are still stutteringly glacial on the latest phones with the latest OS. With that in mind, the crux of the issue is that Android seems to infer the box model differently when relating to translate. To demonstrate, here's a transform-based version of the same thing, which does the same thing as the previous fiddle, and works on all browsers that support translate3d…

(w/ translate)

If you examine this on iPhone (again, by appending /show), you'll notice improved frame rate on iPhones. The same goes for Firefox running on Android, and arguably on Chrome and the default browser on Android too, except that here, the translateX offset of -100% somehow refers to the space occupied by all three tabs, so the wrapper slides just enough such that none of the tabs are visible. This is odd, since transform percentages are specified as relating to the full box model of the element being transformed — and computed style unambiguously describes the wrapper width as being the same as its parent (not, as the result implies, stretched 3-fold to accomodate the tabs).

Can we describe this as a bug?

As far as I can tell, there's no pure-CSS (I'm not averse to media query feature detection, CSS vendor forking or property hacking) method of infering and resolving this problem. In fact the only way of making this same CSS mechanic work that I can think of now is sniffing the UA string for Android and applying different rules conditionally. Yuk! If I'm to make an Android-WebKit-only solution, and break functionality everywhere else, I can take the de facto behaviour in to account and make the percentages refer to fractions:

(w/ translate — for Android update: unnecessary and breaks on Android 4.4 KitKat)

/

Not ideal, as this makes the UI browser-specific and requires that we know up front the number of tabs in a group before writing our CSS. But this doesn't even completely solve the problem there, as, on orientation change (and this is really weird), the offset switches to the specification-correct behaviour displayed by other browsers!

I've also tried applying the translation to the actual tabs, which again works everywhere else, but despite correctly picking up and applying the rules, Android's browser won't render the effect. Stranger and strangerer:

(w/ translate, working on Android's theory, not working on Android at all)

Any insights or ideas for a cross-browser solution much appreciated.

tl;dr? Get the mechanism demonstrated in the link below to behave with GPU acceleration on Android Chrome & default browser.

UPDATE 2 (2014-01-13 13:25:30Z): According to bref.it's comment below, the reported behaviour is fixed as of Android 4.4 KitKat — but the fix I described below now breaks it! Sod's law.

UPDATE 1 (2012-11-01 17:54:09Z): The buggy behaviour is inferrable from the transformation matrix as reported by the computed style of the transformed element, which returns a pixel value. I'll try to write a Modernizr test for this to pave the way for any possible solutions.

I've developed a mechanism for sliding a container to reveal full-width, horizontally arranged sub-sections. Sliding tabs, basically. Because there's a lot of performance intensive stuff and elaborate Javascript going on, I want to keep JS to the minimum and do as much of the purely stylistic in CSS. I think I've done pretty well, considering — JS just changes an attribute on the wrapper:

(w/ left)

http://jsfiddle.net/barney/VPJuq/ (mobile devices can append /show/ to these fiddle URLs to see the results by themselves)

A word on how this works: treating the tabs as inline-blocks allows me to specify white-space: nowrap (the rest of the code in the last couple of rules essentially collapses the whitespace between the tabs) and allow them to stack horizontally without clearing / returning, all the while each maintaining the full width of their parent. From there, setting a negative left offset to the wrapper does the magic. Cool, huh?

Now, a bit of context: the interface I'm developing is to be run in native mobile applications — the application's core functionality relies on cutting edge mobile-specific technology (don't ask — NDA) — via a UIWebView, and the only platform that currently supports the technology in question is Android.

Here my problem is twofold: transform: translate works a /lot/ smoother (translate3d even more so) than left or margin-left transitions, to a point that is really really desirable, borderline crucial — especially on Android, seeing as non-translated transitions are still stutteringly glacial on the latest phones with the latest OS. With that in mind, the crux of the issue is that Android seems to infer the box model differently when relating to translate. To demonstrate, here's a transform-based version of the same thing, which does the same thing as the previous fiddle, and works on all browsers that support translate3d…

(w/ translate)

http://jsfiddle.net/barney/EJ7ve

If you examine this on iPhone (again, by appending /show), you'll notice improved frame rate on iPhones. The same goes for Firefox running on Android, and arguably on Chrome and the default browser on Android too, except that here, the translateX offset of -100% somehow refers to the space occupied by all three tabs, so the wrapper slides just enough such that none of the tabs are visible. This is odd, since transform percentages are specified as relating to the full box model of the element being transformed — and computed style unambiguously describes the wrapper width as being the same as its parent (not, as the result implies, stretched 3-fold to accomodate the tabs).

Can we describe this as a bug?

As far as I can tell, there's no pure-CSS (I'm not averse to media query feature detection, CSS vendor forking or property hacking) method of infering and resolving this problem. In fact the only way of making this same CSS mechanic work that I can think of now is sniffing the UA string for Android and applying different rules conditionally. Yuk! If I'm to make an Android-WebKit-only solution, and break functionality everywhere else, I can take the de facto behaviour in to account and make the percentages refer to fractions:

(w/ translate — for Android update: unnecessary and breaks on Android 4.4 KitKat)

http://jsfiddle.net/barney/nFt5t/

Not ideal, as this makes the UI browser-specific and requires that we know up front the number of tabs in a group before writing our CSS. But this doesn't even completely solve the problem there, as, on orientation change (and this is really weird), the offset switches to the specification-correct behaviour displayed by other browsers!

I've also tried applying the translation to the actual tabs, which again works everywhere else, but despite correctly picking up and applying the rules, Android's browser won't render the effect. Stranger and strangerer:

(w/ translate, working on Android's theory, not working on Android at all)

http://jsfiddle.net/barney/EJ7ve/9

Any insights or ideas for a cross-browser solution much appreciated.

Share Improve this question edited May 23, 2017 at 12:21 CommunityBot 11 silver badge asked Oct 31, 2012 at 11:24 BarneyBarney 16.5k5 gold badges65 silver badges80 bronze badges 8
  • 1 I know from experience that working with css3 and Android is a pain (even more with the completely buggy Samsung browser). You could try to detect the width of you container and apply css transform via javascript offsetWidth or clientWidth. The meta viewport may also be the source of the problem. – kursus Commented Nov 23, 2013 at 17:02
  • This is what I ended up doing. It's sad because the presentation code was otherwise completely procedural… – Barney Commented Nov 23, 2013 at 18:49
  • Sorry I didn't realize the question was so old. As of today Android HTML5 (css and js) support is still a shame, what surprises me the most is that almost nobody seems to care. I stopped being worried by the ugliness of my Androids ports. – kursus Commented Nov 23, 2013 at 19:35
  • At the time I was writing an Android-exclusive HTML5 app, so the task of writing a heavily customised performant UI from scratch was somewhat eased by the single target. But it was a huge pain. The first major gotcha was that multiple transition properties were not supported. There were countless others, but I digress. – Barney Commented Nov 24, 2013 at 10:51
  • 1 All your examples are working perfectly on a just-updated (4.4) Nexus 7 2013 on the latest Chrome. All except the "for android" version, ironically, where the tabs only seem to slide by 40 or so pixels. – fregante Commented Dec 3, 2013 at 5:28
 |  Show 3 more comments

3 Answers 3

Reset to default 3 +200

I am using Android 2.3 and can't produce the exact problem you describe (with regards to your comment "-100% somehow refers to the space occupied by all three tabs"). However, I was experiencing other issues resulting in intermittent/jumpy transitions and some other weirdness I didn't really investigate.

For the sake of conversation, lets assume that the "wrapper" is 500px wide.

By using your inline-block/nowrap technique (which is a very under-appreciated layout technique from YUI), your 3 tab panels are taking up 1500px of horizontal space. Based on what you describe, you need to spoof the browser into thinking all of tab panels together are taking up 500px of horizontal space. Checkout my fiddle, which essentially uses some negative margins and translates to trick the browser in a way I think should work, but I can't test it. Either way, my example fixed the weirdness I was experiencing on my Android 2.3 - so you may want to use it either way.

http://jsfiddle.net/ryanwheale/EJ7ve/29/

(and the relevant CSS)

.tabs > * {
    width: 100%;
    margin-left: -100%;
}
.tabs > *:first-child {
    -webkit-transform: translate3d(0%, 0, 0);
    -moz-transform: translate3d(0%, 0, 0);
    -ms-transform: translate3d(0%, 0, 0);
    -o-transform: translate3d(0%, 0, 0);
    transform: translate3d(0%, 0, 0);
}
.tabs > *:first-child + * {
    -webkit-transform: translate3d(100%, 0, 0);
    -moz-transform: translate3d(100%, 0, 0);
    -ms-transform: translate3d(100%, 0, 0);
    -o-transform: translate3d(100%, 0, 0);
    transform: translate3d(100%, 0, 0);
}
.tabs > *:first-child + * + * {
    -webkit-transform: translate3d(200%, 0, 0);
    -moz-transform: translate3d(200%, 0, 0);
    -ms-transform: translate3d(200%, 0, 0);
    -o-transform: translate3d(200%, 0, 0);
    transform: translate3d(200%, 0, 0);
}
.tabs > *:first-child + * + * + * {
    -webkit-transform: translate3d(300%, 0, 0);
    -moz-transform: translate3d(300%, 0, 0);
    -ms-transform: translate3d(300%, 0, 0);
    -o-transform: translate3d(300%, 0, 0);
    transform: translate3d(300%, 0, 0);
}

Note: I tried using relative positioning instead of the transforms above, and I was still getting the same weirdness I described above.

I was experiencing a similar issue.

For me the problem was:

* {
    box-sizing: border-box;
}

I had border-box applied to all elements. Once I removed this, the percentage translate3d was applied correctly.

If CSS is not working (and should be working), you need an workaround. Please check my modified version:

http://jsfiddle.net/EG9v8/1/

function makeCss(index){
    // Calculate the offset, given the tabs size
    var size = -$('.tabs').width() * index;
    // Create style for the given offset.
    var css = '-webkit-transform: translate3d('+size+'px, 0, 0);';
    css += '-moz-transform: translate3d('+size+'px, 0, 0);';
    css += '-ms-transform: translate3d('+size+'px, 0, 0);';
    css += '-o-transform: translate3d('+size+'px, 0, 0);';
    css += 'transform: translate3d('+size+'px, 0, 0);';
    return css;
}

$('.tabLinks a').on('click', function(e){
    e.preventDefault();

    var index = $(this).index();

    // Set the current style
    $('.tabs').attr('style', makeCss(index));
});
发布评论

评论列表(0)

  1. 暂无评论