Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Complex Webkit Bug? Relative Position + Z-Index + Overflow Hidden + CSS3 Transform

The code (JSFiddle Preview) below produces unexpected results in Webkit in comparison to other modern browsers:

<script type="text/javascript">
jQuery(document).ready(function($) {
    RunFunction();

    $('.ColorSquare').click(function() {
        $('#Lightbox').css('display','block');
        $('#ShowColorSquare').css('display','block');
        $('#ShowColorSquare').css('z-index','10');
        $('#ShowColorSquare').css('left',$('#ShowColorSquare').parent().width() / 2 - 50);
        $('#ShowColorSquare').css('top',$('#ShowColorSquare').parent().height() / 2 - 50);
        $('#ShowColorSquare').html('The color is: ' + $(this).css('background-color'));
    });
    $('#ShowColorSquare').click(function() {
        $('#Lightbox').css('display','none');
        $('#ShowColorSquare').css('display','none');
        $('#ShowColorSquare').html('');
    });
    $('#Lightbox').click(function() {
        $('#Lightbox').css('display','none');
        $('#ShowColorSquare').css('display','none');
        $('#ShowColorSquare').html('');
    });
});
function RunFunction() {
    $('#slide1').animate({
        left: '-=310'
    }, 3000);
    $('#slide2').animate({
        left: '-=310'
    }, 3000);
    $('#slide3').animate({
        left: '-=310'
    }, 3000, function() {
        if($('#slide1').css("left") == '-310px') {
            $('#slide1').css("left",620);
        }
        if($('#slide2').css("left") == '-310px') {
            $('#slide2').css("left",620);
        }
        if($('#slide3').css("left") == '-310px') {
            $('#slide3').css("left",620);
        }   
        RunFunction(); 
    });
}
</script>
<style>
#Spin {
    width:50px;
    height:50px;
    margin: 15px 0px 15px 15px;
    background-color:#960;
    animation-name:Spin;
    animation-duration:5s;
    transform-origin:50% 50%;
    animation-iteration-count:infinite;

    -webkit-animation-name:Spin;
    -webkit-animation-duration:5s;
    -webkit-transform-origin:50% 50%;
    -webkit-animation-iteration-count:infinite;
}
@keyframes Spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}
@-webkit-keyframes Spin {
    0% { -webkit-transform: rotate(0deg); }
    100% { -webkit-transform: rotate(360deg); }
}
.ColorSquare {
    height:100px;
    width:100px;
    position:absolute;
}
#ShowColorSquare {
    height:100px;
    width:100px;
    position:absolute;
    background-color:white;
    display:none;
}
#Lightbox {
    background-color:#000;
    width:100%;
    height:100%;
    position:fixed;
    top:0px;
    left:0px;
    opacity:.8;
    display:none;
    z-index:5;
}
.Panel {
    width:225px;
    height:250px;
    position:absolute;
    background-color:#6C6C6C;
}
</style>

<div id="Spin"></div>

<div style="height:260px;width:500px;overflow-x:hidden;background:#CCC;">
    <div style="height:250px;width:500px;position:relative;">
        <div id="slide1" class="Panel" style="top:0px;left:0px;">
            <div>Slide 1</div>
            <div style="position:relative;margin-top:10px;width:225px;height:200px;">
                <div class="ColorSquare" style="background-color:#093;left:0px;top:0px;"></div>
                <div class="ColorSquare" style="background-color:#C9F;left:100px;top:100px;"></div>
            </div>
        </div>
        <div id="slide2" class="Panel" style="top:0px;left:310px;">
            <div>Slide 2</div>
            <div style="position:relative;margin-top:10px;width:225px;height:200px;">
                <div class="ColorSquare" style="background-color:#CF9;left:0px;top:0px;"></div>
                <div class="ColorSquare" style="background-color:#C63;left:100px;top:100px;"></div>
            </div>
        </div>
        <div id="slide3" class="Panel" style="top:0px;left:620px;">
            <div>Slide 3</div>
            <div style="position:relative;margin-top:10px;width:225px;height:200px;">
                <div class="ColorSquare" style="background-color:#696;left:0px;top:0px;"></div>
                <div class="ColorSquare" style="background-color:#F96;left:100px;top:100px;"></div>
            </div>
        </div>
        <div id="ShowColorSquare"></div>
    </div>
</div>
<div id="Lightbox"></div>

Expected Results: It's supposed to have 3 DIVs (slides) continuously animate to the left in a loop, including the respected colored boxes within the slides. If you click on a colored box, a lightbox shows with the RBG color of the colored box clicked within the respected slide. Click again to close the lightbox. All while a 3D transform is applied before the light gray parent DIV, with position relative with overflow hidden, with Jquery animate on absolute positioning slide DIVs.

Results in Webkit: The colored boxes within the slides don't appear to move/render at all until you resize the browser window on a desktop, or click the JSFiddle panel resize handle (or pinch/zoon) on a tablet. On a different debug note, if the 3D transform animation is not looping, when the animation stops, DIVs render as expected.

Test results showing Webkit bug:

  • Win7 IE10: Pass
  • Win7 Chrome: Pass
  • Win7 FF: Pass
  • Win7 Safari: Fail
  • Win8 IE11: Pass
  • Android Chrome: Fail
  • iOS Safari: Fail
  • iOS Chrome: Fail
  • MacOS Safari: Fail
  • MacOS Chrome: Fail

Notice (JSFiddle Preview) without the 3D transform, the code works, although the animation is not smooth on desktop. The lightbox works fine.

Notice (JSFiddle Preview) with the 3D transform after the parent DIV, the animation is smooth and the lightbox works fine.

Notice (JSFiddle Preview) with -webkit-transform: rotate(0deg) applied to the parent DIV with the overflow, the animation is choppy on tablets, but the 3D transform can exist before the parent DIV or within the slide DIVs. Another problem is created though. The parent overflow DIV has a lower z-index than the lightbox, making the dark lightbox DIV appear above the white dialog DIV that's within the parent DIV.

I know this is a very weird example, but it's a watered down example of more private complex code. The 3D transform must be before the parent DIV, or within a slide. The lightbox dialog must be within the parent DIV or slide, but appear above the dark lightbox DIV, which can't be within the parent DIV because the overflow hidden will not make the dark lightbox DIV appear full browser screen.

Any help is appreciated.

like image 385
detailCode Avatar asked Jul 30 '13 14:07

detailCode


1 Answers

If you read up on GPU Accelerated Compositing in Chrome http://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome you'll find that a renderLayer will get it's own compositing layer when:

  1. Layer has 3D or perspective transform CSS properties
  2. Layer is used by video element using accelerated video decoding
  3. Layer is used by a canvas element with a 3D or 2D context
  4. Layer uses a CSS animation for its opacity or uses an animated webkit transform
  5. Layer has a descendant that has a compositing layer
  6. Layer has a sibling with a lower z-index which has a compositing layer (in other words the layer is rendered on top of a composited layer)

You're getting different results when you add or remove the 3D transform because the code either goes through the GPU accelerated path, or the software rendering path. The GPU accelerated path is very sensitive to what GPU/drivers you have and if you see any sort of strange graphical glitches, one of the first things you should check is if they still exist when you turn off hardware acceleration off in chrome://flags/

All three examples work for me on Chrome OSX, so this is likely a browser bug with a specific graphics card. If you can find a base case I would suggest reporting this bug to the chromium project with your GPU info.

like image 72
Kerry Liu Avatar answered Dec 06 '22 02:12

Kerry Liu