是否可以使用事件检查CSS转换是否完成?

时间:2019-04-30 11:36:40

标签: javascript css-transitions

是否可以通过JavaScript以某种方式验证CSS过渡是否完成而无需预先注册过渡事件?

问题是:

  • 我有一个Web应用程序,该应用程序使用CSS过渡来淡化页面加载时的某些元素
  • 我无法修改此Web应用程序的JavaScript代码
  • 访问此页面时,我可以在浏览器控制台中执行JavaScript
  • 我想确保CSS转换已完成100%,然后再继续执行自定义Java脚本代码
  • 在浏览器控制台中,我可以切换到转移事件,但是在许多情况下,这会失败,因为:
    • 转换元素还不存在
    • 设置挂钩时动画已经完成

是否有可能通过JavaScript检查元素的CSS转换是否完成?随时吗?

我无法利用javascript事件(例如https://jonsuh.com/blog/detect-the-end-of-css-animations-and-transitions-with-javascript/

3 个答案:

答案 0 :(得分:2)

否。

您能做的最好的就是查看CSS以查看过渡持续时间。

答案 1 :(得分:0)

不是答案,而是一种快速的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]进行比较,而不必存储多个帧的值。但这不适用于颜色(和其他颜色),因为描述相同颜色的方法很多。

答案 2 :(得分:0)

IT IS POSSIBLE

TIMED CHAINS;

抱歉,正在开会。搜索了我的一个旧项目,但找不到。我将在这里概述这个想法,最初我想也许我们可以去使用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”,要检查的style属性以及最后要调用的函数的函数

 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毫秒)检查一次值以确定过渡是否结束。超时也是可选的,它会在一段时间后取消运行。

如果节点不存在就没有问题,因为我们传递了一个返回该节点或为null的函数,如果该节点不存在,它将不会执行。

以上是一个承诺,它将返回一个“ thenable”对象,您可以随意链接它。

简单的用例,例如,在更改样式或类之后:

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!");});

这是EXAMPLE;

TIMED CHAINS;