是否可以使用javascript、jQuery或其他方式获取/设置CSS3 @keyframes
动画的当前动画百分比?
例如,有一个类为.spin
的div
,它只是使用一个也称为spin
的关键帧在圆中旋转。
-
我尝试过使用
$('.spin').css('animation')
、$('.spin').css('animation: spin')
和其他几种方法来获取动画的当前百分比值,但它们都会提醒空 -
我还对在每个预定义的关键帧%更改原始动画感兴趣,我尝试过
$('.spin').css('animation', '..new definition here...')
和$('.spin').css('spin', '50%', '...new definition here...)
之类的东西,但都没有成功。
有什么想法吗?
我在CSS3中使用纯javascript大致实现了我想要的。
为了让我的实验找到实现这些目标的方法,我创建了一个基本的CSS3动画,围绕一个小的圆形路径旋转一个圆圈。我的目标是,当点击一个按钮时,将动画的原点更改为新位置
为了实现获得动画百分比值的第一个目标,我使用以下setInterval
简单地近似了当前百分比,它显示了近似的当前百分比。这与动画(milliseconds / 100)
的持续时间相对应,每40毫秒运行一次
var result = document.getElementById('result'), currentPercent = 0;
var showPercent = window.setInterval(function() {
if(currentPercent < 100)
{
currentPercent += 1;
}
else {
currentPercent = 0;
}
result.innerHTML = currentPercent;
}, 40);
关于此解决方案的说明:
- 这并不完美,因为当单击另一个选项卡时计数器会继续运行,但动画会停止,因此它们会变得不同步
- 在最后一次点击后很长时间点击该按钮时,它也会出现故障。显然,setInterval的运行时间比动画长一点,因此它们在每次迭代中的同步性越来越低
- 我到处寻找更完美的解决方案,但到目前为止还没能找到
要实现为动画的%值设置新定义的第二个目标,需要一个更复杂的解决方案。在浏览了几十篇文章和网页(下面列出了重要的文章和网页)后,我设法想出了以下解决方案:
var circle = document.getElementById('circle'),
button = document.getElementById('button');
var result = document.getElementById('result'),
totalCurrentPercent = 0,
currentPercent = 0;
var showPercent = window.setInterval(function() {
if(currentPercent < 100)
{
currentPercent += 1;
}
else {
currentPercent = 0;
}
result.innerHTML = currentPercent;
}, 40);
function findKeyframesRule(rule) {
var ss = document.styleSheets;
for (var i = 0; i < ss.length; ++i) {
for (var j = 0; j < ss[i].cssRules.length; ++j) {
if (ss[i].cssRules[j].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && ss[i].cssRules[j].name == rule) { return ss[i].cssRules[j]; }
}
}
return null;
}
function change(anim) {
var keyframes = findKeyframesRule(anim),
length = keyframes.cssRules.length;
var keyframeString = [];
for(var i = 0; i < length; i ++)
{
keyframeString.push(keyframes[i].keyText);
}
var keys = keyframeString.map(function(str) {
return str.replace('%', '');
});
totalCurrentPercent += currentPercent;
if(totalCurrentPercent > 100)
{
totalCurrentPercent -= 100;
}
var closest = getClosest(keys);
var position = keys.indexOf(closest),
firstPercent = keys[position];
for(var i = 0, j = keyframeString.length; i < j; i ++)
{
keyframes.deleteRule(keyframeString[i]);
}
var multiplier = firstPercent * 3.6;
keyframes.insertRule("0% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 0) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 0) + "deg); background-color:red; }");
keyframes.insertRule("13% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 45) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 45) + "deg); }");
keyframes.insertRule("25% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 90) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 90) + "deg); }");
keyframes.insertRule("38% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 135) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 135) + "deg); }");
keyframes.insertRule("50% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 180) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 180) + "deg); }");
keyframes.insertRule("63% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 225) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 225) + "deg); }");
keyframes.insertRule("75% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 270) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 270) + "deg); }");
keyframes.insertRule("88% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 315) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 315) + "deg); }");
keyframes.insertRule("100% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 360) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 360) + "deg); }");
circle.style.display = "inherit";
circle.style.webkitAnimationName = anim;
window.clearInterval(showPercent);
currentPercent = 0;
showPercent = self.setInterval(function() {
if(currentPercent < 100)
{
currentPercent += 1;
}
else {
currentPercent = 0;
}
result.innerHTML = currentPercent;
}, 40);
}
button.onclick = function() {
circle.style.webkitAnimationName = "none";
circle.style.display = "none";
setTimeout(function () {
change("rotate");
}, 0);
}
function getClosest(keyframe) {
var curr = keyframe[0];
var diff = Math.abs (totalCurrentPercent - curr);
for (var val = 0; val < keyframe.length; val++) {
var newdiff = Math.abs(totalCurrentPercent - keyframe[val]);
if (newdiff < diff) {
diff = newdiff;
curr = keyframe[val];
}
}
return curr;
}
在此处查看实验本身包括描述javascript的每个部分正在进行的注释
关于此解决方案的说明:
- 我试图使更改函数尽可能地非硬编码
- 它可以近似当前的
@keyvalue
百分比 - 从一个动画到另一个动画的过渡只有当动画的%值与动画的当前%值最接近时才是平滑的,因此向动画添加更多的%定义会使其更加平滑
在试图为这个问题找到解决方案的过程中,我发现了以下有用的资源:
- RussellUresti在这篇SO文章中的回答和相应的例子非常有影响力,极大地帮助了我的最终解决方案
- 为了根据输入和数组值获得最接近的值,我在这篇SO文章中使用了paxdiablo的方法(谢谢)
- 虽然我自己没有使用这个插件,但它似乎在jQuery中实现了非常相似的效果(尽管似乎没有那么可定制)
---编辑---
如果您只是使用CSS转换,或者您可以将动画更改为只使用转换,则可以使用这种更简单的方法。您可以通过复制转换所更改的属性来暂停转换,将属性设置为更改后的属性,然后删除为其设置动画的类。当然,如果您使用同一对象来设置动画/暂停,则需要为第一次单击设置动画,然后在下一次单击时暂停。你可以很容易地做纯javascript等价物以及
注意:CSS更改属性中的!important
是必要的,除非动画类有一个比jQuery选择器更高级的选择器,也就是类似div #defaultID.animationClass {
的选择器,而不仅仅是#defaultID.animationClass {
。由于#defaultID
和#defaultID.animationClass
都是一个级别,因此本例需要!important
--另一个编辑--
有关此主题的更多信息,请查看我关于CSS技巧的文章
参加聚会永远不会太迟。
//Animation pause at specific percentage, Example: pauseAnimationAt("myDiv",33,10)
function pauseAnimationAt(l,p,d){
percentage = p/d*1000;
function pauseAnimation(){
setTimeout(function(){ document.getElementById(l).style="animation-play-state: paused"; }, percentage);//l = Element ID, p = percentage when you wanna stopt animation, d = Duration of animation (Yes, you need to know that)
}
document.getElementById(l).addEventListener("animationstart", pauseAnimation());
}