是否可以在不使用事件的情况下检查 CSS 转换是否完成?

Is it possible to check if a CSS Transition is finished WITHOUT using events?

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

问题是:

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

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

没有

您最多只能查看 CSS 以查看过渡持续时间。

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

IT IS POSSIBLE

TIMED CHAINS;

抱歉耽搁了,正在开会。搜索了我的一个旧项目,但找不到。我将在这里勾勒出这个想法,我最初认为也许我们可以选择 Mutation Observer,但我们也必须在那里进行定期检查。所以我想这个就可以了。首先你应该避免一些事情:

  • 每帧都调用 getComputedStyle,这是一个坏主意,因为调用它是一个非常昂贵的函数并触发布局,因此您应该改为节流。
  • 硬拷贝样式对象,这是一个需要复制的庞大对象,因此您应该为特定 属性
  • 传递一个参数
  • 使用节点引用,如果节点不存在,如您所说,这将抛出引用错误,而不是使用函数 return 节点。

第一件事是写一个辅助函数,一个定期运行一些测试函数和return如果成功的函数:

 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 毫秒的值以确定转换是否结束。超时也是可选的,它会在一定时间后取消运行ning。

如果节点不存在也不是问题,因为我们正在传递一个 return 节点或 null 的函数,如果节点不存在,它将不会执行。

以上是一个承诺,将 return 一个 "thenable" 对象,您可以根据需要链接该对象。

简单用例,例如在您更改样式或 class:

document.getElementById("someDiv").className = "def c1";
onTransitionEnd(
  function(){return document.getElementById("someDiv");},
  "transform",
  function(){alert("heyy!!");}
);

它现在会提醒您 "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);
});

这里有一些例子:

为了最后一个工作,打开开发者控制台,select蓝色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;