I have created basic animation of a circle using css @keyframes. I'm using javascript to trigger animation start/stop by click inside the circle.
The animation itself could be divided into 5 (looped) phases:
pause-expand-pause-shrink-pause (see @keyframes css section below)
The goal I want to achieve is, eventually, to be able to set animation duration and change the values of keyframes (say by having input fields for pause and expand/shrink durations - details doesn't really matter for the scope of this question). I have put together a JavaScript function, to perform this task, and have set it to onload
just to test how it works.
My HTML:
<!doctype html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<link rel="stylesheet" href="style.css">
<script src = "animation.js"></script>
</head>
<body onload=setAnimationDuration(1,1)>
<div id="circle" class='circle-paused' onclick=cssAnimation()></div>
</body>
</html>
My CSS:
#circle {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
.circle-paused {
width: 9%;
padding-top: 9%;
border-radius: 50%;
background-color: #800080;
margin: auto;
}
.circle-animated {
/* 2 sec pause 4 sec expand_shrink*/
width: 9%;
padding-top: 9%;
-webkit-animation-name: my-circle; /* Safari 4.0 - 8.0 */
-webkit-animation-duration: 12s; /* Safari 4.0 - 8.0 */
animation-name: my-circle;
animation-duration: 12s;
animation-iteration-count: infinite;
animation-timing-function: linear;
border-radius: 50%;
margin: auto;
}
@keyframes my-circle {
0% {background-color: #800080; width: 9%; padding-top: 9%;}
33.3% {background-color: #D8BFD8; width: 28%; padding-top: 28%;}
50% {background-color: #D8BFD8; width: 28%; padding-top: 28%;}
83.3% {background-color: #800080; width: 9%; padding-top: 9%;}
100% {background-color: #800080; width: 9%; padding-top: 9%;}
}
My JavaScript:
function cssAnimation() {
if (document.getElementById('circle').className == 'circle-paused') {
document.getElementById('circle').className = 'circle-animated'
} else {
document.getElementById('circle').className = 'circle-paused'
}
}
function findKeyframes(animation_name) {
// get list of current keyframe rules
var style_sheet = document.styleSheets;
for (var i = 0; i < style_sheet.length; ++i) {
for (var j = 0; j < style_sheet[i].cssRules.length; ++j) {
// type 7 correspond to CSSRule.KEYFRAMES_RULE, for more info see https://developer.mozilla.org/en-US/docs/Web/API/CSSRule
if (style_sheet[i].cssRules[j].type == 7 && style_sheet[i].cssRules[j].name == animation_name) {
return style_sheet[i].cssRules[j];
}
}
}
// keyframe rules were not found for given animation_name
return null;
}
function getPercentage(total, fraction) {
// Returns what percentage the fraction is from total
// The result is rounded to 1 decimal place
return Math.round(((100 / total) * fraction) * 10) / 10;
}
function setAnimationDuration(pause, expand_shrink) {
var total_animation_duration = (pause * 2) + (expand_shrink * 2)
var pause_percentage = getPercentage(total_animation_duration, pause)
var expand_shrink_percentage = getPercentage(total_animation_duration, expand_shrink)
var pause1 = pause_percentage + expand_shrink_percentage;
var shrink = pause1 + expand_shrink_percentage;
var frame_percentage_list = [0, expand_shrink_percentage, pause1, shrink, 100]
var key_frame_list = findKeyframes('my-circle')
var new_rule_list = []
var to_be_removed_key_list = []
//create array of new rules to be inserted
//collecting old keys of rules to be deleted
for(var i = 0; i < key_frame_list.cssRules.length; i++) {
var current_rule = key_frame_list.cssRules[i].cssText
to_be_removed_key_list.push(key_frame_list.cssRules[i].keyText)
new_rule_list.push(current_rule.replace(/[+-]?([0-9]*[.])?[0-9]+%/, frame_percentage_list[i] + '%'))
}
// delete old rules
for(var i = 0; i < to_be_removed_key_list.length; i++) {
key_frame_list.deleteRule(to_be_removed_key_list[i])
}
// populate new ruels
for(var i = 0; i < new_rule_list.length; i++) {
key_frame_list.appendRule(new_rule_list[i])
}
document.getElementById('circle').style.animationDuration = total_animation_duration + "s"
}
Code above, on JSFiddle
The problem itself:
The code is working as expected in FireFox (55.0.3), Chrome (61.0) and Safari (11.0). Though, when I started testing it in IE11, I have found that key_frame_list.deleteRule('rule_key')
throws an Invalid argument
error. While researching the issue, I have found (and went trough) this article (though it does not tackle the IE problem, it improved my overall understanding of the css animations). On MSDN I have find two references, concerning deleteRule
: one and two. Though I didn't really understood what was meant, in the second one, by:
The key must resolve to a number between 0 and 1, or the rule is ignored.
I assumed that in IE you have to pass index to deleteRule
instead of string key. So I've tried to check my assumption in IE console. Here is what I have found (given my js code is in onload):
var key_frame_list = findKeyframes('my-circle')
key_frame_list.cssRules.length => 5
key_frame_list.deleteRule(0)
key_frame_list.cssRules.length => 4
key_frame_list.deleteRule(1)
key_frame_list.cssRules.length => 3
key_frame_list.deleteRule(0)
key_frame_list.deleteRule(1)
key_frame_list.deleteRule(2)
...
key_frame_list.cssRules.length => 3
What is happening is:
key_frame_list.deleteRule(0)
- removes first rule (which is 0%)
key_frame_list.deleteRule(1
) - removes the last rule (which is 100%)
After that no matter which index I pass to key_frame_list.deleteRule()
the key_frame_list.cssRules.length
remains 3.
My expectation was that I will be able to recur with key_frame_list.deleteRule(0)
and remove all the rules (as I expected that the indexes will shift after each rule deletion).
Now, I would like to understand:
- What is the proper way (basically, 'Am I doing something wrong?') to use
deleteRule
in the IE (or if another method should be used)? - Why am I not able to delete more than two rules out of five?
- Is there a method suitable for this purposes that will work with the same arguments on Firefox, Chrome and IE11, I am not aware of?