1 / 166

Back to the canvas element

Back to the canvas element. Animating within the canvas. Motivating example. View this page in a -moz- or -webkit- browser: http://www.cs.ucc.ie/j.bowen/cs4506/slides/canvasDiving/g16/main.html In the canvas on this page, a shark eats three divers

didina
Download Presentation

Back to the canvas element

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. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Back to the canvas element Animating within the canvas

  2. Motivating example • View this page in a -moz- or -webkit- browser: http://www.cs.ucc.ie/j.bowen/cs4506/slides/canvasDiving/g16/main.html • In the canvas on this page, a shark eats three divers • The "drama" has a soundtrack, sound effects and introductory and postscript segments • In this set of slides, we develop a set of object classes which can be used to build • other dramas like this • interactive dramas/games

  3. Animation objects • By the time we reach the end of these slides, we will have seen how to implement a set of object types which will make it easy to produce animations in several canvases on one page • We will have defined a set of object types which includes the following: • Drama • A Drama has a background, which is painted onto a canvas, and a list of entities called Actors which move into, around and out of the canvas • Actor • An Actor is an entity which moves into, around and out of a canvas

  4. A simple example - no animation yet • View this animation in a -moz- or -webkit- browser: http://www.cs.ucc.ie/j.bowen/cs4506/slides/canvasDiving/g1/main.html • In this simple example, • a seascape is painted as a background onto the canvas • a diver is placed on this background

  5. The HTML file • An external .js file is used to manipulate the canvas <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Canvas example</title> <script src="main.js" type="text/Javascript" language="javascript"> </script> </head> <body> <canvas id="myCanvas" width="500px" height="650px" style="border:solid 1px #ccc;"> Your browser does not support the canvas element </canvas> </body> </html>

  6. Architecture of the .js file • The canvas and context are global variables • An event listener is used to fire an initialization function, init(), when the page has been loaded • The init() function calls one function to draw the seascape background and another to place the diver at (250,150) var canvas; var context; function drawSeascape() { ... } function drawDiverAt(destX,destY) { ... } function init() { canvas=document.getElementById('myCanvas'); context=canvas.getContext('2d'); drawSeascape(); drawDiverAt(250,150); } window.addEventListener('load', init, false);

  7. The drawSeascape and drawDiverAt functions • Both of these use the standard drawImage() method provided by the context object function drawSeascape() { var seascape = new Image(); seascape.src = 'seascape.jpg'; context.drawImage(seascape,0,0,canvas.width,canvas.height); } function drawDiverAt(destX,destY) { var diverImg = new Image(); diverImg.src = 'diver.png'; var diverWidth = 100; var diverHeight = 100; context.drawImage(diverImg, destX,destY,diverWidth,diverHeight); }

  8. Reminder: the setTimeout() function • This takes two arguments: • a piece of JavaScript to be executed • a delay which must elapse before the code is executed • the delay must be specified in milliseconds • Format setTimeout(" ...some Javascript code ... ",delay);

  9. A first attempt at animation • View this animation in a -moz- or -webkit- browser: http://www.cs.ucc.ie/j.bowen/cs4506/slides/canvasDiving/g2/main.html • Here, • a seascape is painted as a background onto the canvas • a is animated down through the canvas

  10. The HTML file is unchanged <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Canvas example</title> <script src="main.js" type="text/Javascript" language="javascript"> </script> </head> <body> <canvas id="myCanvas" width="500px" height="650px" style="border:solid 1px #ccc;"> Your browser does not support the canvas element </canvas> </body> </html>

  11. Architecture of the .js file • There are several global variables • An initialization function is fired when the page has been loaded var framesPerSecond=25; var canvas; var context; var diverX; var diverY; var diverYIncrementPerFrame; function drawSeascape() { ... } function drawDiverAt(destX,destY) { ... } function animateDiverDownPastCanvas() { ... } function moveDiverDown() { ... } function init() { ... } window.addEventListener('load', init, false);

  12. The init() function • This paints the background and places the diver outside the canvas frame • The number of frames and the Y-increment per frame are computed • Then animateDiverDownPastCanvas() is called function init() { canvas=document.getElementById('myCanvas'); context=canvas.getContext('2d'); drawSeascape(); diverX=250; diverY=-150; drawDiverAt(diverX,diverY); durationOfAnimationInSeconds=7; numberOfFrames = durationOfAnimationInSeconds*framesPerSecond; distanceToTraverse=canvas.height-diverY; diverYIncrementPerFrame = distanceToTraverse/(numberOfFrames); animateDiverDownPastCanvas(); }

  13. The drawSeascape and drawDiverAt functions are unchanged function drawSeascape() { var seascape = new Image(); seascape.src = 'seascape.jpg'; context.drawImage(seascape,0,0,canvas.width,canvas.height); } function drawDiverAt(destX,destY) { var diverImg = new Image(); diverImg.src = 'diver.png'; var diverWidth = 100; var diverHeight = 100; context.drawImage(diverImg, destX,destY,diverWidth,diverHeight); }

  14. The drawSeascape and drawDiverAt functions • animateDiverDownPastCanvas() computes how often a frame should be drawn • Then it uses indirect recursion, through moveDiverDown, to call itself at this frequency until the diver has fallen past the bottom of the canvas • The diver is moved to its various positions by moveDiverDown() function animateDiverDownPastCanvas() { var millisecondsPerFrame=1000/framesPerSecond; if (diverY<650) { setTimeout("moveDiverDown()",millisecondsPerFrame); } } function moveDiverDown() { drawSeascape(); diverY=diverY+diverYIncrementPerFrame; drawDiverAt(diverX,diverY); animateDiverDownPastCanvas(); }

  15. Using objects • Animations of any real utility will quickly become quite complex • To control complexity, it is essential to use object-oriented programming • Before proceeding, we will review some aspects of object definition in Javascript • On the next few slides, we will consider the simple example of an object to represent the concept of a Box

  16. The Box() interface definition • Our concept of a Box has 2 attributes (width and height) and 3 methods (for reporting the width, height and area of the box) • We decide to have a constructor which has two required parameters, representing the width and height of the new Box to be created • The IDL definition is as follows: NamedConstructor=Box(number width, number height) interface Box { attribute number width; attribute number height; number getWidth(); number getHeight(); number getArea(); };

  17. Implementing the Box concept in JavaScript, version 1 • Strictly speaking, there are no object classes in Javascript, only individual objects • Functions are used to build objects • In the code below, the concept of a Box is defined and used function Box(width,height) { this.width=width; this.height=height; this.getWidth = function () { return this.width; } this.getHeight = function () { return this.height; } this.getArea = function () { return this.width*this.height; } } ... var box1= new Box(100,200); alert(box1.getArea()); /*outputs 20000 */

  18. Implementing the Box concept in JavaScript, version 2 • Sometimes an object may have private attributes • The Box object created below has a private attribute, area, which is not defined through in the IDL interface and is not available outside function Box(width,height) { this.width=width; this.height=height; var area = width*height; this.getWidth = function () { return this.width; } this.getHeight = function () { return this.height; } this.getArea = function () { return area; } } ... var box1= new Box(100,200); alert(box1.getArea()); /*outputs 20000 */

  19. The Boxx() interface definition • Sometimes an object's attributes are all private • For example, a box has a width, height and area, but they are not available through the IDL interface below • the constructor method does not take any parameters • there are methods for setting the width and height of a box • there is no way to directly affect the area of a box NamedConstructor=Boxx() interface Boxx { void setWidth(in number width); void setHeight(in number height); number getWidth(); number getHeight(); number getArea(); };

  20. Implementing the Boxx concept in JavaScript, version 1 function Boxx() { var width; var height; this.setWidth = function (x) { this.width = x; } this.setHeight = function (x) { this.height = x; } this.getWidth = function () { return this.width; } this.getHeight = function () { return this.height; } this.getArea = function () { return area; } } ... var box1= new Boxx(); box1.setWidth(100); box1.setWidth(200); alert(box1.getArea()); /*outputs 20000 */

  21. Implementing the Boxx concept in JavaScript, version 2 • An object can also have private methods • Here, for example, the Boxx concept is implemented using a private method, area(), which is not available outside the object function Boxx() { var width; var height; function area() { return width*height; } this.setWidth = function (x) { this.width = x; } this.setHeight = function (x) { this.height = x; } this.getWidth = function () { return this.width; } this.getHeight = function () { return this.height; } this.getArea = function () { return area(); } } ... var box1= new Boxx(); box1.setWidth(100); box1.setWidth(200); alert(box1.getArea()); /*outputs 20000 */

  22. Using objects to introduce multiple divers • View this animation in a -moz- or -webkit- browser: http://www.cs.ucc.ie/j.bowen/cs4506/slides/canvasDiving/g3/main.html • Here, • a seascape is painted as a background onto the canvas • several divers, of different appearances and sizes, appear, in different places, on the canvas • As we shall see, objects are used to represent the divers

  23. The HTML file is unchanged <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Canvas example</title> <script src="main.js" type="text/Javascript" language="javascript"> </script> </head> <body> <canvas id="myCanvas" width="500px" height="650px" style="border:solid 1px #ccc;"> Your browser does not support the canvas element </canvas> </body> </html>

  24. Architecture of the .js file • Constructor functions are introduced for two types of object: • a Background object is painted on the canvas, as a backdrop for the actors • an Actor is an entity which can be drawn on top of the backdrop • An initialization function is called when the page is loaded function Actor(imageSrc,width,height,x,y) { ... } function Background(imageSrc) { ... } function init() { ... } window.addEventListener('load', init, false);

  25. The Background() interface definition • A Background has 1 public attribute and 1 public method • The constructor has one required parameter NamedConstructor=Background(DOMString imageSrc) interface Background { attribute DOMString imageSrc; void draw(); };

  26. The Actor() interface definition • An Actor has 5 public attributes and 2 public methods • The constructor has one required parameter and four optional parameters NamedConstructor=Actor(DOMString imageSrc, [number width, number height, number x, number y] ) interface Actor { attribute DOMString imageSrc; attribute number width; attribute number height; attribute number x; attribute number y; void draw(); void translate(in number x, in number y); };

  27. Using these objects in the init() function • Three actors are created: the first is drawn three times in different places, the second is drawn twice and the third is drawn once • Notice that the Actor() constructor is used with differing numbers of optional parameters function init() { var background= new Background('seascape.jpg'); background.draw(); var diver1=new Actor("diver1.png"); diver1.x=250; diver1.y=400; diver1. draw() ; diver1.translate(50,0); diver1. draw() ; diver1.translate(25,0); diver1. draw() ; var diver2=new Actor("diver2.png",50,50); diver2.x=50; diver2.y=600; diver2. draw() ; var diver3=new Actor("diver3.png",50,50,400,300); diver3. draw() ; diver3.translate(50,0); diver3. draw() ; }

  28. The Background() implementation • Notice that a private attribute, called image, is used in the implementation of the object • The constructor method also checks whether or not the required parameter was provided and, if not, tries to degrade gracefully function Background(imageSrc) { if (imageSrc) { this.imageSrc=imageSrc; } else { this.imageSrc=""; alert('Warning: no image source defined'); } varimage = new Image(); this.draw = function () { var canvas=document.getElementById('myCanvas'); var context=canvas.getContext('2d'); image.src=this.imageSrc; context.drawImage(image,0,0,canvas.width,canvas.height); } }

  29. The Actor() implementation • A private attribute, called image, is used in the implementation of the object function Actor(imageSrc,width,height,x,y) { if (width) { this.width=width; } else { this.width=100; } if (height) { this.height=height; } else { this.height=100; } if (x<0 || x>0) { this.x=x; } else { this.x=0; } if (y<0 || y>0) { this.y=y; } else { this.y=0; } if (imageSrc) { this.imageSrc=imageSrc; } else { this.imageSrc=""; } var image = new Image(); this.draw = function () { var canvas=document.getElementById('myCanvas'); var context=canvas.getContext('2d'); image.src=this.imageSrc; context.drawImage(image,this.x,this.y,this.width,this.height); } this.translate = function (x,y) { this.x=this.x+x; this.y=this.y+y; } }

  30. Animating an Actor • View this animation in a -moz- or -webkit- browser: http://www.cs.ucc.ie/j.bowen/cs4506/slides/canvasDiving/g5/main.html • An (extended version of the) Actor object is used to represent a diver • The diver is animated across the seascape • Buttons are provided for pausing and resuming the animation

  31. Only change to HTML file is addition of buttons <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Canvas example</title> <script src="main.js" type="text/Javascript" language="javascript"> </script> </head> <body> <canvas id="myCanvas" width="500px" height="650px" style="border:solid 1px #ccc;"> Your browser does not support the canvas element </canvas> <button type="button" id="button1">Pause</button> <button type="button" id="button2">Resume</button> </body> </html>

  32. Architecture of the .js file • Two global variables, framesPerSecond and milliSecondsPerFrame, are used to define key aspects of the animation • if all browsers supported CONST, framesPerSecond should be a constant • The background and diver are global objects • Two functions, startAnimation() and continueAnimation(), are provided for overall animation control • Two functions , pause() and resume(), are provided to respond to buttons var framesPerSecond = 25; var millisecondsPerFrame = 1000/framesPerSecond; var background; var diver1; function Actor(imageSrc,width,height,x,y) { ... } function Background(imageSrc) { ... } function startAnimation() { ... } function continueAnimation() { ... } function pause() { ... } function resume() { ... } function init() { ... } window.addEventListener('load', init, false);

  33. The Background() concept is unchanged • The Background() interface is unchanged NamedConstructor=Background(DOMString imageSrc) interface Background { attribute DOMString imageSrc; void draw(); }; • So its implementation is unchanged function Background(imageSrc) { if (imageSrc) { this.imageSrc=imageSrc; } else { this.imageSrc=""; alert('Warning: no image source defined'); } var image = new Image() this.draw = function () { var canvas=document.getElementById('myCanvas'); var context=canvas.getContext('2d'); image.src=this.imageSrc; context.drawImage(image,0,0,canvas.width,canvas.height); } }

  34. Improving the concept of an Actor • Now that we have recognized the concept of an Actor, it would make sense to give it some of the features we found in CSS animations, such as * the duration of the Actor's animation * the Actor's play state (to pause/resume its movement) * a sequence of keyframes to specify transformations • To specify the duration of the animation, we will need a public method like this: void setAnimationDuration(in number duration); • To specify the play state, we will need a public method like this: void setAnimationPlayState(in DOMString playstate); • For now, we will not consider the full concept of a sequence of keyframes • Instead, we will consider the notion of a straight-line trajectory, along which the Actor moves during its animation • To use the trajectory, we could provide three methods like this: void setAnimationTrajectory(in number x1, in number y1, in number x2, in number y2); void goToStartOfTrajectory(); void moveToNextPointOnTrajectory();

  35. Improving the concept of an Actor (contd.) • Whenever we have methods for setting attributes of an object, it usually makes sense to have corresponding methods for accessing the values of these attributes • We have identified the need for void setAnimationDuration(in number duration); void setAnimationPlayState(in DOMString playstate); void setAnimationTrajectory(in number x1, in number y1, in number x2, in number y2); • So, it might seem that we also need these three methods numbergetAnimationDuration(); DOMStringgetAnimationPlayState(); trajectory getAnimationTrajectory(); • In fact, however, we will find that we need just this one DOMStringgetAnimationPlayState();

  36. The new Actor() interface definition • An Actor has the same set of public attributes as before and the constructor is the same as before • However there are now 6 different public methods NamedConstructor=Actor(DOMString imageSrc, [number width, number height, number x, number y] ) interface Actor { attribute DOMString imageSrc; attribute number width; attribute number height; attribute number x; attribute number y; void setAnimationTrajectory(in number x1, in number y1, in number x2, in number y2); void goToStartOfTrajectory(); void moveToNextPointOnTrajectory(); void setAnimationDuration(in number duration); void setAnimationPlayState(in DOMString playstate); DOMStringgetAnimationPlayState(); };

  37. Before we ... • Before we consider how to implement the new Actor concept, let's see it in use

  38. Using the Actor concept in the init() function • We set event listeners on the pause/resume buttons • We create the backdrop although we do not yet draw it • We construct an actor and set the trajectory and duration of its animation • Then, we start the animation function init() { button1=document.getElementById('button1'); button1.addEventListener('click', pause, false); button2=document.getElementById('button2'); button2.addEventListener('click', resume, false); background=new Background('seascape.jpg'); diver1=new Actor("diver1.png"); diver1.setAnimationTrajectory(100,150,400,500); diver1.setAnimationDuration(7); startAnimation(); }

  39. Starting the animation • To animate the Actor, we • draw the previously-created background • place it at the start of its trajectory • set its animation play state to 'running' • and set a Timeout to continue the animation after the elapse of the appropriate number of milliseconds for one frame function startAnimation() { background.draw(); diver1.goToStartOfTrajectory(); diver1.setAnimationPlayState('running'); setTimeout("continueAnimation()",millisecondsPerFrame); }

  40. Continuing the animation • When generating each subsequent frame of the animation, we must • first redraw the backdrop before we • place the Actor at the next point in its trajectory • It is possible that the next point is the end of the trajectory and, if so, the animation play state will be set to ‘finished’ when Actor is moved • So, • we check whether the animation play state is still ‘running’ before • we set a Timeout to draw yet another frame function continueAnimation() { background.draw(); diver1.moveToNextPointOnTrajectory(); if ( diver1.getAnimationPlayState() == 'running' ) { setTimeout("continueAnimation()",millisecondsPerFrame); } }

  41. The pause() and resume() functions function pause() { diver1.setAnimationPlayState('paused'); } • When the pause button is clicked, the pause() function sets the Actor’s play state to ‘paused’ • Thus, next time continueAnimation() is executed, it will not issue a further timeout-delayed call to itself, so the animation will halt function resume() { diver1.setAnimationPlayState('running'); continueAnimation(); } • When the resume button is clicked, the resume() function • sets the Actor’s play state to ‘running’ • and restarts the animation by calling continueAnimation()

  42. Before we look at implementing new Actor concept • Before we look at implementing the new Actor concept, ... • we need to have another look at * the meaning of the keyword this in Javascript and ... * how it is used when implementing objects

  43. The keyword this in Javascript, part 1 • Consider the output from this simple HTML page http://www.cs.ucc.ie/j.bowen/cs4506/slides/thisExamples/ex1.html <html> <head><title>Keyword *this* example</title> <script> var product = function() { return this.width * this.height; } </script> </head> <body> <script> box1 = new Object(); box1.width=10; box1.height=10; box1.area=product; alert(box1.width); alert(box1.area); </script> </body> </html> • Notice that box1 gets its own copy of the function definition

  44. The keyword this in Javascript, part 2 • var someName = function(...) { ... }is usually written in shorthand form as function someName(...) { ... } • See output from http://www.cs.ucc.ie/j.bowen/cs4506/slides/thisExamples/ex2.html <html> <head><title>Keyword *this* example</title> <script> function product() { return this.width * this.height; } </script> </head> <body> <script> box1 = new Object(); box1.width=10; box1.height=10; box1.area=product; alert(box1.width); alert(box1.area); </script> </body> </html>

  45. The keyword this in Javascript, part 3 • Seehttp://www.cs.ucc.ie/j.bowen/cs4506/slides/thisExamples/ex3.html • Notice when each copy of the function is executed, this refers to the object which owns the function <html><head><title>Keyword *this* example</title><script> function product() { return this.width * this.height; } </script> </head><body><script> box1 = new Object(); box1.width=10; box1.height=10; box1.area=product; alert(box1.width); alert(box1.area()); box2 = new Object(); box2.width=20; box2.height=10; box2.area=product; alert(box2.width); alert(box2.area()); </script> </body> </html>

  46. The keyword this in Javascript, part 4 • See http://www.cs.ucc.ie/j.bowen/cs4506/slides/thisExamples/ex4.html • Here, too, when the function is executed, this refers to the object which owns the function, in this case the global top-level object, the window object <html> <head><title>Keyword *this* example</title> <script> function f1() { return this.location; } </script> </head> <body> <script> alert( f1() ); </script> </body> </html>

  47. The keyword this in Javascript, part 5 • We have just seen that, when a function is executed, the keyword this is interpreted to mean the object which owns the function • But, when we wrote constructor functions, we used the keyword this with a different meaning • Consider, for example, this constructor, which takes four parameters and returns an object with four public attributes, representing a rectangular plate made of a material of some density function Plate(w,h,t,d) { this.width=w; this.height=h; this.thickness=t; this.density=d; } • Here, we use this to refer to the object that will be built by the constructor • Let's contrast these two meanings of this in one simple program

  48. The keyword this in Javascript, part 5 • See http://www.cs.ucc.ie/j.bowen/cs4506/slides/thisExamples/ex5.html <html><head><title>Keyword *this* example</title> <script> function f1() { return this.location; } function Plate(w,h,t,d) { this.width=w; this.height=h; this.thickness=t; this.density=d; } </script></head><body><script> alert(f1()); plate1= new Plate(10,20,5,2); alert(plate1.width); </script></body></html> • When f1 is executed, this refers to the window • When Plate is executed, this refers to the object that is being built • The second interpretation of this is a consequence of the use of the keyword new

  49. The keyword this in Javascript, part 6 • We have just seen that this has a different meaning when constructor functions are executed (under control of the keyword new) than it has when ordinary functions are executed • Okay, we can accept this distinction • However, there is a complication as shall see, a problem arises when we define private methods

  50. The keyword this in Javascript, part 7 • To see the intricacy involved in using this in constructor functions, suppose we wish to extend the concept of a Plate that we have just defined, • to include public methods for accessing the volume and weight of a plate • One approach is on the next slide

More Related