stanley
stanley

Reputation: 585

Perspective property in Firefox causes transform to behave different than chrome and not as desired

There is a very similar question here, but it is quite old and the solutions do not work for me.

I have a turning page effect which uses the transform-style:preserve-3d and perspective, to create a 3D page then the turning page effect is animated with keyframes, using buttons to apply and remove a style with each keyframe.

The problem is with the perspective property, if this is removed, I lose the 3D effect but it works ok in both firefox and chrome. With the perspective set, the page turning effect appears incorrect in firefox only, it appears as though the page is pulled downward as it turns. In chrome, it works fine and the page turning effect appears as you would expect a page in a book to look like as it turns (as though the page goes up as it turns). The front of the page is red and the back is green.

I would like to retain the 3d effect with perspective set to 1000px and for the page to turn in firefox like it does in chrome.

I would appreciate if anyone has a a solution or workaround.

function turnLeft() {
  page = document.getElementById('page');
  page.classList.remove('turnRight')
  page.classList.add('turnLeft')
}

function turnRight() {
  page = document.getElementById('page');
  page.classList.remove('turnLeft')
  page.classList.add('turnRight')
}
body {
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  height: 100vh;
  width: 100vw;
  background-color: aliceblue;
  perspective: 1000px;
}

.page {
  position: absolute;
  left: 50%;
  top: 50%;
  height: 50%;
  width: 25%;
  transform: translateY(-50%) rotateX(25deg);
  -moz-transform: translateY(-50%) rotateX(25deg);
  border: solid 1px black;
  transform-origin: 0% 45%;
  transform-style: preserve-3d;
}

.front {
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  background-color: red;
  transform-style: preserve-3d;
}

.back {
  position: absolute;
  box-sizing: content-box;
  border: solid 2px #000;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  transform: rotate3d(0, 1, 0, 180deg);
  -moz-transform: rotate3d(0, 1, 0, 180deg);
  background-color: green;
  transform-style: preserve-3d;
}

.turnLeft {
  -moz-animation: turnPageToLeft 0.4s ease-in 0s 1 normal forwards;
  animation: turnPageToLeft 0.4s ease-in 0s 1 normal forwards;
}

.turnRight {
  -moz-animation: turnPageToRight 0.4s ease-in 0s 1 normal forwards;
  animation: turnPageToRight 0.4s ease-in 0s 1 normal forwards;
}

@keyframes turnPageToLeft {
  0% {
    transform: translateY(-50%) rotateX(25deg);
  }
  100% {
    transform: rotate3d(0, 1, 0, -180deg) translateY(-51%) rotateX(-25deg) scale(1, 0.99);
  }
}

@-moz-keyframes turnPageToLeft {
  0% {
    transform: translateY(-50%) rotateX(25deg);
  }
  100% {
    transform: rotate3d(0, 1, 0, -180deg) translateY(-51%) rotateX(-25deg) scale(1, 0.99);
  }
}

@keyframes turnPageToRight {
  0% {
    transform: rotate3d(0, 1, 0, -180deg) translateY(-51%) rotateX(-25deg)scale(1, 0.99);
  }
  100% {
    transform: translateY(-50%) rotateX(25deg);
  }
}

@-moz-keyframes turnPageToRight {
  0% {
    transform: rotate3d(0, 1, 0, -180deg) translateY(-51%) rotateX(-25deg)scale(1, 0.99);
  }
  100% {
    transform: translateY(-50%) rotateX(25deg);
  }
}
<body>
  <div id="page" class="page">
    <div class="front"></div>
    <div class="back"></div>
  </div>
  <button onclick="turnLeft()">Turn Left</button>
  <button onclick="turnRight()">Turn Right</button>
</body>

Upvotes: 2

Views: 172

Answers (1)

Gwen
Gwen

Reputation: 1

Change this bit from:

transform: rotate3d(0, 1, 0, -180deg)

to:

transform: rotate3d(0, 1, 0, -179deg)

Technically there are two correct ways to animate from 180 to -180; Firefox just happens to pick the "downward and away" path that looks wrong for this particular effect. By tweaking the animation from 180 to -179, it ensures the "up and over" path is the shorter one, thus always picked by both browsers.

function turnLeft() {
  page = document.getElementById('page');
  page.classList.remove('turnRight')
  page.classList.add('turnLeft')
}

function turnRight() {
  page = document.getElementById('page');
  page.classList.remove('turnLeft')
  page.classList.add('turnRight')
}
body {
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  height: 100vh;
  width: 100vw;
  background-color: aliceblue;
  perspective: 1000px;
}

.page {
  position: absolute;
  left: 50%;
  top: 50%;
  height: 50%;
  width: 25%;
  transform: translateY(-50%) rotateX(25deg);
  border: solid 1px black;
  transform-origin: 0% 45%;
  transform-style: preserve-3d;
}

.front {
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  background-color: red;
  transform-style: preserve-3d;
}

.back {
  position: absolute;
  box-sizing: content-box;
  border: solid 2px #000;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  transform: rotate3d(0, 1, 0, 180deg);
  background-color: green;
  transform-style: preserve-3d;
}

.turnLeft {
  animation: turnPageToLeft 0.4s ease-in 0s 1 normal forwards;
}

.turnRight {
  animation: turnPageToRight 0.4s ease-in 0s 1 normal forwards;
}

@keyframes turnPageToLeft {
  0% {
    transform: translateY(-50%) rotateX(25deg);
  }
  100% {
    transform: rotate3d(0, 1, 0, -179deg) translateY(-51%) rotateX(-25deg);
  }
}


@keyframes turnPageToRight {
  0% {
    transform: rotate3d(0, 1, 0, -179deg) translateY(-51%) rotateX(-25deg);
  }
  100% {
    transform: translateY(-50%) rotateX(25deg);
  }
}
<body>
  <div id="page" class="page">
    <div class="front"></div>
    <div class="back"></div>
  </div>
  <button onclick="turnLeft()">Turn Left</button>
  <button onclick="turnRight()">Turn Right</button>
</body>

Upvotes: 0

Related Questions