6. Animations and User Inp Animations and User Input
Transcription
6. Animations and User Inp Animations and User Input
6. Animations and User Input IN THIS CHAPTER • How to create animations in WebGL • Learn the difference between requestAnimationFrame(), requestAnimationFrame setInterval(), and setTimeout() • How to measure the frame rate in your WebGL application • Learn the details of event handling • How to handle key input • How to handle mouse events ANIMATING THE SCENE • Declarative animations • Cascading Style Sheets (CSS) animations or the SVG <animate> tag • These animations are handled without any scripts • Specify pecify how the element should animate but you don’t have to generate each new frame yourself • Script-based animations • Each new frame is updated by a JavaScript as the result of a callback CSS • A standard used to describe the presentation of a document selector { } property1: value1; property2: value2; ... h1 { } color: red; text-align: align: center; Specifies pecifies that the text within the <h1> headings should be colored red and the headers should be centered Using setInterval() () and setTimeout() • JavaScript-based based animations is to use one of these two JavaScript methods timeoutInMilliseconds • setTimeout(codeToCall, timeoutInMilliseconds) • setInterval(codeToCall, timeoutInMilliseconds timeoutInMilliseconds) • They are global methods • Part of the window object in JavaScript • setTimeout() • Call codeToCall once • clearTimeout() () to cancel the call to the scheduled function or code Using setInterval() () and setTimeout() • setInteval() • Call codeToCall repeatedly () to cancel the call to the scheduled function or code • clearInterval() • Basic rendering loop function draw() { // 1. Update the positions of the objects in your scene // 2. Draw the current frame of your scene } function startup() { // Do your usual setup and initialization setInterval(draw, 16.7); } Using requestAnimationFrame() requestAnimationFrame • Newer, ewer, recommended method of implementing script-based script animations • If your callback function is called draw() function draw(currentTime) { // 1. Reqeuest a new call to draw the next frame before // you actually start drawing the current frame. requestAnimationFrame(draw); // 2. Update the positions of the moving objects in your scene // 3. Draw your scene } function startup() { // Do your usual setup and initialization canvas = document.getElementById(“myGLCanvas”); gl = createGLContext(canvas); setupShaders(); setupBuffers(); setupTextures(); draw(); } JavaScript a function can be invoked with any number of arguments, regardless of the number of arguments that are specified in the function definition Using requestAnimationFrame() requestAnimationFrame • For cross-platform support of requestAnimationFrame() requestAnimationFrame /** * Provides requestAnimationFrame in a cross browser way. */ window.requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { return window.setTimeout(callback, 1000/60); }; }) (); Compensating Movement for Different Frame Rates on draw(currentTime) { Reqeuest a new call to draw the next frame before you actually start drawing the current frame. requestAnimationFrame(draw); requestAnimationFrame Calculate how to compensate for varying frame rate and then update the position of the moving objects. urrentTime === undefined) { entTime = Date.now(); wgl.animationStartTime === undefined) { gl.animationStartTime = currentTime; wgl.y < 5) { Move your object. In this specific example the movement is just moving a box vertically from the position where y = 2.7 to y = 5 he movement should take 3 seconds. gl.y = 2.7 + (currentTime - pwgl.animationStartTime)/3000 * (5.0-2.7); 2.7); function startup() { // Do your usual setup and initialization canvas = document.getElementById(“myGLCanvas gl = createGLContext(canvas); setupShaders(); setupBuffers(); setupTextures(); gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.enable(gl.DEPTH_TEST); pwgl.x = 0.0; pwgl.y = 2.7; pwgl.z = 0.0; pwgl.animationStartTime = undefined; draw(); } Draw your scene Creating an FPS Counter to Measure the Smoothness of Your Animation • FPS = 1 Frame Time function draw(currentTime) { pwgl.requestId = requestAnimFrame(draw); if (currentTime === undefined) { currentTime = Date.now(); } // Update FPS if a second or more has passed since last FPS update if(currentTime - pwgl.previousFrameTimeStamp >= 1000) { pwgl.fpsCounter.innerHTML = pwgl.nbrOfFramesForFPS;; pwgl.nbrOfFramesForFPS = 0; pwgl.previousFrameTimeStamp = currentTime;; } // Draw the scene ... // When a frame is completed, update the number of drawn // frames to be able to calculate the FPS value pwgl.nbrOfFramesForFPS++; } requestID is a long integer value that uniquely identifies the entry in the callback list pwgl.fpsCounter -> <span> element The innerHTML property sets or returns the inner HTML of an element Understanding the Disadvantage of Using FPS as a Measurement • Disadvantage of using FPS he FPS is not linear against the time it takes to render a frame • The • Lost 10 fps from 60fps 1 1 − ≈ 0.020 − 0.0167 = 0.0033s = 3.3ms 50 60 • Lost 10 fps from 30fps 1 1 − ≈ 0.050 − 0.033 = 0.017s = 17 ms 20 30 Event Handing for User Interaction • Two major alternatives to handling events in the web browser • Basic Event Handling with DOM Level 0 • More Advanced Event Handling with DOM Level 2 Basic Event Handling with DOM Level 0 • Specify the event handler • The Event Handler as an Attribute of an HTML Element • The Event Handler as a Property of a JavaScript Object • The event handling names for DOM Level 0 • Start with the prefix “on”, such as onload, onmousedown, and onmouseup The Event Handler as an Attribute of an HTML Element <!DOCTYPE HTML> <html lang=”en”> <head> ... </head> <body onload=”startup();”> <canvas id=”myGLCanvas”” width=”500” height=”500”></canvas> height=”500”></canvas </body> </html> Executed when the document is loaded The Event Handler as a Property of a JavaScript Object want to use as your event handler • Assign the function that you wan directly to a property of a JavaScript object • The code is cleaner when you separate the HTML code from the JavaScript code • Can let the event handler functions be dynamic and assign them to a JavaScript property function imageLoadHandler() { // handle the loaded image ... } var image = new Image(); image.onload = imageLoadHandler; // Assign the event handler image.src = url; Advanced Event Handling with DOM Level 2 • Use the method addEventListener() addEventListener to register an event listener on an element canvas.addEventListener('webglcontextlost', handleContextLost, handleContextLost false); canvas.addEventListener('webglcontextrestored', handleContextRestored, handleContextRestored false); • The event handling names forr DOM DO Level 2 do not have the “on” prefix like the event handlers for DOM Level 0 have • onload(level 0), load(level2) Event Propagation • DOM level 0 • The events are dispatched to the document elements on which they occur • DOM Level 2 • It has an event propagation that happens in three phases • 1. Event capturing phase • 2.. Handlers at the actual target node are executed • 3. Event bubbling phase Event Propagation Event Propagation • Event capturing phase • The events start at the top of the DOM tree, which is generally at the document object, and then propagate down towards the target node • If any of the ancestors to the target node has registered an event handler that has capturing enabled bled, these handlers are executed during the event capturing phase canvas.addEventListener('webglcontextlost', handleContextLost, handleContextLost false); Enable event capturing during event propagation • Handlers at the actual target node are executed • This corresponds to the behavior of the DOM Level 0 event model Event Propagation • Event bubbling phase • The opposite of the capturing phase • The event bubbles up towards the top of the DOM tree again until it reaches the document object • During the bubbling phase, any registered event handlers on the ancestor nodes to the target are triggered Key Input • Three keyboard events are generated when an alphanumeric key is pressed • keydown • When an alphanumeric key is pressed, represent physical keys • keypress • After keydown, keypress followed immediately, represents which character is typed • keyup • When the key is released, represent physical keys Key Input • Two properties on a key event • keyCode • Contains a virtual keycode that gives you information about which key the user pressed • The virtual keycode corresponds to the th ASCII value for the uppercase version of the key • charCode • Gives you the ASCII value for the resulting character Key Input ocument.addEventListener('keydown', handleKeyDown, false); ocument.addEventListener('keyup', handleKeyUp, false); ocument.addEventListener('keypress', handleKeyPress, false); nction handleKeyDown(event) { console.log(“keydown - keyCode=%d, charCode=%d”, event.keyCode, event.keyCode event.charCode); nction handleKeyUp(event) { console.log(“keyup - keyCode=%d, charCode=%d”, event.keyCode, event.charCode); event.charCode nction handleKeyPress(event) { console.log(“keypress - keyCode=%d, charCode=%d”, event.keyCode, event.keyCode event.charCode); press Esc, In Chrome eydown - keyCode=27, charCode=0 eyup - keyCode=27, charCode=0 If press ‘A’(without Caps lock), In Chrom keydown - keyCode=65, charCode=0 keypress - keyCode=97, charCode=97 keyup - keyCode=65, charCode=0 If press ‘A’(without Caps lock), In Firefox keydown - keyCode=65, charCode=0 keypress - keyCode=0, charCode=97 keyup - keyCode=65, charCode=0 If press Esc, In Firefox keydown - keyCode=27, keyCode charCode=0 keypress - keyCode=27, keyCode charCode=0 keyup - keyCode=27, =27, charCode=0 Key Input Key Code Key Code Key Code Left arrow 37 1 49 B 66 Escape Right arrow Up arrow Down arrow 27 38 39 40 0 2 3 9 48 50 51 57 A 65 C 67 Z 90 D 68 Handling a Single-Key Key Press or Multiple Simultaneously Pressed Keys nction handleKeyDown(event) { When you get a keydown you first handle any immediate actions that are relevant. (String.fromCharCode(event.keyCode) == “M”) { // Fire missile since M key was pressed down ireMissile(); Store information about which key has been pressed so we can check this in the function handlePressedDownKeys(). This strategy let you have several keys pressed down simultaneously pwgl.listOfPressedKeys[event.keyCode] = true; nction handleKeyUp(event) { Update list of keys that are pressed down, by setting the released key to false; pwgl.listOfPressedKeys[event.keyCode] = false; function handlePressedDownKeys() { if (pwgl.listOfPressedKeys[38]) { // Arrow up, the user pressed the gas pedal. speed += 0.5; } if (pwgl.listOfPressedKeys[40]) { // Arrow down, the user pressed the brake. speed -= 0.5; } if (pwgl.listOfPressedKeys[37]) { // Arrow left, the user wants to turn left. turnLeft(); } if (pwgl.listOfPressedKeys[39]) { // Arrow right, the user wants to turn right. turnRight(); } } Updating each frame If consider update time, see slide 9 Mouse Input • Three basic events • mousemove • mousedown • mouseup • Property of mouse event • button • 0(left), 1(middle), 2(right) • clientX, clientY • Window coordinate(top-left coner downwards) x,y Mouse Input nction handleMouseMove(event) { console.log(“mousemove - clientX=%d, clientY=%d”, event.clientX, event.clientY); event.clientY nction handleMouseDown(event) { console.log(“mousedown - clientX=%d, clientY=%d, button=%d”, event.clientX, event.clientX event.clientY, event.button); nction handleMouseUp(event) { console.log(“mouseup - clientX=%d, clientY=%d, button=%d”, event.clientX, event.clientX event.clientY, event.button); click left button where x=5, y=5 ousedown - clientX=5, clientY=5, button=0 ouseup - clientX=5, clientY=5, button=0 click right button where x=5, y=5 ousedown - clientX=5, clientY=5, button=2 ouseup - clientX=5, clientY=5, button=2