Firefox CSS rotation is different than Chrome rotation

I want to create a 3D rectangle (box) that users can move with arrows. It works fine in Chrome, but in Firefox, some transitions (actually very different) are different from Chrome. Take a look at this script (this is all my code) and compare it in both browsers for a better understanding.

Since the first violin contains a lot of code, I will simplify it and choose one random weird transition. Look at this script and click the "Left" button or the left arrow once. It works great, but when you click it again, the rectangle rotates 3 times instead of 1 time.

Is this a Firefox bug or what am I doing wrong?

Below is the code you'll find in a simplified fiddle.

var position = 'show-front'; $('#left').bind('click', function() { if (position == 'show-front') { $('#box').removeClass().addClass('show-right'); position = 'show-right'; } else if (position == 'show-right') { $('#box').removeClass().addClass('show-back-3'); position = 'show-back-3'; } else if (position == 'show-back-3') { $('#box').removeClass().addClass('show-left'); position = 'show-left'; } else if (position == 'show-left') { $('#box').removeClass().addClass('show-front'); position = 'show-front'; } }); $(window).bind('keyup', function(event) { switch (event.keyCode) { case 37: // left $('#left').click(); break; } }); 
 .container { width: 150px; height: 100px; position: relative; margin: 25px auto 25px auto; perspective: 600px; } #box { width: 100%; height: 100%; position: absolute; transform-style: preserve-3d; transition: transform 1s; } #box figure { display: block; position: absolute; border: 1px solid black; line-height: 98px; font-size: 45px; text-align: center; font-weight: bold; color: white; } figure { margin: 0; } #box .front, #box .back { width: 148px; height: 98px; } #box .right, #box .left { width: 48px; height: 98px; left: 50px; } #box .top, #box .bottom { width: 148px; height: 48px; top: 25px; line-height: 48px; } #box .front { background: hsla(000, 100%, 50%, 0.7); } #box .back { background: hsla(160, 100%, 50%, 0.7); } #box .right { background: hsla(120, 100%, 50%, 0.7); } #box .left { background: hsla(180, 100%, 50%, 0.7); } #box .top { background: hsla(240, 100%, 50%, 0.7); } #box .bottom { background: hsla(300, 100%, 50%, 0.7); } #box .front { transform: translateZ(25px); } #box .back { transform: rotateX(180deg) translateZ(25px); } #box .right { transform: rotateY(90deg) translateZ(75px); } #box .left { transform: rotateY(-90deg) translateZ(75px); } #box .top { transform: rotateX(90deg) translateZ(50px); } #box .bottom { transform: rotateX(-90deg) translateZ(50px); } #box.show-front { transform: translateZ(-50px); } #box.show-right { transform: translateZ(-150px) rotateY(-90deg); } #box.show-back-3 { transform: translateZ(-50px) rotateX(180deg) rotateZ(-180deg); } #box.show-left { transform: translateZ(-150px) rotateY(90deg); } 
 <section class="container"> <div id="box" class="show-front"> <figure class="front">1</figure> <figure class="back">2</figure> <figure class="right">3</figure> <figure class="left">4</figure> <figure class="top">5</figure> <figure class="bottom">6</figure> </div> </section> 
+6
source share
3 answers

Based on the assumption that Firefox is simply distorted in this regard (see analysis below), here is a workaround that works in Firefox. It wraps the #box element in another div and only translates the wrapper. And the wrapper only rotates 90 degrees from the start point in one direction at a time, so Firefox can't mess it up.

As soon as the transition ends, the reset rotation returns to its original position, and at the same time the inner box rotates to a new position, both without transition, so the change is not visible.

The second important change is the currently computed #box transform and adding rotation to it, so we don’t need to track the turns along the way.

Note that the rotation order matters. In order to achieve what you are trying to do (rotation in "world space" rather than "object space"), you need to apply the rotation in the reverse order. For instance. to rotate to the right, use .css("transform", "rotateY(90deg) " + currentComputedTransform) . This will solve the problem you mentioned in the comments where it rotates around the wrong axis. See below for more details.

Please note that I do not allow the rotation to start if it is already running, because this will not work. You can queue up keystrokes in the array if you want this to be possible, but you can also reduce the length of the transition in proportion to the length of the queue in this case, so it won’t take forever.

Updated script: https://jsfiddle.net/955k5fhh/7/

Relevant javascript:

 $("#box").wrap("<div id='outer'></div>"); var pending=null; function rotate(axis,angle,dir) { if (pending) return; $("#outer").removeClass().addClass(dir); var current=$("#box").css("transform"); if (current=='none') current=''; pending="rotate"+axis+"("+angle+"deg) " + current; } $("#outer").bind('transitionend', function() { $(this).removeClass(); $("#box").css('transform',pending); pending=null; }); $('#up').bind('click', function() { rotate('X',90,"up"); }); $('#down').bind('click', function() { rotate('X',-90,"down"); }); $('#right').bind('click', function() { rotate('Y',90,"right"); }); $('#left').bind('click', function() { rotate('Y',-90,"left"); }); 

Previous analysis

I played with JS-solutions, and I came across this useful post https://gamedev.stackexchange.com/a/67317 - it indicates that to rotate objects in the "world space" instead of the "object space" you just need to change the rotation order .

Based on this, I simplified your script as follows:

 var rot = ""; var tr = "translateZ(-50px)"; $('#up').bind('click', function() { rot=" rotateX(90deg)"+rot; $("#box").css("transform",tr+rot); }); $('#down').bind('click', function() { rot=" rotateX(-90deg)"+rot; $("#box").css("transform",tr+rot); }); $('#right').bind('click', function() { rot=" rotateY(90deg)"+rot; $("#box").css("transform",tr+rot); }); $('#left').bind('click', function() { rot=" rotateY(-90deg)"+rot; $("#box").css("transform",tr+rot); }); 

https://jsfiddle.net/955k5fhh/ (note that this is not a complete solution, because ultimately the rot string will be too long)

And in Chrome, it behaves as expected. And again, Firefox is mistaken, even if you just chain, for example. rotateX(90deg) transform rotateX(90deg) .

So, I went one step further and turned neighboring rotations on the same axis ...

 var rots = []; var tr = "translateZ(-50px)"; function transform() { var tf = "translateZ(-50px)"; rots.forEach(function(rot) { tf += " rotate" + rot[0] + "(" + rot[1] + "deg)"; }); console.log(tf); $("#box").css("transform", tf); } function addRot(axis,angle) { if (rots.length==0 || rots[0][0]!=axis) { rots.unshift([axis,angle]); } else { rots[0][1]+=angle; } transform(); } $('#up').bind('click', function() { addRot('X',90); }); $('#down').bind('click', function() { addRot('X',-90); }); $('#right').bind('click', function() { addRot('Y',90); }); $('#left').bind('click', function() { addRot('Y',-90); }); 

https://jsfiddle.net/955k5fhh/2/

Which, again, works well in Chrome, and works a little better in Firefox, but still, as soon as you switch the axes, you can end the rotation incorrectly. Similarly, if you press the button before the transition is complete, it may not rotate correctly.

So, I would conclude that, unfortunately, yes, Firefox is just buggy, but at least there are workarounds.

+3
source

It looks like you're doing everything right, and the rotation differences in Chrome vs. Firefox are caused by the way the two browsers handle CSS3. When you look at the rotation from show-back-4 to show-top-4 , your CSS file indicates a rotation of 270deg . In Firefox, this is done in this way. In Chrome, it looks like it is optimizing and not making a complete turn, saving on processing power or something like that. So yes, I think it’s just the difference in browsers, not an error in any of them.

0
source

You can try using keyframes to get more control over the animation, something like this:

https://jsfiddle.net/L36v50kh/2/

I define both the start and end points for all transitions in the violin as follows:

 @keyframes front-to-right { from {transform: translateZ(-50px) rotateY(0deg); } to {transform: translateZ(-150px) rotateY(-90deg);} } 

It looks the same in both browsers, but it trembles when you press the button until the animation ends.

You can also think of animations using JavaScript to get precise control and not define every transition, something like this:

 var applyRotation = function() { $('#box').css('transform', 'rotateY(' + rotateY + 'deg)'); handleMultipleRotations(); }; var unwindTimeout; var rotateY = 0; var handleMultipleRotations = function() { $('#box').css('transition-duration', ''); if (typeof unwindTimeout === 'number') { clearTimeout(unwindTimeout); unwindTimeout = undefined; } if (Math.abs(rotateY) >= 360) { unwindTimeout = setTimeout(function() { rotateY -= Math.floor(rotateY / 360) * 360; $('#box').css({ 'transition-duration': '0s', 'transform': 'rotateY(' + rotateY + 'deg)' }); }, 1000); } }; $('document').ready(function() { $('#left').on('click', function() { rotateY -= 90; applyRotation(); }); $('#right').on('click', function() { rotateY += 90; applyRotation(); }); }); 
 /* minified to draw focus to js */ .container{width:150px;height:100px;position:relative;margin:25px auto;perspective:600px}#box{width:100%;height:100%;position:absolute;transform-style:preserve-3d;transition:transform 1s}#box figure{display:block;position:absolute;border:1px solid #000;line-height:98px;font-size:45px;text-align:center;font-weight:700;color:#fff}figure{margin:0}#box .back,#box .front{width:148px;height:98px}#box .left,#box .right{width:48px;height:98px;left:50px}#box .bottom,#box .top{width:148px;height:48px;top:25px;line-height:48px}#box .front{background:hsla(000,100%,50%,.7)}#box .back{background:hsla(160,100%,50%,.7)}#box .right{background:hsla(120,100%,50%,.7)}#box .left{background:hsla(180,100%,50%,.7)}#box .top{background:hsla(240,100%,50%,.7)}#box .bottom{background:hsla(300,100%,50%,.7)}#box .front{transform:translateZ(25px)}#box .back{transform:rotateX(180deg) translateZ(25px)}#box .right{transform:rotateY(90deg) translateZ(75px)}#box .left{transform:rotateY(-90deg) translateZ(75px)}#box .top{transform:rotateX(90deg) translateZ(50px)}#box .bottom{transform:rotateX(-90deg) translateZ(50px)} 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <section class="container"> <div id="box" class="show-front"><figure class="front">1</figure><figure class="back">2</figure><figure class="right">3</figure><figure class="left">4</figure><figure class="top">5</figure><figure class="bottom">6</figure></div> </section> <section id="options"><p><button id="left">Left</button><button id="right">Right</button></p></section> 
0
source

Source: https://habr.com/ru/post/987347/


All Articles