towards programmatic control of keyframes, example 1

100
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

Upload: mikasi

Post on 01-Feb-2016

40 views

Category:

Documents


0 download

DESCRIPTION

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 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 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

Page 2: Towards  programmatic control of keyframes, example 1

Firefox (-moz-) report, part 1

Page 3: Towards  programmatic control of keyframes, example 1

Firefox (-moz-) report, part 2

Page 4: Towards  programmatic control of keyframes, example 1

Chrome (-webkit-) report

Page 5: Towards  programmatic control of keyframes, example 1

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

Page 6: Towards  programmatic control of keyframes, example 1

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

Page 7: Towards  programmatic control of keyframes, example 1

Extract from the CSS3 DOM

• The CSS3 Animations DOMhttp://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

Page 8: Towards  programmatic control of keyframes, example 1

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

Page 9: Towards  programmatic control of keyframes, example 1

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

Page 10: Towards  programmatic control of keyframes, example 1

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>

Page 11: Towards  programmatic control of keyframes, example 1

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

Page 12: Towards  programmatic control of keyframes, example 1

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 definitionsDocumentStyle { readonly attribute StyleSheetList styleSheets; };StyleSheetList { readonly attribute unsigned long length; StyleSheet item( in unsigned long index); }; StyleSheet { readonly attribute DOMString type; attribute boolean disabled; readonly attribute Node ownerNode; readonly attribute StyleSheet parentStyleSheet; readonly attribute DOMString href; readonly attribute DOMString title;

readonly attribute MediaList media; }; • Stylesheet does not contain CSS information, but it has a specialization

called CSSStyleSheet – see next slide

Page 13: Towards  programmatic control of keyframes, example 1

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 CSSRule ownerRule;

readonly attribute CSSRuleList cssRules;

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;

CSSRule item(in unsigned long index) ; };• We have already seen the CSSRule interface• Now, we know enough to analyse the Javascript file

Page 14: Towards  programmatic control of keyframes, example 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; }

Page 15: Towards  programmatic control of keyframes, example 1

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

Page 16: Towards  programmatic control of keyframes, example 1

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 CSSRuleList cssRules;

void insertRule(in DOMString rule);

void deleteRule(in DOMString key);

CSSKeyframeRule findRule(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

Page 17: Towards  programmatic control of keyframes, example 1

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

CSSKeyframeRule findRule(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

Page 18: Towards  programmatic control of keyframes, example 1

Manipulating @keyframes rules (contd.)

• A CSSKeyframeRule represents 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 CSSStyleDeclaration style;

};

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

Page 19: Towards  programmatic control of keyframes, example 1

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

Page 20: Towards  programmatic control of keyframes, example 1

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>

Page 21: Towards  programmatic control of keyframes, example 1

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

Page 22: Towards  programmatic control of keyframes, example 1

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

Page 23: Towards  programmatic control of keyframes, example 1

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

Page 24: Towards  programmatic control of keyframes, example 1

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

Page 25: Towards  programmatic control of keyframes, example 1

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>

Page 26: Towards  programmatic control of keyframes, example 1

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

Page 27: Towards  programmatic control of keyframes, example 1

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

Page 28: Towards  programmatic control of keyframes, example 1

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

Page 29: Towards  programmatic control of keyframes, example 1

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

Page 30: Towards  programmatic control of keyframes, example 1

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

Page 31: Towards  programmatic control of keyframes, example 1

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

Page 32: Towards  programmatic control of keyframes, example 1

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

Page 33: Towards  programmatic control of keyframes, example 1

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

Page 34: Towards  programmatic control of keyframes, example 1

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>

Page 35: Towards  programmatic control of keyframes, example 1

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

Page 36: Towards  programmatic control of keyframes, example 1

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

Page 37: Towards  programmatic control of keyframes, example 1

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

}

Page 38: Towards  programmatic control of keyframes, example 1

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

Page 39: Towards  programmatic control of keyframes, example 1

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

Page 40: Towards  programmatic control of keyframes, example 1

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

Page 41: Towards  programmatic control of keyframes, example 1

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

Page 42: Towards  programmatic control of keyframes, example 1

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 rulefunction 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; }

}

}

Page 43: Towards  programmatic control of keyframes, example 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;

}

Page 44: Towards  programmatic control of keyframes, example 1

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

}

Page 45: Towards  programmatic control of keyframes, example 1

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/

Page 46: Towards  programmatic control of keyframes, example 1

An attempt at an interactive animation

• Try to use this animation in a -moz- browserhttp://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

Page 47: Towards  programmatic control of keyframes, example 1

Animation events

• Try to use this animation in a -webkit- browserhttp://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

Page 48: Towards  programmatic control of keyframes, example 1

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>

Page 49: Towards  programmatic control of keyframes, example 1

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

Page 50: Towards  programmatic control of keyframes, example 1

The diver.js file

• We add an event listener to each diverDiv, listening for animationStart eventsconst 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);

Page 51: Towards  programmatic control of keyframes, example 1

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

);

}

Page 52: Towards  programmatic control of keyframes, example 1

CSS Animation events• CSS3 defined the AnimationEvent as a specialization of the Event

class that had been defined in DOM2AnimationEvent : 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/

Page 53: Towards  programmatic control of keyframes, example 1

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

Page 54: Towards  programmatic control of keyframes, example 1

Using events: message to user before/after animation

• Try to use this animation in a -webkit- browserhttp://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

Page 55: Towards  programmatic control of keyframes, example 1

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>

Page 56: Towards  programmatic control of keyframes, example 1

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

Page 57: Towards  programmatic control of keyframes, example 1

The diver.js file

• Javascript architecture is mostly the same• But now, two animation event handlers are attached to each diverconst 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);

Page 58: Towards  programmatic control of keyframes, example 1

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"; } }

Page 59: Towards  programmatic control of keyframes, example 1

Transition Events

• View this page in a -webkit- or -moz- or -o- or -ms- browserhttp://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

Page 60: Towards  programmatic control of keyframes, example 1

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>

Page 61: Towards  programmatic control of keyframes, example 1

Analysing the CSS file

• 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}

Page 62: Towards  programmatic control of keyframes, example 1

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

Page 63: Towards  programmatic control of keyframes, example 1

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/

Page 64: Towards  programmatic control of keyframes, example 1

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

Page 65: Towards  programmatic control of keyframes, example 1

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

Page 66: Towards  programmatic control of keyframes, example 1

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>

Page 67: Towards  programmatic control of keyframes, example 1

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

Page 68: Towards  programmatic control of keyframes, example 1

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

Page 69: Towards  programmatic control of keyframes, example 1

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

Page 70: Towards  programmatic control of keyframes, example 1

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>

Page 71: Towards  programmatic control of keyframes, example 1

Analysing the CSS file

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

Page 72: Towards  programmatic control of keyframes, example 1

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

Page 73: Towards  programmatic control of keyframes, example 1

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

}

Page 74: Towards  programmatic control of keyframes, example 1

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=""

}

Page 75: Towards  programmatic control of keyframes, example 1

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

Page 76: Towards  programmatic control of keyframes, example 1

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

Page 77: Towards  programmatic control of keyframes, example 1

And, now for the beef in the sandwich ...

• ..., that is, the simulateAnimationEnd() function

• It is on the next slide

Page 78: Towards  programmatic control of keyframes, example 1

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

Page 79: Towards  programmatic control of keyframes, example 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);

Page 80: Towards  programmatic control of keyframes, example 1

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, entitled Safari DOM Extensions Reference, and dated 21 November 2008:

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

Page 81: Towards  programmatic control of keyframes, example 1

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

Page 82: Towards  programmatic control of keyframes, example 1

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

Page 83: Towards  programmatic control of keyframes, example 1

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>

Page 84: Towards  programmatic control of keyframes, example 1

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

Page 85: Towards  programmatic control of keyframes, example 1

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

Page 86: Towards  programmatic control of keyframes, example 1

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

}

Page 87: Towards  programmatic control of keyframes, example 1

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=""

}

Page 88: Towards  programmatic control of keyframes, example 1

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

Page 89: Towards  programmatic control of keyframes, example 1

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

Page 90: Towards  programmatic control of keyframes, example 1

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

}

Page 91: Towards  programmatic control of keyframes, example 1

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

Page 92: Towards  programmatic control of keyframes, example 1

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>

Page 93: Towards  programmatic control of keyframes, example 1

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

Page 94: Towards  programmatic control of keyframes, example 1

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

Page 95: Towards  programmatic control of keyframes, example 1

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

}

Page 96: Towards  programmatic control of keyframes, example 1

The randomInteger() function is unchanged

function randomInteger(lower,upper)

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

Page 97: Towards  programmatic control of keyframes, example 1

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=""

}

Page 98: Towards  programmatic control of keyframes, example 1

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

Page 99: Towards  programmatic control of keyframes, example 1

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

Page 100: Towards  programmatic control of keyframes, example 1

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

}