Bug in 3D Transformation Identified in Safari 11.0.1

Currently, I am working on implementing a CSS3 3D transform for an opening cube. The functionality works seamlessly in Chrome, Firefox, and Opera browsers. However, there seems to be a strange issue in Safari where the cube is positioning itself oddly.

let isExpanded = false;
let cube = document.querySelector(".cube");

document.querySelector("#toggle").onclick = function(e) {
  e.preventDefault();
  document.querySelector("#toggle").disabled = true;
  toggle();
}

function toggle() {
  if (!isExpanded) {
    isExpanded = true;
    // Pause Rotation
    document.querySelector(".cube").classList.add("pause");
    // Open Cube
    open();
  } else {
    isExpanded = false;
    close();
  }
}

function close() {
  // Close the cube
  cube.classList.remove("open");
  cube.style.bottom = "0";
  // Rotate to the start position and restart animation
  setTimeout(function() {
    cube.classList.remove("origin-bottom");
  }, 1000); // >= side transform's transition time
  setTimeout(function() {
    cube.style.animation = "spin 15s infinite linear";
    cube.style.webkitAnimation = "spin 15s infinite linear";
    document.querySelector("#toggle").disabled = false;
  }, 1200);
}

function open() {
  // Set transform value to current position & Disable Animation
  prop = window.getComputedStyle(cube, null).getPropertyValue("transform");
  cube.style.transform = prop;
  cube.style.webkitTransform = prop;
  cube.style.MozTransform = prop;
  cube.style.msTransform = prop;
  cube.style.OTransform = prop;
  cube.style.animation = "none";
  cube.style.webkitAnimation = "none";
  // Rotate the cube to its initial position & Remove pause
  setTimeout(function() {
    cube.classList.remove("pause");
    cube.classList.add("origin-bottom");
    cube.style.transform = "rotateX(-20deg) rotateY(42deg)";
    cube.style.webkitTransform = "rotateX(-20deg) rotateY(42deg)";
    cube.style.MozTransform = "rotateX(-20deg) rotateY(42deg)";
    cube.style.msTransform = "rotateX(-20deg) rotateY(42deg)";
    cube.style.OTransform = "rotateX(-20deg) rotateY(42deg)";
  }, 50);
  // Open the cube
  setTimeout(function() {
    cube.classList.add("open");
    document.querySelector("#toggle").disabled = false;
  }, 1000); // >= side transform's transition time
}
body {background: #333;}
.cube-wrapper {
  margin: auto;
  width: 200px;
  height: 200px;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
  position: absolute;
  -webkit-perspective: 600px;
  perspective: 600px;
  perspective-origin: 50% 50%;
}
... [CSS code continued]
<button id="toggle">toggle</button>
<div class="preserve3d">
  ... [HTML structure continued]
</div>

Despite the unusual displacement in Safari, the inspector indicates that the cube is correctly positioned. When utilizing getComputedStyle, the correct values are retrieved, which makes debugging this issue quite perplexing.

After researching, it was discovered that Safari has some complications with rotateY. Attempting the suggested solution of assigning z-index values did not resolve the problem.

For your convenience, here is a concise version of the code: https://jsfiddle.net/7mxghcq9/

Thank you in advance :)

Answer №1

It's difficult to determine exactly what is happening or if Safari's behavior is incorrect...

However, a successful solution involves placing the cube.style.animation = 'none'; within your first timeout function, followed by triggering a reflow to remove the paused status.

function open() {
  // Set transform value to current position
  prop = window.getComputedStyle(cube, null).getPropertyValue("transform");

  cube.style.transform = prop;
  cube.style.webkitTransform = prop;
  cube.style.MozTransform = prop;
  cube.style.msTransform = prop;
  cube.style.OTransform = prop;
  
  // Disable Animation & Rotate the cube to its initial position & Remove pause
  setTimeout(function() {
    cube.style.animation = "none";
    cube.offsetWidth; // force a reflow
    cube.classList.remove("pause");
    cube.classList.add("origin-bottom");
    cube.style.transform = "rotateX(-20deg) rotateY(42deg)";
  }, 50);
  // Open the cube
  setTimeout(function() {
    cube.classList.add("open");
    document.querySelector("#toggle").disabled = false;
  }, 1000); // >= side transform's transition time
}

let isExpanded = false;
let cube = document.querySelector(".cube");

document.querySelector("#toggle").onclick = function(e) {
  e.preventDefault();
  document.querySelector("#toggle").disabled = true;
  toggle();
}

function toggle() {
  if (!isExpanded) {
    isExpanded = true;
    // Pause Rotation
    document.querySelector(".cube").classList.add("pause");
    // Open Cube
    open();
  } else {
    isExpanded = false;
    close();
  }
}

function close() {
  // Close the cube
  cube.classList.remove("open");
  cube.style.bottom = "0";
  // Rotate to the start position and restart animation
  setTimeout(function() {
    cube.classList.remove("origin-bottom");
  }, 1000); // >= side transform's transition time
  setTimeout(function() {
    cube.style.animation = "spin 15s infinite linear";
    cube.style.webkitAnimation = "spin 15s infinite linear";
    document.querySelector("#toggle").disabled = false;
  }, 1200);
}
body {background: #333;}
.cube-wrapper {
  margin: auto;
  width: 200px;
  height: 200px;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
  position: absolute;
  -webkit-perspective: 600px;
  perspective: 600px;
  perspective-origin: 50% 50%;
}
.cube-wrapper .cube {
  width: 200px;
  height: 200px;
  position: relative;
  bottom: 0;
  -webkit-transform-style: preserve-3d;
  transform-style: preserve-3d;
  backface-visibility: visible;
  transition: all 1s ease-in-out;
  animation: spin 15s infinite linear;
}
.cube-wrapper .cube .side {
  transition: transform 1s ease-in-out, background 0.5s ease-in-out, opacity 0.5s ease-in-out;
  outline: 1px solid rgba(250, 250, 250, 0.3);
  position: absolute;
  left: 0;
  top: 0;
  width: 200px;
  height: 200px;
  transform-origin: 50% 50%;
  -webkit-transform-origin: 50% 50%;
}
.cube-wrapper .cube .right {
  transform: rotateX(0deg) rotateY(90deg) rotateZ(0deg) translateZ(100px);
}
.cube-wrapper .cube .left {
  transform: rotateX(0deg) rotateY(270deg) rotateZ(0deg) translateZ(100px);
}
.cube-wrapper .cube .top {
  transform: rotateX(90deg) rotateY(0deg) rotateZ(0deg) translateZ(100px);
}
.cube-wrapper .cube .bottom {
  transform: rotateX(-90deg) rotateY(0deg) rotateZ(0deg) translateZ(100px);
}
.cube-wrapper .cube .front {
  transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg) translateZ(100px);
}
.cube-wrapper .cube .behind {
  transform: rotateX(0deg) rotateY(180deg) rotateZ(0deg) translateZ(100px);
}
/* pause */
.preserve3d .cube-wrapper .cube.pause, .preserve3d .cube-wrapper .cube:hover {
  -webkit-animation-play-state: paused !important;
  animation-play-state: paused !important;
}
/* open */
.preserve3d .cube-wrapper .cube.open .right {
  transform: rotateY(90deg) rotateX(-90deg) translateY(-100px) !important;
}
.preserve3d .cube-wrapper .cube.open .left {
  transform: rotateY(-90deg) rotateX(-90deg) translateY(-100px) !important;
}
.preserve3d .cube-wrapper .cube.open .front {
  transform: rotateX(-90deg) translateY(-100px) !important;
}
.preserve3d .cube-wrapper .cube.open .behind {
  transform: rotateY(180deg) rotateX(-90deg) translateY(-100px) !important;
}
.preserve3d .cube-wrapper .cube.origin-bottom .right {
  transform-origin: bottom !important;
}
.preserve3d .cube-wrapper .cube.origin-bottom .left {
  transform-origin: bottom !important;
}
.preserve3d .cube-wrapper .cube.origin-bottom .top {
  opacity: 0;
  pointer-events: none;
}
.preserve3d .cube-wrapper .cube.origin-bottom .front {
  transform-origin: bottom !important;
}
.preserve3d .cube-wrapper .cube.origin-bottom .behind {
  transform-origin: bottom !important;
}
.cube.origin-bottom{
  animation-fill-mode: forwards;
}
/* animation */
@keyframes spin {
  from {
    transform: rotateX(-20deg) rotateY(42deg);
  }
  to {
    transform: rotateX(340deg) rotateY(382deg);
  }
}
<button id="toggle">toggle</button>
<div class="preserve3d">
  <div class="cube-wrapper">
    <div class="cube">
      <div class="side right">Right
      </div>
      <div class="side left">Left
      </div>
      <div class="side top">Top
      </div>
      <div class="side bottom">Bottom
      </div>
      <div class="side front">Front
      </div>
      <div class="side behind">Behind
      </div>
    </div>
  </div>
</div>

Answer №2

One way to resolve the issue is by clearing your browsing history and refreshing the page. I had a similar experience, and clearing the cache was effective in fixing it. As long as you are using Safari 9 or higher, you should be able to rotate fully without any problems. Since you are utilizing Webkit, the functionality should work smoothly. I have personally tested this solution and can confirm that it works well for me.

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

The CSS transition will only be triggered when the class is turned on, not when it is turned off

Having some trouble with a transition effect involving multiple elements on a page. Specifically, I am working on a sliding side menu for the responsive top navigation menu. The goal is to have the relative body and a fixed header bar move to the right whe ...

Utilizing ScrollView Component in React Native

I have developed a simple app that displays a collection of images with titles, resembling a film gallery where all available films can be viewed. However, I encountered an issue when trying to implement the ScrollView element as it does not allow for scro ...

What is the best way to input sorted data from a variable into a textarea field?

HTML: <button id="myButton">Click Here</button> <br /> <textarea id="imgLinks" rows="10" cols="30"></textarea> JS: $(document).ready(function() { $("#myButton").click(function() { var finalLink = '["img/1.j ...

Picture is not showing up on my MVC software

Within a table containing multiple other tds, I am having an image tag. <table class="MyClass"> <tr> <td> @Html.LabelFor(m => m.ShopName) </td> <td> @Html.TextBoxFor(mode ...

Dealing with API Troubles: Resolving 'AxiosError: Request failed with status code 401' Issue in ReactJS

I encountered the following error: AxiosError: Request failed with status code 401 While attempting to log in to my react app, I researched similar solutions and discovered that some answers suggested the issue could be related to missing headers in Axi ...

Error encountered when attempting to resolve Angular UI Router provider

Having difficulty with integrating a resolve into a state using Angular UI router. Strangely, it works perfectly fine in another section of my code. I've organized my code into different component areas structured like this: /app /component-dashbo ...

Creating an editable list item with jQuery: A step-by-step guide

I am trying to create a user-friendly interface where users can view and edit their information in a listview. The data will be retrieved from a database and displayed in the listview. My goal is to make the listview editable, so when a user clicks on it ...

"Integrating Associated Models with Sequelize: A Step-by-Step Guide

I am attempting to retrieve all transactions along with their associated stripePayments, but I keep encountering the error include.model.getTableName is not a function. In my database schema, there is a table for transactions that has a one-to-many relati ...

Error: The 'address' property cannot be found in the specified 'string' type

Hey there! I'm currently facing an issue while trying to pass an object from one page to another and store it in the object on the second page. I'm encountering an error with EsLint. let accountDetails:any={ name:this.userAccount.name, p ...

Encountering an error while trying to install an npm package

$ npm install sillyname npm WARN tar TAR_ENTRY_ERROR UNKNOWN: unknown error, write npm WARN tar TAR_ENTRY_ERROR UNKNOWN: unknown error, write npm WARN tar TAR_ENTRY_ERROR UNKNOWN: unknown error, write npm WARN tar TAR_ENTRY_ERROR UNKNOWN: unknown error, wr ...

Share an image using a subdomain in Express.js

Suppose I have the following code for testing on a local environment. sendImage: async function(req, res) { console.log(req.hostname); var filepath = path.join(__dirname, '../../img/uploads/' + req.params.year + '/' + req.para ...

Understanding the readability of JavaScript arrays.ORDeciphering

Recently, I've been working with a JSON OBJECT that looks something like this { "kay1": "value1", "key2": "value2", "key3":{ "key31": "value31", "key32": "value32", "key33": "value33" } } However, I am interested in converting it ...

What could be causing the malfunction of material-UI Tabs?

My Material UI Tabs seem to have stopped working after a recent update. I suspect it may be related to the react-tap-event-plugin update I installed. Initially, I thought it was due to tab indexing but even using values like value='a', value=&apo ...

Modify the content and display of a stationary div based on vertical positions

I am interested in creating a vertical website that features multiple divs appearing at specific y-points on the page. These divs will contain text and will always be positioned fixed at 20% from the left and about 250px from the top. My goal is to have t ...

Choosing Select2: Customizing the context of formatSelection

I've created a simple custom wrapper for Select2, which has been very helpful. However, I am facing an issue with the formatSelection field. When initializing Select2 through my wrapper, it looks like this: this.elem.select2({ allowClear : option ...

Changing the display property causes ordered lists to shift towards the left

// Ensure the list is displayed when checked and hidden when unchecked. function toggleList(event) { let ol = document.getElementById('list'); if (event.currentTarget.checked) { ol.style.display = 'initial'; } else { ol.s ...

Toggle jQuery to hide a div and update its CSS styling

There is an anchor with the class of "hide-btn1" that I want to trigger a series of actions when clicked: The rcol-content should hide and the text should change from Hide to Show The #container width needs to increase to 2038px The table.status-table wi ...

Distribute divs of equal width evenly within a grid

I'm facing a unique challenge at the moment. I'm attempting to create a grid system where all elements have a fixed width of 200px each. What I envision is a clever grid setup using only CSS, where each "row" will strive to accommodate as many el ...

Alter the value of a parameter within a script tag with JavaScript/jQuery

Looking to dynamically change a parameter value using JavaScript or jQuery. Here is an example URL: www.exampleurl.com/somepage?foo=test I have a function that can extract the value after the 'foo' parameter: function getParameterByName(name, ...

Exploring the functionality of the onblur HTML attribute in conjunction with JavaScript's ability to trigger a

How do the HTML attribute onblur and jQuery event .trigger("blur") interact? Will both events be executed, with JavaScript this.trigger("blur") triggering first before the HTML attribute onblur, or will only one event fire? I am using ...