假设我有这样的风格,有一个过渡:
#theElement {
position: relative;
left: 200px;
transition: left 1s;
}
还有一些代码:
var element = document.getElementById("theElement");
function animateX(px) {
element.style.left = px + "px";
}
所以,简单地说有一个animateX
函数,它只是说它的作用,为theElement
的左属性进行动画处理,现在如果我还想拥有一个立即设置 left 属性的函数,而无需过渡,该怎么办:
function setX(px) {
element.style.transition = "none";
element.style.left = px + "px";
element.style.transition = "left 1s";
}
为什么这不起作用,我该如何解决?
为了完成这项工作,您需要通过在 JS 中读取 CSS 更改来刷新它们。这个问题的答案解释了它是如何工作的:
我可以使用javascript强制浏览器"刷新"任何待处理的布局更改吗?
工作示例如下:
var element = document.getElementById("theElement");
function animateX(px) {
element.style.left = px + "px";
}
function setX(px) {
element.style.transition = "none";
element.style.left = px + "px";
// apply the "transition: none" and "left: Xpx" rule immediately
flushCss(element);
// restore animation
element.style.transition = "";
}
function flushCss(element) {
// By reading the offsetHeight property, we are forcing
// the browser to flush the pending CSS changes (which it
// does to ensure the value obtained is accurate).
element.offsetHeight;
}
#theElement {
position: relative;
left: 200px;
transition: left 1s;
width: 50px;
height: 50px;
background: red;
}
.wrapper {
width: 400px;
height: 100px;
}
<div class="wrapper">
<div id="theElement"></div>
</div>
<button onclick="animateX(100)">Animate 100</button>
<button onclick="setX(0)">Set 0</button>
其他解决方案很好,但我想提供一种替代方案并解释正在发生的事情。
在您的示例中,它未按预期工作的原因是因为所有 CSS 属性都已更改,然后触发浏览器重排事件并开始转换。换句话说,transition
属性设置为 none
,然后更改 left
属性,然后将 transition
属性更改回 left 1s
。
更新所有样式属性后,将触发浏览器重排事件,重新绘制 CSS,然后开始转换:
element.style.transition = "none";
element.style.left = px + "px";
element.style.transition = "left 1s";
// The transition starts after all the CSS has been modified and a reflow has been trigged.
它像这样执行有几个原因。
主要原因是性能。与其在更改单个 CSS 属性后重新绘制每个元素,不如更改所有属性并具有单个重绘事件更有效。此外,如果要转换多个属性,则希望在转换元素之前更改每个属性(这正是正在发生的事情(。
如果您想要一个不涉及任何超时/延迟或强制重排的干净替代方法,则只需在设置值之前将 transitionDuration
属性设置为 0s
,然后在转换元素时删除此内联样式。
例如:
var element = document.getElementById("theElement");
function setX(px) {
element.style.transitionDuration = '0s';
element.style.left = px + "px";
}
function animateX(px) {
element.style.transitionDuration = '';
element.style.left = px + "px";
}
#theElement {
position: relative;
width: 100px;
height: 100px;
border: 2px solid;
left: 200px;
transition: left 1s;
}
<div id="theElement"></div>
<button onclick="animateX(100)">Transition 100</button>
<button onclick="animateX(300)">Transition 300</button>
<button onclick="setX(0)">Set 0</button>
<button onclick="setX(50)">Set 50</button>
同样,您也可以在转换或设置值之前切换元素上的类:
var element = document.getElementById("theElement");
function setX(px) {
element.classList.remove('transition');
element.style.left = px + "px";
}
function animateX(px) {
element.classList.add('transition');
element.style.left = px + "px";
}
#theElement {
position: relative;
width: 100px;
height: 100px;
border: 2px solid;
left: 200px;
}
#theElement.transition {
transition: left 1s;
}
<div id="theElement"></div>
<button onclick="animateX(100)">Transition 100</button>
<button onclick="animateX(300)">Transition 300</button>
<button onclick="setX(0)">Set 0</button>
<button onclick="setX(50)">Set 50</button>
上面的代码片段有效,但值得指出的是,您可以侦听 transitionend
事件并在转换结束时删除类:
例如:
function animateX(px) {
element.classList.add('transition');
element.style.left = px + "px";
element.addEventListener('transitionend', callback);
function callback() {
this.classList.remove('transition');
this.removeEventListener('transitionend', callback);
}
}
您可以执行此操作,但必须使用超时还原旧的转换属性。
function setX(px) {
element.style.transition = "none";
element.style.left = px + "px";
setTimeout(function() {
element.style.transition = "left 1s";
});
}
JSFiddle
如果不使用超时来还原旧的转换属性,浏览器将永远不会知道它已更改。除非您的代码已完全执行,否则它不会呈现页面。
我还建议像这样取消设置过渡属性:
element.style.transition = "";
否则,您将永久覆盖此元素的样式表。在元素上设置过渡样式之前,过渡由样式表确定。如果在元素上设置过渡样式,则会覆盖样式表。
更新:似乎超时还不够。我在FF/Linux中尝试过,但它不起作用。因此,您要么再添加一些毫秒(不推荐(,要么应用@jperezov的解决方案(推荐(。
只是为了对Josh Crozier的回答进行一些补充:
还存在 ontransitioncalcel 事件,该事件在事件被取消时触发,因此您可以测试某些方法是否取消它。从目前的草案中,我挑选:
如果元素不再存在于文档中,则实现必须取消任何正在运行的转换
因此,您可以删除元素,进行更改,然后将元素添加回其原始位置; 设置display: none
有各种副作用,停止它们之间的过渡,
如果元素具有属性的运行转换,并且没有匹配的转换属性值,则实现必须取消运行转换
因此,您可以尝试删除转换属性值,
如果运行转换中属性的当前值等于更改后样式中的属性值,或者这两个值不可转换,则实现必须取消运行转换。
因此,您可以将 left 属性设置为 auto
或先initial
以中断过渡。
唉,没有一个过渡事件是可以取消的,所以在其中调用e.preventDefault()
不应该阻止它们。