The way Firefox handles CSS rotation is distinct from the rotation method used in

I am attempting to create a 3D rectangle (parallelepiped) that users can manipulate using arrow keys. The functionality works smoothly in Chrome, but I have noticed discrepancies in the transitions when testing in Firefox. Please take a look at this fiddle (contains my entire code) and compare the behavior in both browsers for better understanding.

Due to the complexity of the initial fiddle, I have simplified it and isolated one peculiar transition. Click on this fiddle and press the "Left" button or left arrow key once. Everything appears normal, but upon pressing it again, the rectangle rotates three times instead of just once.

I am curious if this issue is specific to Firefox or if there is an error in my coding logic?

Below, you will find the excerpt from the 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>

Answer №1

It seems that Firefox has some bugs when it comes to handling transitions, but fear not! There is a workaround available that specifically targets Firefox. By wrapping the #box element in another div and only transitioning the wrapper, you can avoid any mishaps caused by Firefox.

The key lies in resetting the rotation back to its starting position once the transition is complete, while simultaneously rotating the inner box to the new position without any visible changes. Additionally, by utilizing the current computed transformation of #box and adding the rotation to that, you can streamline the process without tracking each rotation individually.

Remember, the order of rotations matters. To ensure smooth rotations in "world space" rather than "object space," apply the rotations in reverse order. For example, to rotate right, use

.css("transform", "rotateY(90deg) " + currentComputedTransform)
.

Furthermore, to prevent issues with simultaneous rotations, I have implemented a restriction that prevents a new rotation from starting if one is already in progress.

If you want to try out the updated fiddle with these improvements, you can access it here: https://jsfiddle.net/955k5fhh/7/

For more detailed insights into the JavaScript behind this solution, refer to the code snippet below:

$("#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");
});

// Add event listeners for other directional rotations...

Enhanced Strategies

Delving deeper into refining the solution, I drew inspiration from a valuable post discussing world-space rotations versus object-space rotations. This inspired me to further optimize the implementation and simplify the existing fiddle as seen below:

// Simplified transformation using sequential rotations
var rot = "";
var tr = "translateZ(-50px)";

$('#up').bind('click', function() {
    rot=" rotateX(90deg)"+rot;
    $("#box").css("transform",tr+rot);
});

// Implement similar rotation functions for other directions...

For an improved approach that streamlines adjacent rotations and mitigates potential spinning errors, I refined the script even further:

// Improved transformation with streamlined rotations
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();
}

// Bind event handlers for directional rotations...

Feel free to explore the enhanced versions of the fiddle provided above, which offer smoother performance on Chrome and improved functionality on Firefox. While Firefox may still present challenges, these workarounds demonstrate effective solutions to mitigate potential issues.

Answer №2

From what I can see, you are on the right track with your code. The discrepancy in rotation between Chrome and Firefox is likely due to how each browser interprets CSS3. In the transition from show-back-4 to show-top-4, your CSS specifies a rotation of 270deg. Firefox executes this as instructed, while Chrome seems to optimize by not completing the full rotation, possibly for better performance. It appears to be a browser difference rather than a bug in either one.

Answer №3

To enhance the animation, one method to try is utilizing keyframes for more precise control. Here is an example:

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

In the provided fiddle, I specify both the initial and final positions for all transitions in this manner:

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

Although this appears consistent across browsers, it may appear jittery if the button is clicked before the animation completes.

Alternatively, consider using JavaScript for animations to have precise control without defining each transition separately. Here is a sample approach:

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) translate…
<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>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it feasible to save BoxGeometries or MeshLambertMaterial in an array using Three.js?

var taCubeGeometryArray = new Array(); var taCubeMaterialArray = new Array(); taCubeGeometryArray.push(new THREE.BoxGeometry( .5, .5, .5)); taCubeMaterialArray.push(new THREE.MeshLambertMaterial({map: THREE.ImageUtils.loadTexture( "image.png" )})); Could ...

I am having trouble understanding why my JavaScript code is bypassing the if statements

let emptyErr = [] if (!(req.body.title)) { emptyErr[0] = ('A title must be provided for a post!') } else if (!req.body.category) { emptyErr[1] = ('Please select a category for your post.') } else if (!req.body.content) { ...

Steps to display selected value on another page

Presented below is the code snippet: <html> <head> <meta charset="UTF-8"/> </head> <body> <!--Text field area starts here--> <H3>My Surveys </H3> <hr> <br> <br> <br> <ol> < ...

Create JSX code for rendering components outside of the main component

As someone who is diving into the world of React, I am currently on lecture 23 out of 247 on Udemy where I am learning about states and events. However, one particular question has been lingering in my mind without a definitive answer. Our company has mad ...

Eliminate the CSS triggered by a mouse click

Having some difficulty toggling the switch to change the background color. Struggling with removing the applied CSS and getting it to toggle between two different colored backgrounds. Code Sample: <!Doctype html> <html lang="en"> <head> ...

Utilize jQuery to track and record AdWords conversions

I have noticed a number of inquiries regarding tracking Adwords conversions with jQuery on this platform. However, it appears that there is no definitive solution yet. My attempt at recording a conversion involves the code below following the submission o ...

One-of-a-kind browser identification for users, no login required

I have an online boutique and I want to enable customers to make purchases without creating an account. To achieve this, I need a way to associate a selected list of items with a user. Initially, I considered using the IP address for this purpose but fou ...

The Vuetify data-table header array is having trouble accepting empty child associations

My Vuetify data-table relies on a localAuthority prop from a rails backend. Everything runs smoothly until an empty child association (nested attribute) is passed, specifically 'county': <script> import axios from "axios"; exp ...

Using TypeScript to define values with the placeholder "%s" while inputting an object as a parameter

One common way to decorate strings is by using placeholders: let name = "Bob"; console.log("Hello, %s.", name) // => Outputs: "Hello, Bob." I'm curious if there's a way to access specific values within an object being passed in without specif ...

Include the array in the 'content' property of the CSS using Jquery

I need help formatting data in CSS. The code I have is as follows: if (ext){ switch (ext.toLowerCase()) { case 'doc': pos = 'doc'; break; case 'bmp': pos = 'bmp'; break; ...

Typescript conversion: Changing a relational array into a tree object

My database is structured as an array of objects with the following format; array = [ {"name": "a", "id": "1", "parentId": NULL}, {"name": "b", "id": "2", "parentId": "1"}, {"name": "c", "id": "3", "parentId": "1"}, {"name": "d", "id": "4", "parentId" ...

What could be causing my node-statsd client script to not terminate?

When attempting to log a metric to a StatsD server using the node-statsd library, I encountered an issue where the script did not exit automatically. The code snippet in question is as follows: var StatsD = require('node-statsd').StatsD; var cli ...

Presented is the Shield UI JavaScript sparklines chart

I'm experimenting with the new lightweight ShieldUI Sparklines chart. I've copied the code directly from this page: Now, when I try to run it on my computer, I'm encountering an unexpected issue. I can see text, but the chart itself is not ...

The markers from KML exported from My Maps are not showing up on the Google Maps JavaScript API

I have a map on Google My Maps that I want to showcase through the Google Maps JavaScript API. This way, I can easily merge multiple maps into one and add paths/markers without needing to code it all manually. Test out the map I'm using by clicking t ...

retrieve the value obtained from a promise in an outer scope

I need a simple function that returns the name. Here's my existing code snippet: getName(entity, id) { const promise = userServices.getName(entity, id).then((data) => { return data; }); / ...

An issue has occurred while trying to execute the npm start command within PhpStorm

When I try to run npm start on Windows' command prompt, it works fine. However, I encounter errors when running it in the PhpStorm Terminal. You can see the errors here. Is this a result of them being different platforms or could it be related to som ...

Is there a way to detect when the mobile keyboard is open in a React application?

I am currently working with a Textfield that includes the Autofocus attribute. I am wondering if there is a method to detect when the keyboard opens in mobile view and then store this information in a boolean variable. https://i.stack.imgur.com/z0EtB.png ...

Issue with triggering onchange action for Multiple File Upload functionality

I'm currently in the process of developing a Website that requires a feature allowing users to upload multiple files. Here is the input-field I am working with: <div class="carousel-item carousel-custom-item active input-wrapper" > <inpu ...

Series of responses cascading from one another

Users are experiencing an issue where the need for adjusting margins based on the depth of responses in posts. Currently, this involves setting a margin-left value of 40px for one level deep reactions and 80px for two levels deep, and so on. To address th ...

Why is it that my for loop produces the accurate result, yet my forEach function fails to do so?

Hey there, I'm new to SO and seeking some guidance. I have a task where I need to create a function with two parameters - an array of strings and a string to potentially match within the array. I've developed two versions of the function: one us ...