Можно ли проверить, завершен ли переход CSS БЕЗ использования событий? - PullRequest
1 голос
/ 30 апреля 2019

Можно ли как-то с помощью JavaScript проверить, что CSS-переход завершен без предварительной регистрации событий перехода?

Проблема:

  • У меня есть веб-приложение, которое использует CSS-переходы для затухания некоторых элементов при загрузке страницы
  • Я не могу изменить этот код JavaScript для веб-приложений
  • Когда я захожу на эту страницу, я могу выполнить JavaScript в консоли браузера
  • Я хочу убедиться, что переход CSS завершен на 100%, прежде чем я продолжу свой собственный код сценария Java
  • в консоли браузера я мог подключиться к событию перехода, но во многих случаях это не удавалось, потому что:
    • переходного элемента еще нет
    • анимация уже закончена, когда я установил ловушку

Есть ли возможность проверить через JavaScript, выполнен ли переход CSS для элемента? В любое время?

Я не могу использовать события javascript (например, https://jonsuh.com/blog/detect-the-end-of-css-animations-and-transitions-with-javascript/)

Ответы [ 3 ]

2 голосов
/ 30 апреля 2019

Нет.

Лучшее, что вы можете сделать, это посмотреть на CSS, чтобы увидеть продолжительность перехода.

0 голосов
/ 30 апреля 2019

ВОЗМОЖНО

ЦЕПИ ВРЕМЕНИ ;

Извините за задержку, был на встрече. Искал один из моих старых проектов, но не смог найти. Я в общих чертах изложу эту идею. Сначала я подумал, что, возможно, мы могли бы обратиться к 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!");});

А вот и ПРИМЕР ;

ЦЕПИ ВРЕМЕНИ ;

0 голосов
/ 30 апреля 2019

Не ответ, а быстрый POC, на котором можно основываться:

element.onclick = function() {
  const color = 0x1000 | (Math.random() * 0x1000);
  
  const prop = Math.random() < .5? "background-color": "color";
  
  element.style[prop] = "#" + color.toString(16).slice(-3);
}

let curr = {};
requestAnimationFrame(function check() {
  const prev = curr;

  curr = Object.assign({}, getComputedStyle(element));

  const changed = Object.keys(curr).filter(key => curr[key] !== prev[key]);

  out.innerHTML = changed.map(key => `<li>${key}</li>`).join("\n");

  requestAnimationFrame(check);
});
html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

#element {
  cursor: pointer;
  width: 100%;
  height: 100%;
  padding: 20px;
  transition: all 1s;
}
<div id="element">
  <p>Click somewhere</p>

  currently transitioning:
  <ul id="out"></ul>
  
</div>

вы заметите мерцание, потому что два соседних кадра могут не отличаться во время этой интерполяции. Вы хотели бы кэшировать больше кадров и сравнивать что-то вроде 5-10 кадров друг от друга; зависит от используемого вами метода интерполяции и продолжительности перехода.

Кроме того, в зависимости от свойств, которые вы проверяете, вы также можете сравнить getComputedStyle(element)[key] с element.style[key] вместо сохранения значений для нескольких кадров. Но это не работает для цвета (и других), так как существует множество способов описать один и тот же цвет.

...