是否可以在不使用事件的情况下检查 CSS 转换是否完成?
Is it possible to check if a CSS Transition is finished WITHOUT using events?
是否可以通过 JavaScript 以某种方式验证 CSS-转换是否已完成,而无需预先注册转换事件?
问题是:
- 我有一个 Web 应用程序,它使用 CSS 过渡在页面加载时淡化某些元素
- 我无法修改此 Web 应用程序 JavaScript 代码
- 当我访问这个页面时,我可以在浏览器控制台中执行JavaScript
- 我想确保 CSS 过渡已 100% 完成,然后再继续我的自定义 java 脚本代码
- 在浏览器控制台中,我可以挂钩到过渡事件,但在很多情况下这会失败,因为:
- 过渡元素还没有
- 设置钩子时动画已经完成
是否可以通过 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;
是否可以通过 JavaScript 以某种方式验证 CSS-转换是否已完成,而无需预先注册转换事件?
问题是:
- 我有一个 Web 应用程序,它使用 CSS 过渡在页面加载时淡化某些元素
- 我无法修改此 Web 应用程序 JavaScript 代码
- 当我访问这个页面时,我可以在浏览器控制台中执行JavaScript
- 我想确保 CSS 过渡已 100% 完成,然后再继续我的自定义 java 脚本代码
- 在浏览器控制台中,我可以挂钩到过渡事件,但在很多情况下这会失败,因为:
- 过渡元素还没有
- 设置钩子时动画已经完成
是否可以通过 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;