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