Извините за задержку, был на встрече. Искал один из моих старых проектов, но не смог найти. Я в общих чертах изложу эту идею. Сначала я подумал, что, возможно, мы могли бы обратиться к Mutation Observer, однако мы также должны проводить там периодические проверки. Так что я думаю, что это подойдет. Прежде всего вам следует избегать некоторых вещей:
- вызывать getComputedStyle каждый кадр, это плохая идея, потому что это очень дорогая функция для вызова и запускает макет, поэтому вместо этого вам следует регулировать скорость.
- объект стиля с жесткой копией, это здоровенный объект для копирования, поэтому вместо этого вам следует передать аргумент для определенного свойства
- при работе со ссылкой на узел, если узел не существует, как вы сказали, это выдаст ошибку ссылки, вместо этого используйте функцию для возврата узла.
Первым делом нужно написать вспомогательную функцию, которая будет периодически запускать некоторую тестовую функцию и возвращать ее в случае успеха:
function watchman(obj,test,action,options){
var currentFrame = {value:undefined};
function watch(){
if(test.call(obj,options)){
action.call(obj,options);
return;
} else {
currentFrame.value = window.requestAnimationFrame(watch);
}
};
currentFrame.value = window.requestAnimationFrame(watch);
return currentFrame;
};
Далее идет фактическая функция, я бы сказал, что нет необходимости создавать новый объект, мы можем создать функцию с 3 (2 необязательными) аргументами, нодом "functor", свойством стиля для проверки и, наконец, функцией для вызова. .
function onTransitionEnd(fNode,prop,f,precision,timeout){
precision = precision || 5;
timeout = timeout || Infinity;
return new Promise(function(res){
var node = fNode(),
oValue = node && getComputedStyle(node)[prop],
currentFrame = watchman(
fNode,
function(counter){
if(counter.counter * 17 >= timeout){
window.cancelAnimationFrame(currentFrame.value);
}
if(++counter.counter % precision === 0) {
if(!this()){return}
var nValue = getComputedStyle(this())[prop];
if(nValue === oValue) {
return true;
}
oValue = nValue;
}
},
function(counter){
res(f.call(fNode(),prop));
},
{counter:0}
);
});
};
Точность по умолчанию 5 означает, что функция будет проверять каждые 5 тиков, 5 * 17 миллисекунд, чтобы определить, завершился ли переход. Тайм-аут не является обязательным, он отменяет запуск после определенного периода.
Это не проблема, если узел НЕ существует, так как мы передаем функцию, которая возвращает узел или ноль, если узел не существует, он не будет выполняться.
Выше обещание, которое вернет "жизнеспособный" объект, который вы можете связать, как хотите.
Простой вариант использования, например, сразу после изменения стиля или класса:
document.getElementById("someDiv").className = "def c1";
onTransitionEnd(
function(){return document.getElementById("someDiv");},
"transform",
function(){alert("heyy!!");}
);
теперь будет предупреждать вас "привет". Чтобы связать это:
document.getElementById("someDiv").className = "def c1";
onTransitionEnd(
function(){return document.getElementById("someDiv");},
"transform",
function(prop){alert("heyy!!"); return this;}
).then(function(node){
node.className = "def";
return onTransitionEnd(
function(){return document.getElementById("someDiv");},
"transform",
function(){alert("heyy-2!!"); return this;}
);
}).then(function(node){
alert("id is " + node.id);
});
Вот несколько примеров:
, чтобы последний работал, откройте консоль разработчика, выберите синий div и измените его id на "someDiv", функция будет выполняться.
Вы можете задаться вопросом, вызывать ли onTransitionEnd каждый раз, когда вы меняете стиль, в этом случае вы можете написать оболочку. Если у вас нет идеи, дайте мне знать, я тоже напишу это.
Очевидно, что вы не использовали оболочку, поэтому вот вспомогательная оболочка:
function Select(node){
this.node = node;
};
Select.prototype.style = function(prop,value,f){
var node = this.node;
this.node.style[prop] = value;
f && onTransitionEnd(
function(){return node;},
prop,
f
);
return this;
};
Вот как бы вы его использовали:
var selection = new Select(document.getElementById("someDiv"));
selection
.style("width","100px",function(propName){alert(propName + " changed!");})
.style("height","100px",function(propName){alert(propName + " changed!");})
.style("transform","scale(0.5,0.5)",function(propName){alert(propName + " changed!");});
А вот и ПРИМЕР ;
ЦЕПИ ВРЕМЕНИ ;