Question

I have the following markup:

<div class="cube trigger cuberotate">
    <div class="face init f z"></div>
    <div class="face init l y"></div>
    <div class="face init b z"></div>
    <div class="face init r y"></div>
    <div class="face init u x"></div>
    <div class="face init d x"></div>
</div>

Which resembles a 3d cube, every face is rotated and translated onto their proper position and I let the cube rotate using an animation on the faces' parent.

Here's the related css of it:

.cube {
  position: absolute;
  cursor: pointer;

  width: 120px;
  height: 120px;

  top: 0;
  left: 0;

  transform-origin: 50% 50%;
  transform-style: preserve-3d;
}

.face {
  position: absolute;

  width: 120px;
  height: 120px;

  border: 0px solid #fff;
  background: #c82222;

  transform-origin: 50% 50%;

  opacity: 1;
  padding: 0px;

  -webkit-touch-callout: none;
  user-select: none;

  transition: all 0.5s ease-out;    
}

I wanted to make the cube appear one face at time on document ready, so I just threw in some javascript, basically an interval every 500ms which simply removes the .init class which overrides the opacity: 1 value on the .face class.

(function($) {
  'use strict';

  // Some selectors and shit...
  var $face = $('.face').first(),
      speed = 500,
      timer = null;

  $(document).ready(function(){
    // Start showing faces
    timer = window.setInterval(function(){
      var $next = $face.next();

      $face.removeClass('init');

      if(!$next.hasClass('face')) {
        window.clearInterval(timer);
      }

      $face = $next;
    }, speed);

  });


})(jQuery);


// And the additional CSS below
.face.init {
  opacity: 0;
}

In an ideal world this code should work, however I am facing a problem on Google Chrome the opacity doesn't transition back to 1 after the class is removed keeping the cube completely invisible. If you right click and inspect it becomes visible again.

Curiously on Safari, which is also a webkit-based browser this does not happen at all and the faces show one at time as they are supposed to do.

I tried to use both .animate() from jquery and also tried the jquery plugin transit

  • Now, Safari and Chrome aren't supposed to behave in the same way, or are there major differences under the hood despite the rendering engine being the same?
  • Is it something I did wrong?
  • There's a workaround for this?

Here's my pen: http://codepen.io/luigimannoni/pen/FstKG/

Thanks

Update:

I have tried obviously on Chrome on my Mac and also on Windows 7 and they both behave the same way (different machines also)

Also tried Firefox which works seamlessly like Safari apart from the rotating animation which isn't happening (but I kept Firefox out of consideration as it's a different browser).

Additional update:

Chrome on mobile devices (both iOS and Android) are working and behaving like Safari on desktop.

Another Update:

After playing around around I have found probably it's a browser bug, Chrome Canary works fine as expected. I posted this on facebook where I've got a couple of good workarounds from a developer, which I found quite creative.

The first one involved in have a rgba() background-color and make the alpha change instead of having the transition on the opacity: http://codepen.io/anon/pen/IjsBL

The second one involved a bit of javascript coding, forcing the browser repaint the faces at each frame: http://codepen.io/anon/pen/Hofzb

I am starting a bounty to see what stackoverflow can do here!

Was it helpful?

Solution

You could try to assign 0.01 to opacity.

.face.init {
  opacity: 0.01;
}

OTHER TIPS

Looks like it is a documented regression bug

For the difference in Safari and Chrome, you should know that Chrome uses Blink(a webkit fork) as its rendering engine since version 28.

This problem was brought to my attention on Facebook. As requested, I'll post my initial thought process.

Initial Thought: Aggressive GPU-use / hardware-acceleration

Initially, I thought Chrome was seeing the 3D transforms in the keyframes-animation and hardware accelerating the animation -- which is what we expect -- but then when trying to interfere via JavaScript, was not interrupting the GPU execution.

Workaround 1:

Use a separate keyframes-animation first to animate opacity and rotation at the same time and then start your current animation, animating just the rotation to continue infinitely.

See this codepen.

Workaround 2:

I then immediately realised he wanted each face to fade in independently, in sequence. Knowing javascript wasn't interrupting the CSS animation, I tried animating the .faces using a keyframes-animation. Using an animation-delay to stagger each face.

See this codepen. But for some reason, it stops after the first face :(

Workaround 3:

At this point I was clutching at straws, and thought to toggle perspective: 500px to perspective: 501px, within a requestAnimationFrame callback, in hopes it would break hardware-acceleration, but no luck.

Workaround 3.1:

But having used a requestAnimationFrame, I decided I could just perform the first rotation and intended fade-in's using javascript and then trigger the CSS animation after.

See this codepen. This was the intended visual.

Workaround 4:

While anyone else would have been done and dusted, still using javascript bugged me to hell -- as much as I love JS, CSS is just smoother (right now).

Then it hit me! I could just animate background-color: rgba(...); rather than opacity: ...;.

And so finially, I had the intended animation using pure CSS.

See this codepen.

This was based on Workaround 2. I had to create 3 extra animations: one for each colour .face identified with classes .x, .y and .z.

I used SCSS to make it clear I was using the original colours (hence the rgba(#c82222,0);) and also to save myself the ball-ache of having to convert that to RGB values anyway.


Hope that helps anyone :)

Please try to initiate opacity from zero with bit more transition values.

CSS position relative fixes the problem:

.fullscreen {
  position: relative;

http://codepen.io/anon/pen/oekyt

It reminds me the of old IE bugs then you have to set

*zoom: 1;

for element. It made element "really rendered", not just "light rendered".

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top