towards programmatic control of keyframes example 1
Download
Skip this Video
Download Presentation
Towards programmatic control of keyframes, example 1

Loading in 2 Seconds...

play fullscreen
1 / 100

Towards programmatic control of keyframes, example 1 - PowerPoint PPT Presentation


  • 91 Views
  • Uploaded on

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

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' Towards programmatic control of keyframes, example 1' - mikasi


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
towards programmatic control of keyframes example 1
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
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
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
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
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
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
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>

the css file is unchanged from the previous program
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
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
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 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
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
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
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 contd1
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
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
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>

the css file is unchanged from the previous program1
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 11
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 21
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
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 program1
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
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 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 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 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
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
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
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
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 program2
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 is modified to illustrate the effects of randomization
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 overview1
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 function1
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 function1
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 function1
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 function1
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 function1
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
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
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
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
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
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
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 program3
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>

just webkit keyframes in the css
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
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);

the reportstart function
The reportStart() function
  • The reportStart() function uses several attributes of the event object
  • As we shall see on the next few slides, these attributes were introduced in DOM2 and DOM3

function reportStart(e)

{ alert( \'Just starting an instance of animation \'+

e.animationName+

\' on \'+

e.target.getAttribute("id")

);

}

css animation events
CSS Animation events
  • CSS3 defined the AnimationEvent as a specialization of the Event class that had been defined in DOM2

AnimationEvent : Event

{ readonly attribute DOMString animationName;

readonly attribute float elapsedTime;

void initAnimationEvent( in DOMString typeArg, in boolean canBubbleArg,

in boolean cancelableArg; in DOMString

animationNameArg, in float elapsedTimeArg ); };

  • There are three types of animation event
    • animationstart - occurs at the start of an animation
    • animationend - occurs when the animation finishes
    • animationiteration -occurs at the end of each iteration of an animation for which animation-iteration-count is greater than one; does not occur for animations with an iteration count of one
  • elapsedTime - time, in seconds, between start of animation and this event, excluding any time animation was paused; not affect by animation-delay; is always zero for animationstart events
  • We will consider the initAnimationEvent()method later
  • Reference:http://www.w3.org/TR/css3-animations/
reminder dom2 event class
Reminder: DOM2 Event class
  • The Event class was defined in DOM2 as follows:

Event { // PhaseType

const unsigned short CAPTURING_PHASE = 1;

const unsigned short AT_TARGET = 2;

const unsigned short BUBBLING_PHASE = 3;

readonly attribute DOMString type;

readonly attribute EventTarget target; /*where event happened*/

readonly attribute EventTarget currentTarget; /*loc\'n of current handler*/

readonly attribute unsigned short eventPhase;

readonly attribute boolean bubbles;

readonly attribute boolean cancelable;

readonly attribute DOMTimeStamp timeStamp;

void stopPropagation();

void preventDefault();

void initEvent(in DOMString eventTypeArg,

in boolean canBubbleArg,

in boolean cancelableArg); };

  • Reference:http://www.w3.org/TR/DOM-Level-2-Events/events.html
using events message to user before after animation
Using events: message to user before/after animation
  • Try to use this animation in a -webkit- browser

http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g15/diver.html

  • Before the animation starts, the window contains a message asking the user to wait
  • This message disappears when the animation starts
  • A new message appears when the animation is finished
analysing the html file
Analysing the HTML file
  • Only novelty is presence of div containing message

<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:100px">

<div id="messageDiv" style="visibility:visible;color:red;font-size:30px">

Please wait for start of animation

</div>

</div>

<div id="underwater"></div>

</body>

</html>

analysing the css file
Only novelty is that there is a finite iteration count

#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: 2; }

#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); } }

Analysing the CSS file
the diver js file1
The diver.js file
  • Javascript architecture is mostly the same
  • But now, two animation event handlers are attached to each diver

const NUMBER_OF_DIVERS = 3;

function init() {... }

function randomInteger(lower,upper) { ... }

function processStart(e) { ... }

function processEnd(e) { ...}

function newDiver(counter)

{ var diverDiv = document.createElement(\'div\'); diverDiv.setAttribute(\'id\',\'diver\'+counter);

var image = document.createElement(\'img\'); imageNumber=(counter % 3)+1;

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);

diverDiv.addEventListener(\'webkitAnimationEnd\', reportEnd, false);

return diverDiv; }

window.addEventListener(\'load\', init, false);

the processstart and processend functions
The processStart() and processEnd() functions
  • processStart() just hides the message div
  • processEnd() changes its content before making it visible again

function processStart(e)

{ var waitDiv = document.getElementById(\'messageDiv\');

if (waitDiv.style.visibility=="visible")

{ waitDiv.style.visibility="hidden"; }

}

function processEnd(e)

{ var waitDiv = document.getElementById(\'messageDiv\');

if (waitDiv.style.visibility=="hidden")

{ elapsedTime = e.elapsedTime;

newMessage = "Here\'s hoping you enjoyed the animation! "+

"It lasted "+elapsedTime+" seconds";

waitDiv.innerHTML=newMessage;

waitDiv.style.visibility="visible"; }

}

transition events
Transition Events
  • View this page in a -webkit- or -moz- or -o- or -ms- browser

http://www.cs.ucc.ie/j.bowen/cs4506/slides/transitionEvent/transitionEvent.html

  • The images below show what happens, using Chrome as an example
  • The text in the red area starts to transition to a larger font-size
  • When the transition ends, the message on the top of the page is changed
analysing the html file1
Analysing the HTML file
  • We shall see later that
    • the div element is affected by a transition specified in the stylesheet
    • the Javascript specifies that when the transition ends, the content of the h1 element will be changed

<!DOCTYPE html>

<html>

<head>

<title>Transition events</title>

<link rel="stylesheet" href="transitionEvent.css">

<script src="transitionEvent.js" type="text/javascript"></script>

</head>

<body>

<h1 id="message" style="height:100px">

Hold your mouse over the red area below and see what happens

</h1>

<div id="helloDiv" class="greeting">Hello</div>

</body>

</html>

analysing the css file1
When the mouse hovers over the div element, 8 seconds will be spent transitioning the font-size from 10px to 50px

#helloDiv:hover {font-size:50px;

-webkit-transition-duration:8s;

-moz-transition-duration:8s;

-ms-transition-duration:8s;

-o-transition-duration:8s;

transition-duration:8s; }

#helloDiv {background-color:red; font-size:10px}

Analysing the CSS file
the diver js file2
The diver.js file
  • Javascript architecture is familiar
  • An event handler is used to watch for the end of the transition
    • Notice the unusual form of the Mozilla event name

function processEnd(e)

{var messageHeader = document.getElementById(\'message\');

newMessage="OK, you can remove your mouse now. "+

e.elapsedTime+" seconds was spent changing the "+e.propertyName;

messageHeader.innerHTML=newMessage; }

function init ()

{ var helloDiv=document.getElementById("helloDiv");

helloDiv.addEventListener( \'webkitTransitionEnd\', processEnd, false);

helloDiv.addEventListener( \'transitionend\', processEnd, false); /*Mozilla*/

helloDiv.addEventListener( \'oTransitionEnd\', processEnd, false);

helloDiv.addEventListener( \'MSTransitionEnd\', processEnd, false); }

window.addEventListener(\'load\', init, false);

css transition events
CSS Transition Events
  • The CSS3 DOM interace definition for transition events is as follows

TransitionEvent : Event { readonly attribute DOMString propertyName;

readonly attribute float elapsedTime;

void initTransitionEvent(in DOMString typeArg,

in boolean canBubbleArg,

in boolean cancelableArg,

in DOMString propertyNameArg,

in float elapsedTimeArg); };

  • The only type of transition event supported is the end of a transition
    • the start of a transition does not trigger an event
  • W3C reference: http://dev.w3.org/csswg/css3-transitions/
simulating events programmatically
Simulating events programmatically
  • The DOM provides features which allow us to write Javascript that simulates events, such as user interactions with a page, etc.
  • The initAnimationEvent() and initTransitionEvent() methods which we saw earlier are intended to allow us to simulate animation and simulation events
  • Before we try to use these methods, we should look at the basic approach to simulating events programmatically
simulating a mouse click on a check box
Simulating a mouse click on a check box
  • View this page in a modern browser

http://www.cs.ucc.ie/j.bowen/cs4506/slides/eventSimulation/example.html

  • When the user clicks on the button, the document responds as if he had clicked his mouse in the checkbox
  • This was achieved using features provided by the DOM to allow us to programmatically simulate events
analysing the html file2
Analysing the HTML file
  • We will use a click on button1 to trigger a simulation of a click on checkBox1

<html>

<head>

<title>Simulating an event</title>

<script src="example.js" type="text/javascript"></script>

</head>

<body>

<form style="border: solid red 2px; width:400px">

<input type="checkbox" id="checkBox1" name="whatever" value="1">

</form>

<button type="button" id="button1">

Simulate a mouse click in the check box above

</button>

</body>

</html>

the javascript file
The Javascript file
  • It is a three-stage process:
    • creation, initialization, dispatch
  • We create a new event of type MouseEvents
  • We initialize the new event with values appropriate for a mouse event
  • We use dispatchEvent to simulate the new event happening on the check box

function init()

{ var button1 = document.getElementById(\'button1\');

button1.addEventListener(\'click\', simulateMouseClickOnCheckBox, false); }

function simulateMouseClickOnCheckBox()

{ var checkBox1=document.getElementById(\'checkBox1\');

var newEvent = document.createEvent("MouseEvents");

newEvent.initMouseEvent("click", true, true,

window, 0, 0, 0, 0, 0,false, false, false, false, 0, null);

checkBox1.dispatchEvent(newEvent); }

window.addEventListener(\'load\', init, false);

simulating an animation end event first run
Simulating an animation end event, first run
  • We will view this page in a -webkit- browser twice

http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g16/diver.html

  • First time, we will not use the button provided on the page
  • We will wait until all animation stops and look at the output
  • We see that, at the end of each diver\'s animation, text is added to the top message area, saying that the animation lasted 21 seconds
simulating an animation end event second run
Simulating an animation end event, second run
  • Now, reload this page and view it a second time in a -webkit- browser

http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g16/diver.html

  • This time, click on the button just after the animation starts
  • Immediately, we get a (untruthful) message saying that diver1\'s animation is finished and lasted more than 10 minutes!
  • But all three divers continue to move until, a little later, we are told that each animation, including diver1\'s, lasted 21 seconds.
  • So, clicking the button simulated the end of diver1\'s animation but did not actually end it
analysing the html file3
Analysing the HTML file
  • Only novelty is button for invoking the simulation

<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:200px">

<div id="messageDiv" style="visibility:visible;color:red;font-size:30px">

Please wait for start of animation

</div>

</div>

<div id="underwater"></div>

<button type="button" id="button1">

Simulate an end to animation on diver1

</button>

</body>

</html>

analysing the css file2
For illustrative purposes, overflow is visible

Each animation runs for three iterations

As we shall see in the Javascript, each diver\'s animation is an instance of the animation called dive which is defined below

#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-duration : 7s; -webkit-animation-delay : 3s;

-webkit-animation-direction : alternate; -webkit-animation-iteration-count:3; }

#underwater > div > img { width: 100px; height: 100px; }

@-webkit-keyframes dive

{ 0% { -webkit-transform: rotate(90deg) translate(0px, 0px); }

100% { -webkit-transform: translate(0px, 750px); } }

Analysing the CSS file
the diver js file3
The diver.js file
  • Javascript architecture is mostly the same
  • But there is a new function, called simulateAnimationEnd(), which, as we shall see, is attached as an event listener to button1

const NUMBER_OF_DIVERS = 3;

function init() {... }

function randomInteger(lower,upper) { ... }

function processStart(e) { ... }

function processEnd(e) { ...}

function newDiver(counter) { ... }

function simulateAnimationEnd() { ... }

window.addEventListener(\'load\', init, false);

function init
function init()
  • Only novelty is that it attaches simulateAnimationEnd as an event listener to button1

function init()

{ var envelope = document.getElementById(\'underwater\');

var someDiver;

for (var i = 0; i < NUMBER_OF_DIVERS; i++)

{ someDiver=newDiver(i);

envelope.appendChild(someDiver); }

var button1 = document.getElementById(\'button1\');

button1.addEventListener(\'click\', simulateAnimationEnd, false);

}

the randominteger and processstart functions
The randomInteger() and processStart() functions
  • These are almost the same as before
  • Only difference is that processStart() does not hide the messageDiv
    • instead, it just makes the content equal to the empty string
    • this simplifies the processEnd() function which is on the next slide

function randomInteger(lower,upper)

{ return lower + Math.round(Math.random() * (upper-lower)); }

function processStart(e)

{ var messageDiv = document.getElementById(\'messageDiv\');

messageDiv.innerHTML=""

}

the processend function
The processEnd() function
  • This function reacts to an animationEnd event
  • Whether the event actually happens or is simulated,
    • the identity of the diver involved in the event object is found and
    • information about the length of the animation involved in the event object is added to the message div

function processEnd(e)

{ /*Find the diver whose animationEnd just happened or is being simulated*/

diverID=e.target.getAttribute("id");

/*Add appropriate output to the message div*/

var messageDiv = document.getElementById(\'messageDiv\');

oldMessage=messageDiv.innerHTML;

newMessage="The animation on "+diverID+

" lasted "+e.elapsedTime+" seconds.";

messageDiv.innerHTML=oldMessage+newMessage; }

the newdiver function
The newDiver() function
  • Same as before, except that although the divers are given different starting points, they are all given the same animation

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\';

diverDiv.addEventListener(\'webkitAnimationStart\', processStart,false );

diverDiv.addEventListener(\'webkitAnimationEnd\', processEnd, false );

return diverDiv; }

and now for the beef in the sandwich
And, now for the beef in the sandwich ...
  • ..., that is, the simulateAnimationEnd() function
  • It is on the next slide
the simulateanimationend function
The simulateAnimationEnd() function
  • Same architecture as function for simulating a mouse click event
    • createEvent, initEvent, dispatchEvent
  • Note the use of the vendor-specific prefix in three places
  • Note, also, the use of an element called anyObject
    • the next slide provides a note about this

function simulateAnimationEnd()

{ var diver1=document.getElementById(\'diver1\');

var newEvent = document.createEvent("WebKitAnimationEvent");

var anyObject = document.createElement();

newEvent.initWebKitAnimationEvent("webkitAnimationEnd",

true, true,anyObject,600.5);

diver1.dispatchEvent(newEvent);

}

note on the initwebkitanimationevent method part 1
Note on the initWebKitAnimationEvent()method, part 1
  • CSS3 defined the initAnimationEvent() method as follows

void initAnimationEvent( in DOMString typeArg,

in boolean canBubbleArg,

in boolean cancelableArg;

in DOMString animationNameArg,

in float elapsedTimeArg ); };

Reference: http://www.w3.org/TR/css3-animations/

  • The fourth argument is supposed to be a string and, apparently, should be the name of an animation
  • Thus, in the simulateAnimationEnd() function, I expected that the fourth argument to initWebKitAnimationEvent()should be "dive"
  • However, when I tried that, it did not work
  • Then, I found that this web-page used the window object in this position

http://cubiq.org/spinning-wheel-on-webkit-for-iphone-ipod-touch

  • After experimentation, I discovered that the window object is not necessary
    • any object is good enough
    • so, to emphasise this, I just create a new element object and use that

var anyObject = document.createElement();

newEvent.initWebKitAnimationEvent("webkitAnimationEnd",true,true,anyObject,60);

note on the initwebkitanimationevent method part 2
Note on the initWebKitAnimationEvent()method, part 2
  • The previous slide referred to the use of the window object as an argument to initWebKitAnimationEvent() method
  • This was in a web-page that talked about programming for an Apple product, the iPod touch
  • Since -webkit- browsers are based on the Safari model, it may be useful to consult documentation on the Safari DOM:
    • The initWebKitAnimationEvent() method is partly desribed in this PDF document, entitledSafari DOM Extensions Reference, and dated 21 November 2008:

http://pooky.sourceforge.net/data/SafariJSRef.pdf

really creating an animation end event first run
Really creating an animation end event, first run
  • In the last program, we just simulated the end of an animation
  • Suppose that we want to actually create the end of an animation
  • The button on this web-page does so

http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g17/diver.html

  • First time you view this page, do not use the button
    • wait until all animation stops and look at the output
  • Note that, as in the last program, text is added to the top message area, saying that each animation lasted 21 seconds
really creating an animation end event second run
Really creating an animation end event, second run
  • Now, reload this page and view it a second time in a -webkit- browser

http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g17/diver.html

  • This time, click on the button just after the animation starts
  • Immediately, diver1 stops
    • and we get a message lying about the length of its animation
  • The other two divers continue until their animations are finished
the html file is unchanged from before
The HTML file is unchanged from before

<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:200px">

<div id="messageDiv" style="visibility:visible;color:red;font-size:30px">

Please wait for start of animation

</div>

</div>

<div id="underwater"></div>

<button type="button" id="button1">

Simulate an end to animation on diver1

</button>

</body>

</html>

the css file is unchanged from 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-duration : 7s; -webkit-animation-delay : 3s;

-webkit-animation-direction : alternate; -webkit-animation-iteration-count:3; }

#underwater > div > img { width: 100px; height: 100px; }

@-webkit-keyframes dive

{ 0% { -webkit-transform: rotate(90deg) translate(0px, 0px); }

100% { -webkit-transform: translate(0px, 750px); } }

The CSS file is unchanged from before
the javascript architecture is the same as before
The Javascript architecture is the same as before

const NUMBER_OF_DIVERS = 3;

function init() {... }

function randomInteger(lower,upper) { ... }

function processStart(e) { ... }

function processEnd(e) { ...}

function newDiver(counter) { ... }

function simulateAnimationEnd() { ... }

window.addEventListener(\'load\', init, false);

function init is the same as before
function init() is the same as before

function init()

{ var envelope = document.getElementById(\'underwater\');

var someDiver;

for (var i = 0; i < NUMBER_OF_DIVERS; i++)

{ someDiver=newDiver(i);

envelope.appendChild(someDiver); }

var button1 = document.getElementById(\'button1\');

button1.addEventListener(\'click\', simulateAnimationEnd, false);

}

the randominteger and processstart functions are unchanged
The randomInteger() and processStart() functions are unchanged

function randomInteger(lower,upper)

{ return lower + Math.round(Math.random() * (upper-lower)); }

function processStart(e)

{ var messageDiv = document.getElementById(\'messageDiv\');

messageDiv.innerHTML=""

}

the processend function has one extra statement
The processEnd() function has one extra statement
  • When an animationEnd event object involves a diver, we eliminate that diver\'s animation
  • This means that, even if the event was only simulated, the diver stops moving

function processEnd(e)

{ /*Find the diver whose animationEnd just happened or is being simulated*/

diverID=e.target.getAttribute("id");

/*Eliminate this diver\'s animation*/

diver.style.webkitAnimationName="";

/*Add appropriate output to the message div*/

var messageDiv = document.getElementById(\'messageDiv\');

oldMessage=messageDiv.innerHTML;

newMessage="The animation on "+diverID+

" lasted "+e.elapsedTime+" seconds.";

messageDiv.innerHTML=oldMessage+newMessage; }

the newdiver function is unchanged
The newDiver() function is unchanged

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\';

diverDiv.addEventListener(\'webkitAnimationStart\', processStart,false );

diverDiv.addEventListener(\'webkitAnimationEnd\', processEnd, false );

return diverDiv; }

the simulateanimationend function is unchanged
The simulateAnimationEnd() function is unchanged

function simulateAnimationEnd()

{ var diver1=document.getElementById(\'diver1\');

var newEvent = document.createEvent("WebKitAnimationEvent");

var anyObject = document.createElement();

newEvent.initWebKitAnimationEvent("webkitAnimationEnd",

true, true,anyObject,600.5);

diver1.dispatchEvent(newEvent);

}

reporting the correct elapsed time
Reporting the correct elapsed time
  • In the last program, an untruthful elapsed time was reported when diver1\'s animation was artificially ended
  • This program tells the truth when we forcibly end diver1\'s animation:

http://www.cs.ucc.ie/j.bowen/cs4506/slides/diving/g18/diver.html

the html file is unchanged from before1
The HTML file is unchanged from before

<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:200px">

<div id="messageDiv" style="visibility:visible;color:red;font-size:30px">

Please wait for start of animation

</div>

</div>

<div id="underwater"></div>

<button type="button" id="button1">

Simulate an end to animation on diver1

</button>

</body>

</html>

the css file is unchanged from before1
#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-duration : 7s; -webkit-animation-delay : 3s;

-webkit-animation-direction : alternate; -webkit-animation-iteration-count:3; }

#underwater > div > img { width: 100px; height: 100px; }

@-webkit-keyframes dive

{ 0% { -webkit-transform: rotate(90deg) translate(0px, 0px); }

100% { -webkit-transform: translate(0px, 750px); } }

The CSS file is unchanged from before
the javascript architecture is slightly changed
The Javascript architecture is slightly changed
  • We introduce a global variable to hold the time that diver1\'s animation is started

const NUMBER_OF_DIVERS = 3;

var startOfDiver1Animation;

function init() {... }

function randomInteger(lower,upper) { ... }

function processStart(e) { ... }

function processEnd(e) { ...}

function newDiver(counter) { ... }

function simulateAnimationEnd() { ... }

window.addEventListener(\'load\', init, false);

function init is the same as before1
function init() is the same as before

function init()

{ var envelope = document.getElementById(\'underwater\');

var someDiver;

for (var i = 0; i < NUMBER_OF_DIVERS; i++)

{ someDiver=newDiver(i);

envelope.appendChild(someDiver); }

var button1 = document.getElementById(\'button1\');

button1.addEventListener(\'click\', simulateAnimationEnd, false);

}

the randominteger function is unchanged
The randomInteger() function is unchanged

function randomInteger(lower,upper)

{ return lower + Math.round(Math.random() * (upper-lower)); }

the processstart function is slightly changed
The processStart() function is slightly changed
  • Each time an animation is started, we check whether the diver involved is diver1
  • If so, we set the global variable startOfDiver1Animation to the current time in millseconds

function processStart(e)

{ /*Find the diver whose animationStart just happened */

diverID=e.target.getAttribute("id");

if (diverID=="diver1")

{ now=new Date();

startOfDiver1Animation =now.getTime(); }

var messageDiv = document.getElementById(\'messageDiv\');

messageDiv.innerHTML=""

}

the processend function is unchanged
The processEnd() function is unchanged

function processEnd(e)

{ /*Find the diver whose animationEnd just happened or is being simulated*/

diverID=e.target.getAttribute("id");

/*Eliminate this diver\'s animation*/

diver.style.webkitAnimationName="";

/*Add appropriate output to the message div*/

var messageDiv = document.getElementById(\'messageDiv\');

oldMessage=messageDiv.innerHTML;

newMessage="The animation on "+diverID+

" lasted "+e.elapsedTime+" seconds.";

messageDiv.innerHTML=oldMessage+newMessage; }

the newdiver function is unchanged1
The newDiver() function is unchanged

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\';

diverDiv.addEventListener(\'webkitAnimationStart\', processStart,false );

diverDiv.addEventListener(\'webkitAnimationEnd\', processEnd, false );

return diverDiv; }

the simulateanimationend function is slightly changed
The simulateAnimationEnd() function is slightly changed
  • We compute the elapsed time for diver1\'s animation, whose end we are simulating, to be the current time (in milliseconds) minus the time that diver1\'s animation was started
  • We divide the elapsed time by 1000 to get it in seconds
  • We use this computed time when initializing the event

function simulateAnimationEnd()

{var now=new Date();

var elapsedTime=(now.getTime()-startOfDiver1Animation)/1000;

var diver1=document.getElementById(\'diver1\');

var newEvent = document.createEvent("WebKitAnimationEvent");

var anyObject = document.createElement();

newEvent.initWebKitAnimationEvent("webkitAnimationEnd",

true, true,anyObject,elapsedTime);

diver1.dispatchEvent(newEvent);

}

ad