transitionEnd
事件会在结束第一次但不是最后一次的转换时触发,这不是所需的行为。任何解决方法?
document.querySelector('a').addEventListener('transitionend', function(){
var time = (new Date().getMinutes()) + ':' + (new Date().getSeconds());
console.log('transitionEnd - ', time);
});

a{
display:block;
opacity:.5;
width:100px;
height:50px;
background:lightblue;
}
a:hover{
width:200px;
height:100px;
background:red;
transition: 4s width, /* <-- "transitionEnd" should fire after this */
2s height,
.5s background;
}
&#13;
<a>Hover me</a>
&#13;
现在我检查Chrome(我不使用该浏览器),我看到该事件被调用3次,每次转换一次。无论如何,我需要它来解决最后一个,并在Firefox中。 (无论如何我都不知道元素上有多少转换,知道哪个转换是最后一个)
答案 0 :(得分:3)
transitionEnd
在事件对象中返回名为propertyName
的属性。
Source
因此,您可以测试所需的属性,并使用简单的逻辑来过滤回调:
document.querySelector('a').addEventListener('transitionend', function(event){
if(event.propertyName !== 'width') return;
console.log('transitionEnd - width!');
});
答案 1 :(得分:2)
一些hacky解决方案可能是试图找出哪个css属性的总持续时间最长。您可以在window.getComputedStyle
元素上使用<a>
并添加所有duration
和delay
属性来执行此操作。
你可以在三次触发的常规事件处理程序中执行此操作(它非常快),或者创建一个预先计算您正在寻找的属性名称的函数。
这种方法存在的主要问题:
ms
和s
,您可能需要进行更多计算。transition
样式或在转换之前/之后添加新类时,很难预测计算样式是什么。
var getValues = function(str) {
return str
.replace(/[A-Z]/gi, "")
.split(", ")
.map(parseFloat);
};
var getMaxTransitionProp = function(el) {
var style = window.getComputedStyle(el);
var props = style.transitionProperty.split(", ");
var delays = getValues(style.transitionDelay);
var durations = getValues(style.transitionDuration);
var totals = durations.map(function(v, i) {
return v + delays[i];
});
var maxIndex = totals.reduce(function(res, cur, i) {
if (res.val > cur) {
res.val = cur;
res.i = i;
}
return res;
}, {
val: -Infinity,
i: 0
}).i;
return props[maxIndex];
}
var lastEventListenerFor = function(el, cb) {
var lastProp = getMaxTransitionProp(el);
return function(e) {
if (e.propertyName == lastProp) {
cb(e);
}
};
}
var a = document.querySelector("a");
var cb = function(e) {
console.log("End");
};
a.addEventListener("transitionend", lastEventListenerFor(a, cb));
&#13;
a {
display: block;
opacity: .5;
width: 100px;
height: 50px;
background: lightblue;
transition: 3s width,
/* <-- "transitionEnd" should fire after this */
2s height, .5s background;
}
a:hover {
width: 200px;
height: 100px;
background: red;
}
&#13;
<a>Hover me</a>
&#13;
答案 2 :(得分:1)
我现在可能是老问题,但遇到同样的麻烦。 以这种方式解决:
document.querySelector('a').addEventListener('click', function(e){
this.classList.toggle('animate');
let style = window.getComputedStyle(this, null);
Promise.all(style.transition.split(',').map((prop) => {
prop = prop.trim().split(/\s+/);
return Promise.race([
new Promise((resolve) => {
let h = (e) => {
if (e.propertyName == prop[0])
resolve('transitionEnd ' + prop[0]);
};
this.addEventListener('transitionend', h, {once:false});
}),
new Promise((resolve) =>
setTimeout(
() => resolve('TIMEOUT ' + prop[0]),
prop[1].replace(/s/,'')*1000 + 100
)
)
])
}))
.then((res) => {
console.log(res + 'DONE!!!!');
/* do your stuff */
});
});
说明:
免责声明:
你可以在jsfiddle
上玩它答案 3 :(得分:0)
我以更简单的方式解决了这个问题(在我看来)。就我而言,我正在为 svg 的路径(剪切路径)设置动画。所以我在监听 cilpPath 元素 transitionEnd 事件。每条路径都以不同的时间动画,但是(我的情况只有一个属性)。
解决方案:
计算所有 transitionStart 和存储元素在集合中。 (如果是一个具有许多转换的元素,我相信可以存储 propertyName 或某些 id: element-property)。然后在 transtionEnd 事件上从当前运行的转换集中删除元素/属性。如果 set 为空,则所有转换都已结束。
(function() {
window.addEventListener("load", () => {
const animated = document.querySelectorAll("[data-animation]") || [];
[...animated].forEach(target => {
animate(target as HTMLElement);
})
})
function animate(target: HTMLElement){
let currentFrame = 0;
let frameLimit = parseInt(target.getAttribute("data-animation-frames") || "0");
const setFrame = (frame: number) => {
currentFrame = frame;
target.setAttribute("data-animation-frame", frame.toString());
}
const runningTransitions = new Set<HTMLElement>();
target.addEventListener("transitionend", function transitionEnd(e){
runningTransitions.delete(e.target as HTMLElement);
if(runningTransitions.size)
return;
const nextFrame = currentFrame + 1;
if(nextFrame > frameLimit){
target.removeEventListener("transitionend", transitionEnd);
return;
}
setFrame(nextFrame);
})
target.addEventListener("transitionstart", function transitionStart(e){
runningTransitions.add(e.target as HTMLElement)
})
setFrame(currentFrame);
}
})()