Reputation: 6025
While looking at this question Set Webkit Keyframes Values Using Javascript Variable The accepted answer is a Webkit specific way to change key-frame CSS dynamically i decided to see if i could change it around so that it would work in Firefox.
I managed to get this working JSFiddle which works as it should except for the fact that sometimes the animation wont start. About 1/4 of clicks will start the animation but the other 3/4 will cause nothing to happen.
Can someone explain to me what exactly is happening? have i missed something simple or is there a race condition somewhere that i am not accounting for?
HTML
<div id="box"></div>
<div id="update-box">Run Another Animation</div>
CSS
@-webkit-keyframes rotate {
0% {-webkit-transform: rotate(-10deg);}
100% {-webkit-transform: rotate(10deg);}
}
@keyframes rotate {
0% {-moz-transform: rotate(-10deg);}
100% {-moz-transform: rotate(-10deg);}
}
body { text-align: center; }
#box {
height: 100px;
width: 100px;
background-color: blue;
-webkit-animation-duration: 1s;
-webkit-animation-timing-function: linear;
-webkit-animation-name: "rotate";
animation-duration: 1s;
animation-timing-function: linear;
animation-name: "rotate";
margin: 30px auto;
}
#update-box {
border: 3px solid black;
background: #efefef;
font-weight: bold;
cursor: pointer;
padding: 10px;
margin: 30px auto;
display: inline-block;
}
#update-box:hover {
background: black;
color: white;
}
JS
// search the CSSOM for a specific -webkit-keyframe rule
function findKeyframesRule(rule) {
// gather all stylesheets into an array
var ss = document.styleSheets;
// loop through the stylesheets
for (var i = 0; i < ss.length; ++i) {
// loop through all the rules
for (var j = 0; j < ss[i].cssRules.length; ++j) {
// find the -webkit-keyframe rule whose name matches our passed over parameter and return that rule
if ((ss[i].cssRules[j].type == window.CSSRule.KEYFRAMES_RULE) && ss[i].cssRules[j].name == rule) return [ss[i], ss[i].cssRules[j]];
}
}
// rule not found
return null;
}
// remove old keyframes and add new ones
function change(anim) {
// find our -webkit-keyframe rule
var results = findKeyframesRule(anim);
var style_sheet = results[0];
var rule = results[1];
console.log(rule)
// remove the existing 0% and 100% rules
rule.deleteRule("0%");
rule.deleteRule("100%");
rule.appendRule("0% { -moz-transform: rotate(0deg);}")
rule.appendRule("100% { -moz-transform: rotate(" + randomFromTo(-360, 360) + "deg); }")
// create new 0% and 100% rules with random numbers
// assign the animation to our element (which will cause the animation to run)
document.getElementById('box').style.animationName = anim;
}
// begin the new animation process
function startChange() {
// remove the old animation from our object
document.getElementById('box').style.animationName = "none";
// call the change method, which will update the keyframe animation
setTimeout(function () {
change("rotate");
}, 0);
}
// get a random number integer between two low/high extremes
function randomFromTo(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
$(function () {
$('#update-box').bind('click', click_handler);
});
function click_handler(e) {
e.preventDefault();
startChange();
}
Upvotes: 3
Views: 2835
Reputation: 6025
Turns out it was a refresh issue
// search the CSSOM for a specific -webkit-keyframe rule
function findKeyframesRule(rule) {
// gather all stylesheets into an array
var ss = document.styleSheets;
// loop through the stylesheets
for (var i = 0; i < ss.length; ++i) {
// loop through all the rules
for (var j = 0; j < ss[i].cssRules.length; ++j) {
// find the -webkit-keyframe rule whose name matches our passed over parameter and return that rule
if ((ss[i].cssRules[j].type == window.CSSRule.KEYFRAMES_RULE) && ss[i].cssRules[j].name == rule) return [ss[i], ss[i].cssRules[j]];
}
}
// rule not found
return null;
}
// remove old keyframes and add new ones
function change(anim) {
// find our -webkit-keyframe rule
var results = findKeyframesRule(anim);
var style_sheet = results[0];
var rule = results[1];
console.log(rule)
// remove the existing 0% and 100% rules
rule.deleteRule("0%");
rule.deleteRule("100%");
rule.appendRule("0% { transform: rotate(0deg);}")
rule.appendRule("100% { transform: rotate(" + randomFromTo(-360, 360) + "deg); }")
// create new 0% and 100% rules with random numbers
var box = document.getElementById('box');
// assign the animation to our element (which will cause the animation to run)
box.style.animationName = 'none';
box.offsetWidth = box.offsetWidth;
box.style.animationName = anim;
}
// begin the new animation process
function startChange() {
change("rotate");
}
// get a random number integer between two low/high extremes
function randomFromTo(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
$(function () {
$('#update-box').bind('click', click_handler);
});
function click_handler(e) {
e.preventDefault();
startChange();
}
Is the updated working code.
http://jsfiddle.net/RHhBz/675/
The following being the important change
box.style.animationName = 'none';
box.offsetWidth = box.offsetWidth; //This line here is needed to force refresh
box.style.animationName = anim;
Upvotes: 2