1k likes | 1.16k Views
Towards programmatic control of keyframes, example 1. View this animation in a -moz- or -webkit- browser: http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g9/diver.html It provides a button to access a DOM view of the stylesheet
E N D
Towards programmatic control of keyframes, example 1 • View this animation in a -moz- or -webkit- browser: http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g9/diver.html • It provides a button to access a DOM view of the stylesheet • But you will get a somewhat different result from the two types of browser
Understanding this output - some necessary DOM details • To understand the output from this program, we need some DOM details • The CSS2 DOM includes this interface definition CSSRule { // RuleType const unsigned short UNKNOWN_RULE = 0; const unsigned short STYLE_RULE = 1; const unsigned short CHARSET_RULE = 2; const unsigned short IMPORT_RULE = 3; const unsigned short MEDIA_RULE = 4; const unsigned short FONT_FACE_RULE = 5; const unsigned short PAGE_RULE = 6; readonly attribute unsigned short type; attribute DOMString cssText; // raises DOMException on setting readonly attribute CSSStyleSheet parentStyleSheet; readonly attribute CSSRule parentRule; }; References: http://www.w3.org/TR/DOM-Level-2-Style/css#CSS-CSSRule http://dev.w3.org/csswg/cssom/#the-cssrulelist-sequence
Analysing the -mozilla- output • Rule types in the CSS2 DOM: UNKNOWN_RULE = 0; STYLE_RULE = 1; CHARSET_RULE = 2; IMPORT_RULE = 3; MEDIA_RULE = 4; FONT_FACE_RULE = 5; PAGE_RULE = 6; • The first three rules reported in the -mozilla- output are style rules and, appropriately, have type=1 • The last three rules are @keyframes rules and are reported to have type=7, a value which is not defined in the CSS2 DOM • We must look at the CSS3 DOM
Extract from the CSS3 DOM • The CSS3 Animations DOM http://www.w3.org/TR/css3-animations/#dom-interfaces- extends the CSSRule interface as follows: CSSRule { ... const unsigned short KEYFRAMES_RULE = 7; const unsigned short KEYFRAME_RULE = 8; ... }; • The distinction between KEYFRAMES_RULE and KEYFRAME-RULE is not clear in the W3C documentation • As we shall see soon, this has caused some confusion • But notice that the type=7 which was reported by -mozilla- for the @keyframes rules conforms to the CSS3 DOM
Analysing the -webkit- output • Rule types in the CSS2 DOM: UNKNOWN_RULE = 0; STYLE_RULE = 1; CHARSET_RULE = 2; IMPORT_RULE = 3; MEDIA_RULE = 4; FONT_FACE_RULE = 5; PAGE_RULE = 6; • The first three rules reported in the output are style rules and, as expected, have type=1 • However, the three @keyframes rules are reported to have type=8, which is different from the type=7 that is defined in the CSS3 DOM for such rules • So, we will have to implement a cross-browser approach if we intend to use rule types in our Javascript
A note on the -webkit- implementation • In November 2011, a Gecko (Mozilla) implementor wrote: interface CSSRule { ... const unsigned short KEYFRAMES_RULE = 7; const unsigned short KEYFRAME_RULE = 8; }; However, WebKit implements: const unsigned short WEBKIT_KEYFRAMES_RULE = 8; const unsigned short WEBKIT_KEYFRAME_RULE = 9; We should figure out whether we're using 7 and 8 or using 8 and 9. (I implemented what the spec says in Gecko; I only discovered the difference recently.) Reference http://lists.w3.org/Archives/Public/www-style/2011Nov/0023.html
Analysing the program which reported the stylesheet • The HTML file differs little from that in previous programs • The only difference is that it has a button - to let the user demand a view of the stylesheet through the DOM <html> <head> <title>Diver created with Javascript</title> <link rel="stylesheet" href="diver.css"> <script src="diver.js" type="text/javascript"></script> </head> <body> <div style="height:100">Several divers will be created with Javascript</div> <div id="underwater"></div> <button type="button" id="button1"> See the DOM view of the stylesheet </button> </body> </html>
We don't need to examine the CSS page because it is the same as before #underwater { position: relative; height: 650px; width: 500px; border: solid 1px black; background: url('seascape.jpg') no-repeat top left; overflow : hidden; } #underwater > div { width: 100px; height: 100px; position: absolute; /* -webkit-animation-name : dive; */ -webkit-animation-duration : 7s; -webkit-animation-delay : 3s; -webkit-animation-iteration-count: infinite; /*-moz-animation-name : dive;*/ -moz-animation-duration : 7s; -moz-animation-delay : 3s; -moz-animation-iteration-count: infinite; } #underwater > div > img { width: 100px; height: 100px; } @-webkit-keyframes dive1 { 0% { -webkit-transform: rotate(90deg) translate(0px, 0px); } 100% { -webkit-transform: translate(0px, 750px); } } @-moz-keyframes dive1 { 0% { -moz-transform: rotate(90deg) translate(0px, -50px); } 100% { -moz-transform: translate(0px, 750px); } } @-webkit-keyframes dive2 { 0% { -webkit-transform: scale(-1,1) translate(0px, 0px); } 100% { -webkit-transform: scale(-1,1) translate(0px, 750px); } } @-moz-keyframes dive2 { 0% { -moz-transform: rotate(90deg) translate(0px, -50px); } 100% { -moz-transform: scale(-1,1) translate(0px, 750px); } } @-webkit-keyframes dive3 { 0% { -webkit-transform: translate(0px, 0px); } 100% { -webkit-transform: translate(0px, 750px); } } @-moz-keyframes dive3 { 0% { -moz-transform: translate(0px, -50px); } 100% { -moz-transform: translate(0px, 750px); } } The CSS file is unchanged from the previous program
Before we look at the Javascript - some more necessary DOM details • CSS2 introduced an interface, called DocumentStyle, by which the stylesheets embedded in a document could be retrieved • The “expectation [was] that an instance of the DocumentStyle interface can be obtained by using binding-specific casting methods on an instance of the Document interface” • Reference: http://www.w3.org/TR/DOM-Level-2-Style/stylesheets#StyleSheets-extensions • Interface definitions DocumentStyle{ readonly attributeStyleSheetListstyleSheets; }; StyleSheetList { readonly attribute unsigned longlength; StyleSheetitem( in unsigned long index); }; StyleSheet{ readonly attribute DOMStringtype; attribute booleandisabled; readonly attribute NodeownerNode; readonly attribute StyleSheetparentStyleSheet; readonly attribute DOMStringhref; readonly attribute DOMStringtitle; readonly attribute MediaListmedia; }; • Stylesheet does not contain CSS information, but it has a specialization called CSSStyleSheet – see next slide
Some necessary DOM details (contd.) • DOM Level 2 introduced the CSSStyleSheet interface as a specialization of the StyleSheet interface • Interface definitions CSSStyleSheet : stylesheets::StyleSheet { readonly attribute CSSRuleownerRule; readonly attribute CSSRuleListcssRules; unsigned long insertRule(in DOMString rule, in unsigned long index)raises(DOMException); void deleteRule(in unsigned long index)raises(DOMException); }; CSSRuleList { readonly attribute unsigned long length; CSSRuleitem(in unsigned long index) ; }; • We have already seen the CSSRule interface • Now, we know enough to analyse the Javascript file
The diver.js file (part 1) • The init() function attaches an event handler to the button const NUMBER_OF_DIVERS = 3; function init() { var envelope = document.getElementById('underwater'); var someDiver; for (var i = 0; i < NUMBER_OF_DIVERS; i++) { someDiver=newDiver(); envelope.appendChild(someDiver); } var button = document.getElementById('button1'); button.addEventListener('click',cssReport,false); } function randomInteger(lower,upper) { return lower + Math.round(Math.random() * (upper-lower)); } function newDiver() { var diverDiv = document.createElement('div'); var image = document.createElement('img'); image.src = 'diver'+randomInteger(1,3)+'.png'; diverDiv.appendChild(image); diverDiv.style.top = "-100px"; diverDiv.style.left = randomInteger(100,400)+'px'; diverDiv.style.webkitAnimationName = 'dive'+randomInteger(1,3); diverDiv.style.MozAnimationName = 'dive'+randomInteger(1,3); /*Note the Moz*/ return diverDiv; }
The diver.js file (part 2) function cssReport() { var sheet=document.styleSheets[0]; message='\n '; for (var j =0; j < sheet.cssRules.length; j++) {var rule = sheet. cssRules[j]; message=message+'\n\n'+ 'rule-type is '+rule.type+'\n'+ 'css-text is \n'+rule.cssText; } alert(message); } window.addEventListener('load', init, false);
Manipulating the @keyframes rules in a stylesheet • We have just seen how to access a stylesheet through the DOM • Now let's see how to manipulate the @keyframes rules in a stylesheet • The CSS3 Animations DOM defined a CSSKeyframesRule Interface • It represents a complete set of keyframes for a single animation Reference: http://www.w3.org/TR/css3-animations/ • It is defined as a specialization of CSSRule, as follows: CSSKeyframesRule : CSSRule { attribute DOMString name; readonly attribute CSSRuleListcssRules; void insertRule(in DOMString rule); void deleteRule(in DOMString key); CSSKeyframeRulefindRule(in DOMString key); }; • The name attribute contains the name of the animation • The cssRules attribute contains the list of individual keyframes for the animation in the form of a CSSRuleList, whose interface definition we have already seen
Manipulating @keyframes rules (contd.) • As we have just seen, the CSSKeyframesRule interface provides three methods: void insertRule(in DOMString rule); void deleteRule(in DOMString key); CSSKeyframeRulefindRule(in DOMString key); • The findRule() method does not modify an animation • It takes a "when" key and returns the corresponding individual keyframe in the form of a CSSKeyframeRule • the CSSKeyframeRule interface is defined on the next slide • The insertRule() and deleteRule() methods can be used to modify an animation
Manipulating @keyframes rules (contd.) • A CSSKeyframeRulerepresents an individual keyframe, • that is a mapping from a "when" key to the corresponding style specification • The interface is defined as a specialization of CSSRule, as follows: CSSKeyframeRule : CSSRule { attribute DOMString keyText; readonly attribute CSSStyleDeclarationstyle; }; • Reference:http://www.w3.org/TR/css3-animations/
Towards programmatic control of keyframes, example 2 • View this animation in a -moz- or -webkit- browser: http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g10/diver.html • It provides a button to to list the names of the animations in the stylesheet
Analysing the program • The HTML file differs little from that in previous programs <html> <head> <title>Accessing list of animation names</title> <link rel="stylesheet" href="diver.css"> <script src="diver.js" type="text/javascript"></script> </head> <body> <div style="height:100">Several divers will be created with Javascript</div> <div id="underwater"></div> <button type="button" id="button1"> List the animation names </button> </body> </html>
We don't need to examine the CSS page because it is the same as before #underwater { position: relative; height: 650px; width: 500px; border: solid 1px black; background: url('seascape.jpg') no-repeat top left; overflow : hidden; } #underwater > div { width: 100px; height: 100px; position: absolute; /* -webkit-animation-name : dive; */ -webkit-animation-duration : 7s; -webkit-animation-delay : 3s; -webkit-animation-iteration-count: infinite; /*-moz-animation-name : dive;*/ -moz-animation-duration : 7s; -moz-animation-delay : 3s; -moz-animation-iteration-count: infinite; } #underwater > div > img { width: 100px; height: 100px; } @-webkit-keyframes dive1 { 0% { -webkit-transform: rotate(90deg) translate(0px, 0px); } 100% { -webkit-transform: translate(0px, 750px); } } @-moz-keyframes dive1 { 0% { -moz-transform: rotate(90deg) translate(0px, -50px); } 100% { -moz-transform: translate(0px, 750px); } } @-webkit-keyframes dive2 { 0% { -webkit-transform: scale(-1,1) translate(0px, 0px); } 100% { -webkit-transform: scale(-1,1) translate(0px, 750px); } } @-moz-keyframes dive2 { 0% { -moz-transform: rotate(90deg) translate(0px, -50px); } 100% { -moz-transform: scale(-1,1) translate(0px, 750px); } } @-webkit-keyframes dive3 { 0% { -webkit-transform: translate(0px, 0px); } 100% { -webkit-transform: translate(0px, 750px); } } @-moz-keyframes dive3 { 0% { -moz-transform: translate(0px, -50px); } 100% { -moz-transform: translate(0px, 750px); } } The CSS file is unchanged from the previous program
The diver.js file (part 1) • The Javascript is only slightly different from before const NUMBER_OF_DIVERS = 3; function init() { var envelope = document.getElementById('underwater'); var someDiver; for (var i = 0; i < NUMBER_OF_DIVERS; i++) { someDiver=newDiver(); envelope.appendChild(someDiver); } var button = document.getElementById('button1'); button.addEventListener('click',listAnimationNames,false); } function randomInteger(lower,upper) { return lower + Math.round(Math.random() * (upper-lower)); } function newDiver() { var diverDiv = document.createElement('div'); var image = document.createElement('img'); image.src = 'diver'+randomInteger(1,3)+'.png'; diverDiv.appendChild(image); diverDiv.style.top = "-100px"; diverDiv.style.left = randomInteger(100,400)+'px'; diverDiv.style.webkitAnimationName = 'dive'+randomInteger(1,3); diverDiv.style.MozAnimationName = 'dive'+randomInteger(1,3); /*Note the Moz*/ return diverDiv; }
The diver.js file (part 2) function listAnimationNames() { var sheet=document.styleSheets[0]; var message='The animation names are\n\n'; for (var j=0; j< sheet.cssRules.length; j++) { var rule = sheet.cssRules[j]; if ( (rule.type==7 || rule.type==8) ) { message=message+rule.name+'\n'; } } alert(message); } window.addEventListener('load', init, false);
Programmatic control of keyframes, example 3 • View this animation in a -moz- or -webkit- or -o- or -ms- browser: http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g11/diver.html • Depending on the browser's capability, it may generate random animations by randomly choosing different maximum depths for each diver
Analysing the program • The HTML file differs little from that in previous programs <html> <head> <title>Creating random animations</title> <link rel="stylesheet" href="diver.css"> <script src="diver.js" type="text/javascript"></script> </head> <body> <div style="height:100">Several divers will be created with Javascript</div> <div id="underwater"></div> </body> </html>
The CSS file contains no @keyframe rules but, to prepare for a wider ranger of browsers, it does support more vendor prefixes than before #underwater { position: relative; height: 650px; width: 500px; border: solid 1px black; background: url('seascape.jpg') no-repeat top left; overflow: hidden; } #underwater > div { width: 100px; height: 100px; position: absolute; -webkit-animation-delay : 3s; -webkit-animation-duration : 7s; -webkit-animation-direction : alternate; -webkit-animation-iteration-count: infinite; -moz-animation-delay : 3s; -moz-animation-duration : 7s; -moz-animation-direction : alternate; -moz-animation-iteration-count: infinite; -o-animation-delay : 3s; -o-animation-duration : 7s; -o-animation-direction : alternate; -o-animation-iteration-count: infinite; -ms-animation-delay : 3s; -ms-animation-duration : 7s; -ms-animation-direction : alternate; -ms-animation-iteration-count: infinite; animation-delay : 3s; animation-duration : 7s; animation-direction : alternate; animation-iteration-count: infinite; } #underwater > div > img { width: 100px; height: 100px; } The CSS file contains no @keyframe rules
The diver.js file - overview • The Javascript file is extended to cope with the Microsoft event model and to include a function for generating new (vendor-prefixed) @keyframes rules function init() { ... } function newDiver(counter) { ... } function randomInteger(lower,upper) { ... } function nameOfNewAnimation(counter) { ... } function newAtKeyframesRule(animationName,bottomOfDive) { ... } function detectedPrefix() { ... } if (window.addEventListener) { window.addEventListener('load', init, false); } else if (window.attachEvent) /*Microsoft event model*/ { window.attachEvent('onload', init); }
The diver.js file (contd.) - the init() function • The main difference from previous programs is that, when the init() function calls the newDiver() function to create a new diver, it passes the counter as a parameter • As we shall see later, this is to enable the programmatically-generated animations to have distinct names function init() { NUMBER_OF_DIVERS = 3; var envelope = document.getElementById('underwater'); var someDiver; for (var i = 0; i < NUMBER_OF_DIVERS; i++) { someDiver = newDiver(i); envelope.appendChild(someDiver); } }
The diver.js file (contd.) - the newDiver() function • The main difference from previous programs is that newDiver() uses a function, called nameOfNewAnimation(), to generate a customized new animation for each diver and return the name of the new animation, • the name is derived from the counter, passed as a parameter • Also, since the counter is available, it is used to try to ensure that the diver images are as distinct as possible function newDiver(counter) { var diverDiv = document.createElement('div'); var image = document.createElement('img'); imageNumber=(counter % 3)+1;/*Number of distinct diver images is 3*/ image.src = 'diver'+imageNumber+'.png'; diverDiv.appendChild(image); diverDiv.style.top = "-100px"; diverDiv.style.left = randomInteger(100,400)+'px'; var someAnimationName=nameOfNewAnimation(counter); diverDiv.style.webkitAnimationName=someAnimationName; diverDiv.style.MozAnimationName=someAnimationName; diverDiv.style.msAnimationName=someAnimationName; diverDiv.style.oAnimationName=someAnimationName; diverDiv.style.animationName=animationName; return diverDiv; }
The diver.js file (contd.) - the nameOfNewAnimation()function • counter is used to get the new name animation name • All animations have the same pattern, differing only in the maximum depth to which the diver descends • this is a random integer in the range 200 to 750 • newAtKeyframesRule() is called to construct the @keyframes rule for the new animation • if the browser does not support @keyframes rules, "undefined" is returned • if a @keyframes rule is successfully built, insertRule() is called to insert it at the top of the stylesheet function nameOfNewAnimation(counter) { var animationName="randomDive"+counter; var bottomOfDive=randomInteger(200,750); rule=newAtKeyframesRule(animationName,bottomOfDive); if (rule != "undefined") { var styleSheet=document.styleSheets[0]; styleSheet.insertRule(rule,0); } return animationName; }
The diver.js file (contd.) - the newAtKeyframesRule()function • detectedPrefix() is called to check if the browser supports @keyframes rules and, if so, to get the correct vendor prefix • detectedPrefix() reports 'unknown' if the browser does not support @keyframes rules • if detectedPrefix() reports the empty string as a prefix, an unprefixed rule is built function newAtKeyframesRule(animationName,bottomOfDive) { var prefix=detectedPrefix(); if (prefix=='') { rule="@keyframes "+animationName+ " { 0% { transform: translate(0px, 0px); } "+ " 100% { transform: translate(0px, "+bottomOfDive+"px); } }"; } else if (prefix != 'unknown') {rule="@-"+prefix+"-keyframes "+animationName+" { 0% { -"+prefix+ "-transform: translate(0px, 0px); } "+ " 100% { -"+prefix+ "-transform: translate(0px, " +bottomOfDive+"px); } }"; } else rule="undefined"; return rule; }
The diver.js file (contd.) - the detectedPrefix()function • To determine the correct prefix to use we check for the presence or absence of a (possibly not vendor-prefixed) feature called requestAnimationFrame (which we will use later, in canvas animations) • if an un-prefixed version of the feature is available, no prefix is needed for the @keyframes rule • if the feature is not available, it is probable that the browser does not support CSS animations and 'unknown' is returned as the prefix, in order to prevent Javascript aborting because of an attempt to insert a @keyframes rule function detectedPrefix() { if (window.webkitRequestAnimationFrame) { return 'webkit'; } else if (window.mozRequestAnimationFrame) { return 'moz'; } else if (window.oRequestAnimationFrame) { return 'o'; } else if (window.msRequestAnimationFrame) { return 'ms'; } else if (window.requestAnimationFrame) { return ''; } else { alert('Your browser does not (fully) support animation'); return 'unknown'; } }
Programmatic control of keyframes, example 4 • View this animation in a -moz- or -webkit- or -o- or -ms- browser: http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g12/diver.html • If the browser fully supports animation, • it will generate random animations by randomly choosing different maximum depths for each diver • in addition, it will insert a random keyframe into each @keyframes rule (This random insertion of a keyframe is not needed but is shown to illustrate how the technology can be used) • the browser reports the stylesheet to show what the @keyframes rules look like after an additional keyframe has been inserted into each of them
Analysing the program • The HTML file differs little from that in previous programs <html> <head> <title>Creating random animations</title> <link rel="stylesheet" href="diver.css"> <script src="diver.js" type="text/javascript"></script> </head> <body> <div style="height:100">Several divers will be created with Javascript</div> <div id="underwater"></div> </body> </html>
To show the effect of the randomized horizontal movement, we provide a left margin and allow overflow material to be seen #underwater { position: relative; height: 650px; width: 500px; margin-left:300;border: solid 1px black; background: url('seascape.jpg') no-repeat top left; /* overflow: hidden; */ } #underwater > div { width: 100px; height: 100px; position: absolute; -webkit-animation-delay : 3s; -webkit-animation-duration : 7s; -webkit-animation-direction : alternate; -webkit-animation-iteration-count: infinite; -moz-animation-delay : 3s; -moz-animation-duration : 7s; -moz-animation-direction : alternate; -moz-animation-iteration-count: infinite; -o-animation-delay : 3s; -o-animation-duration : 7s; -o-animation-direction : alternate; -o-animation-iteration-count: infinite; -ms-animation-delay : 3s; -ms-animation-duration : 7s; -ms-animation-direction : alternate; -ms-animation-iteration-count: infinite; animation-delay : 3s; animation-duration : 7s; animation-direction : alternate; animation-iteration-count: infinite; } #underwater > div > img { width: 100px; height: 100px; } The CSS file is modified, to illustrate the effects of randomization
The diver.js file - overview • The architecture of the Javascript file is almost unchanged • there are some new functions to introduce a random keyframe into a @keyframes rule • there is a function to report the state of the CSS stylesheet function init() { ... } function newDiver(counter) { ... } function randomInteger(lower,upper) { ... } function nameOfNewAnimation(counter) { ... } function newAtKeyframesRule(animationName,bottomOfDive) { ... } function detectedPrefix() { ... } function introduceRandomKeyframe(someAnimationName) { ... } function newKeyframe(someAnimationName) { ... } function cssReport() { ... } if (window.addEventListener) { window.addEventListener('load', init, false); } else if (window.attachEvent) /*Microsoft event model*/ { window.attachEvent('onload', init); }
The diver.js file (contd.) - the init() function • The only difference from previous programs is that, when the init() function has finished its work, it reports the state of the modified CSS stylesheet function init() { NUMBER_OF_DIVERS = 3; var envelope = document.getElementById('underwater'); var someDiver; for (var i = 0; i < NUMBER_OF_DIVERS; i++) { someDiver = newDiver(i); envelope.appendChild(someDiver); } cssReport(); }
The diver.js file (contd.) - the newDiver() function • The only difference from the previous program is that, after nameOfNewAnimation() has generated the animation for a new diver, it then inserts a random new keyframe into the @keyframes rule for the animation that has just been created • this is done only to illustrate the technology • nameOfNewAnimation() could easily have included the additional keyframe when building the @keyframes rule function newDiver(counter) { var diverDiv = document.createElement('div'); var image = document.createElement('img'); imageNumber=(counter % 3)+1; /*Number of distinct diver images is 3*/ image.src = 'diver'+imageNumber+'.png'; diverDiv.appendChild(image); diverDiv.style.top = "-100px"; diverDiv.style.left = randomInteger(100,400)+'px'; var someAnimationName=nameOfNewAnimation(counter); introduceRandomKeyframe(someAnimationName); diverDiv.style.webkitAnimationName=someAnimationName; diverDiv.style.MozAnimationName=someAnimationName; diverDiv.style.msAnimationName=someAnimationName; diverDiv.style.oAnimationName=someAnimationName; diverDiv.style.animationName=animationName; return diverDiv; }
The diver.js file (contd.) - the nameOfNewAnimation()function • This function is unchanged from the previous program function nameOfNewAnimation(counter) { var animationName="randomDive"+counter; var bottomOfDive=randomInteger(200,750); rule=newAtKeyframesRule(animationName,bottomOfDive); if (rule != "undefined") { var styleSheet=document.styleSheets[0]; styleSheet.insertRule(rule,0); } return animationName; }
The diver.js file (contd.) - the newAtKeyframesRule()function • This function is unchanged from the previous program function newAtKeyframesRule(animationName,bottomOfDive) { var prefix=detectedPrefix(); if (prefix=='') { rule="@keyframes "+animationName+ " { 0% { transform: translate(0px, 0px); } "+ " 100% { transform: translate(0px, "+bottomOfDive+"px); } }"; } else if (prefix != 'unknown') {rule="@-"+prefix+"-keyframes "+animationName+" { 0% { -"+prefix+ "-transform: translate(0px, 0px); } "+ " 100% { -"+prefix+ "-transform: translate(0px, " +bottomOfDive+"px); } }"; } else rule="undefined"; return rule; }
The diver.js file (contd.) - the detectedPrefix()function • This function is unchanged from the previous program function detectedPrefix() { if (window.webkitRequestAnimationFrame) { return 'webkit'; } else if (window.mozRequestAnimationFrame) { return 'moz'; } else if (window.oRequestAnimationFrame) { return 'o'; } else if (window.msRequestAnimationFrame) { return 'ms'; } else if (window.requestAnimationFrame) { return ''; } else { alert('Your browser does not (fully) support animation'); return 'unknown'; } }
The diver.js file (contd.) - the introduceRandomKeyframe()function • We search through the stylesheet for the right @keyframes rule • Then we insert generate a random x value for the middle of the dive • And insert a keyframe for this into the @keyframes rule function introduceRandomKeyframe(someAnimationName) { var styleSheet=document.styleSheets[0]; var foundRule=false; var counter=0; while (!foundRule && counter < styleSheet.cssRules.length) { var rule = styleSheet.cssRules[counter]; if ( ( (rule.type==7 || rule.type==8) ) && rule.name==someAnimationName ) { foundRule=true; var middleOfDive=randomInteger(-500,350); var keyframe=newKeyframe(middleOfDive); if (keyframe != "undefined") { rule.insertRule(keyframe,0); } } else { counter = counter+1; } } }
The diver.js file (contd.) - the newKeyframe()function • We check whether the browser supports keyframes and, if so, whether a prefix is needed • If keyframes are not supported, we return "undefined" as the keyframe text • Otherwise, we return (possibly prefixed) keyframe text using middleOfDive as the X coordinate for at the 50% time point function newKeyframe(middleOfDive) { var prefix=detectedPrefix(); if (prefix=='') { frame="50% { transform: translate("+middleOfDive+"px,300px); } "; } else if (prefix != 'unknown') { frame="50% { -"+prefix+ "-transform: translate("+middleOfDive+"px,300px); } "; } else frame="undefined"; return frame; }
The diver.js file (contd.) - the cssReport()function • We have seen this function in a previous program, so it will not be considered in any detail here function cssReport() { var sheet= document.styleSheets[0]; message='\n '; for (var j =0; j < sheet.cssRules.length; j++) {var rule = sheet. cssRules[j]; message=message+'\n\n'+ 'rule-type is '+rule.type+'\n'+ 'css-text is \n'+rule.cssText; } alert(message); }
Note on animated property values • The value of a style property for an element is affected by an animation only in the interval between these two points in time • the expiry of the delay (if any) after the animation was applied to the element • the end of the animation-duration after the expiry of the delay (if any) Reference: http://www.w3.org/TR/css3-animations/
An attempt at an interactive animation • Try to use this animation in a -moz- browser http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g13/diver.html • The aim was to create an interactive animation in which the user could control whether the diver is descending or moving rightwards. • To make the diver move in a new direction, we must pause the animation, specify the new direction and then resume the animation • We could build the @keyframes rule to animate the new direction if we knew where the paused diver was • However, at present (February 2012) it appears that no mechanism is available for querying the current value of an animated property • So, since we cannot determine the diver's current position when he is suspended in mid-animation, he will start from the default position when we resume the animation
Animation events • Try to use this animation in a -webkit- browser http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g14/diver.html • As it starts the animation for each diver, it outputs an alert message, naming the animation it is starting and the diver to which it is applying this animation
Analysing the program • The HTML file differs little from that in previous programs <html> <head> <title>Diver created with Javascript</title> <link rel="stylesheet" href="diver.css"> <script src="diver.js" type="text/javascript"></script> </head> <body> <div style="height:100">Several divers will be created with Javascript</div> <div id="underwater"></div> </body> </html>
At present, CSS animation events are supported only in -webkit- browsers #underwater { position: relative; height: 650px; width: 500px; overflow:hidden; border: solid 1px black;background: url('seascape.jpg') no-repeat top left; } #underwater > div { width: 100px; height: 100px; position: absolute; -webkit-animation-duration : 7s; -webkit-animation-delay : 3s; -webkit-animation-iteration-count: infinite; } #underwater > div > img { width: 100px; height: 100px; } @-webkit-keyframes dive1 { 0% { -webkit-transform: rotate(90deg) translate(0px, 0px); } 100% { -webkit-transform: translate(0px, 750px); } } @-webkit-keyframes dive2 { 0% { -webkit-transform: scale(-1,1) rotate(-45deg) translate(0px, 0px); } 100% { -webkit-transform: scale(-1,1) translate(0px, 750px); } } @-webkit-keyframes dive3 { 0% { -webkit-transform: translate(0px, 0px); } 100% { -webkit-transform: translate(0px, 750px); } } Just webkit keyframes in the CSS
The diver.js file • We add an event listener to each diverDiv, listening for animationStart events const NUMBER_OF_DIVERS = 3; function init() { var envelope = document.getElementById('underwater'); var someDiver; for (var i = 0; i < NUMBER_OF_DIVERS; i++) { someDiver=newDiver(i); envelope.appendChild(someDiver); } } function randomInteger(lower,upper) { return lower + Math.round(Math.random() * (upper-lower)); } function newDiver(counter) {var diverDiv = document.createElement('div'); diverDiv.setAttribute('id','diver'+counter); var image = document.createElement('img'); imageNumber=(counter % 3)+1; /*Number of diver images is 3*/ image.src = 'diver'+imageNumber+'.png'; diverDiv.appendChild(image); diverDiv.style.top = "-100px"; diverDiv.style.left = randomInteger(100,400)+'px'; diverDiv.style.webkitAnimationName = 'dive'+randomInteger(1,3); diverDiv.addEventListener('webkitAnimationStart', reportStart, false ); return diverDiv; } function reportStart(...) { ... } window.addEventListener('load', init, false);