Firefox CSS 旋转不同于 Chrome 旋转
Firefox CSS rotation differs from Chrome rotation
我想制作一个用户可以使用箭头移动的 3D 矩形(平行六面体)。它在 Chrome 中运行良好,但在 Firefox 中,某些转换(实际上很多)与 Chrome 不同。查看 this fiddle(这是我的全部代码)并在两种浏览器中进行比较以更好地理解。
因为第一个fiddle包含很多代码,我会简化它并随机选择一个奇怪的过渡。查看 this fiddle,然后按一次 "Left" 按钮或向左箭头。它工作正常,但是当你再次按下它时,矩形旋转了 3 次而不是 1 次。
这是 Firefox 的错误还是我做错了什么?
下面的代码是您可以在简化的 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>
看起来您做的一切都正确,Chrome 与 Firefox 的轮换差异是由两种浏览器处理 CSS3 的方式造成的。查看从 show-back-4
到 show-top-4
的轮换时,您的 CSS 文件将轮换指定为 270deg
。在 Firefox 中,它就是这样做的。在 Chrome 中,它看起来像是优化而不是完全旋转,从而节省了处理能力或其他东西。所以是的,我认为这只是浏览器的差异,而不是其中任何一个的错误。
您可以尝试使用关键帧来更好地控制动画,如下所示:
https://jsfiddle.net/L36v50kh/2/
我正在为 fiddle 中的所有转换定义起点和终点,如下所示:
@keyframes front-to-right {
from {transform: translateZ(-50px) rotateY(0deg); }
to {transform: translateZ(-150px) rotateY(-90deg);}
}
这在两种浏览器中看起来都一样,但在动画结束前单击按钮时会跳动。
您还可以考虑使用 JavaScript 制作动画以获得精确控制并避免定义每个过渡,如下所示:
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>
基于 Firefox 在这方面只是错误的假设(见下面的分析),here is a workaround 在 Firefox 上工作。它将 #box
元素包装在另一个 div
中,并且只转换包装器。而且包装器一次只能从起点沿一个方向旋转 90 度,因此 Firefox 不会搞砸它。
过渡完成后,旋转将重置回起始位置,同时内框旋转到新位置,两者都没有过渡,因此变化不可见。
第二个重要变化是使用 #box
的当前计算变换并向其添加旋转,这样我们就不必在进行时跟踪旋转。
请注意旋转顺序很重要。要实现您想要做的事情(以 "world space" 而不是 "object space" 旋转),您需要以相反的顺序应用旋转。例如。要旋转 "right",请使用 .css("transform", "rotateY(90deg) " + currentComputedTransform)
。这将解决您在评论中提到的似乎围绕错误轴旋转的问题。有关详细信息,请参阅下文。
另请注意,如果已经有轮换,我不允许开始轮换,因为那样做是行不通的。如果你想这样做,你可以在一个数组中排队击键,但在这种情况下你可能还想减少与队列长度成比例的过渡持续时间,这样它就不会永远持续下去。
已更新 fiddle:https://jsfiddle.net/955k5fhh/7/
相关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");
});
之前的分析
我一直在玩基于 JS 的解决方案,我遇到了这个有用的 post https://gamedev.stackexchange.com/a/67317 - 它指出在 "world space" 而不是 [=58] 中旋转对象=], 你只需要颠倒旋转的顺序。
基于此,我将您的 fiddle 简化为以下内容:
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/(请注意,这不是一个完整的解决方案,因为最终 rot
字符串会变得太长)
而在 Chrome 上,其表现符合预期。再一次,Firefox 弄错了,即使你只是链接例如一系列 rotateX(90deg)
转换。
所以我更进一步,在同一轴上卷起相邻的旋转...
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/
同样,它在 Chrome 中运行良好,在 Firefox 中运行得更好一些,但是一旦你切换轴,你可能会以错误的方式旋转。同样,如果您在转换完成之前单击按钮,它可能会以错误的方式旋转。
所以我得出的结论是,不幸的是,Firefox 只是在这方面有问题,但至少有解决方法。
我想制作一个用户可以使用箭头移动的 3D 矩形(平行六面体)。它在 Chrome 中运行良好,但在 Firefox 中,某些转换(实际上很多)与 Chrome 不同。查看 this fiddle(这是我的全部代码)并在两种浏览器中进行比较以更好地理解。
因为第一个fiddle包含很多代码,我会简化它并随机选择一个奇怪的过渡。查看 this fiddle,然后按一次 "Left" 按钮或向左箭头。它工作正常,但是当你再次按下它时,矩形旋转了 3 次而不是 1 次。
这是 Firefox 的错误还是我做错了什么?
下面的代码是您可以在简化的 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>
看起来您做的一切都正确,Chrome 与 Firefox 的轮换差异是由两种浏览器处理 CSS3 的方式造成的。查看从 show-back-4
到 show-top-4
的轮换时,您的 CSS 文件将轮换指定为 270deg
。在 Firefox 中,它就是这样做的。在 Chrome 中,它看起来像是优化而不是完全旋转,从而节省了处理能力或其他东西。所以是的,我认为这只是浏览器的差异,而不是其中任何一个的错误。
您可以尝试使用关键帧来更好地控制动画,如下所示:
https://jsfiddle.net/L36v50kh/2/
我正在为 fiddle 中的所有转换定义起点和终点,如下所示:
@keyframes front-to-right {
from {transform: translateZ(-50px) rotateY(0deg); }
to {transform: translateZ(-150px) rotateY(-90deg);}
}
这在两种浏览器中看起来都一样,但在动画结束前单击按钮时会跳动。
您还可以考虑使用 JavaScript 制作动画以获得精确控制并避免定义每个过渡,如下所示:
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>
基于 Firefox 在这方面只是错误的假设(见下面的分析),here is a workaround 在 Firefox 上工作。它将 #box
元素包装在另一个 div
中,并且只转换包装器。而且包装器一次只能从起点沿一个方向旋转 90 度,因此 Firefox 不会搞砸它。
过渡完成后,旋转将重置回起始位置,同时内框旋转到新位置,两者都没有过渡,因此变化不可见。
第二个重要变化是使用 #box
的当前计算变换并向其添加旋转,这样我们就不必在进行时跟踪旋转。
请注意旋转顺序很重要。要实现您想要做的事情(以 "world space" 而不是 "object space" 旋转),您需要以相反的顺序应用旋转。例如。要旋转 "right",请使用 .css("transform", "rotateY(90deg) " + currentComputedTransform)
。这将解决您在评论中提到的似乎围绕错误轴旋转的问题。有关详细信息,请参阅下文。
另请注意,如果已经有轮换,我不允许开始轮换,因为那样做是行不通的。如果你想这样做,你可以在一个数组中排队击键,但在这种情况下你可能还想减少与队列长度成比例的过渡持续时间,这样它就不会永远持续下去。
已更新 fiddle:https://jsfiddle.net/955k5fhh/7/
相关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");
});
之前的分析
我一直在玩基于 JS 的解决方案,我遇到了这个有用的 post https://gamedev.stackexchange.com/a/67317 - 它指出在 "world space" 而不是 [=58] 中旋转对象=], 你只需要颠倒旋转的顺序。
基于此,我将您的 fiddle 简化为以下内容:
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/(请注意,这不是一个完整的解决方案,因为最终 rot
字符串会变得太长)
而在 Chrome 上,其表现符合预期。再一次,Firefox 弄错了,即使你只是链接例如一系列 rotateX(90deg)
转换。
所以我更进一步,在同一轴上卷起相邻的旋转...
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/
同样,它在 Chrome 中运行良好,在 Firefox 中运行得更好一些,但是一旦你切换轴,你可能会以错误的方式旋转。同样,如果您在转换完成之前单击按钮,它可能会以错误的方式旋转。
所以我得出的结论是,不幸的是,Firefox 只是在这方面有问题,但至少有解决方法。