User Tools

Site Tools


tutorials:legacy:html5:getting_started

Getting Started

Javascript and GestureWorks: (Client Setup)

Introduction

In this tutorial, you will be creating a boilerplate HTML canvas application, using the Javascript bindings to connect to a GestureWorks Server instance. You will learn how to load and initialize GestureWorks. For this tutorial you will need the Gestureworks Core Server; a free trial is available.


Requirements

  1. Working knowledge of Javascript
  2. GestureWorks client library
  3. Queue.js library
  4. A running instance of the GestureWorks Websocket Server (see Websocket: Server Setup)
  5. Multitouch device
  6. Supported browser (built and tested in Google Chrome)

Process Overview

All files used in this tutorial can be downloaded here.


Process Detail

1. Setup

Create a new HTML file containing a full-screen canvas and including gestureworks.js.

linenums:1 //
<!DOCTYPE html>
<html>
<head>
    <style>
        html, body {
            width: 100%;
            height: 100%;
            margin: 0px;
            border: 0px;
        }
        canvas {
            display: block;
        }
    </style>
    <script src="js/Queue.js" type="text/javascript" language="javascript"></script>
    <!--gestureworks requries Queue.js to function.-->
    <script src="js/gestureworks.js" type="text/javascript" language="javascript"></script>
    <script type="text/javascript" language="javascript">
        //application logic
    </script>
    <title>GestureWorks Core Demo</title>
</head>
<body>
<canvas id="gw_canvas">
</canvas>
</body>
</html>

2. Instantiating GestureWorks

In our script tag or file, we'll first need to instantiate GestureWorks. Start by replacing '//application logic' with the following code:

linenums:19
var gw;
var touchObjectId = "doc";
 
window.onload = function () {
    gw = new GestureWorks("ws://127.0.0.1:4000/");
    gw.onSocketOpen = function(e) {
        console.log("GestureWorks socket opened.");
        gw.registerTouchObject(touchObjId);
    };
    gw.onSocketError = function(e) {
        console.log("GestureWorks socket error: "+e);
    };
    gw.onSocketClose = function(e) {
        console.log("GestureWorks socket closed.");
    };
    gw.connect();
};

This snippet initializes a GestureWorks instance when the browser window finishes loading. Running the code should output “GestureWorks socket opened” in the javascript console assuming the GestureWorks Websocket Server is running. Please note that the URL should match the location of the server as well as the port number used. For this example we are assuming the server is being ran locally with default parameters.

For GestureWorks to function, a touch object needs to be registered. For this tutorial, we are using one touch object called 'doc' (for document). This touch object is registered inside the onSocketOpen callback using the registerTouchObject() API function. The rest of the code is boilerplate for handling the websocket.

3. Sending GestureWorks Touch Points

To send touch points to GestureWorks we will need to intercept the browsers touch events before the browser performs a default action. Specifically, we want to intercept the 'touchstart', 'touchmove', and 'touchend' events. For simplicity, all three events are being attached to one event listener. Inside the gw.onSocketOpen callback, we will need to add the following:

document.addEventListener('touchstart', touchHandler, false);
document.addEventListener('touchmove', touchHandler, false);
document.addEventListener('touchend', touchHandler, false);

The touchHandler function can be defined as follows:

function touchHandler(ev) {
    if (gw == null || !gw.IsConnected()) { return; }
    ev.preventDefault(); //prevent the browser from doing its default actions with touch-gestures and prevent mouse events from firing.
    var eType = null;
    if (ev.type === 'touchmove') {
        eType = GestureWorks.TouchStatus.TOUCH_UPDATE;
    } else if (ev.type === 'touchstart') {
        eType = GestureWorks.TouchStatus.TOUCH_ADDED;
    } else if (ev.type === 'touchend') {
        eType = GestureWorks.TouchStatus.TOUCH_REMOVED;
    }
 
    if (eType != null) {
        var currentTime = new Date().getTime() - startTime;
        var touches = ev.changedTouches;
        var touch;
        var i;
        //iterate through each touch and inform GestureWorks.
        for (i = 0; i < touches.length; i++) {
            touch = touches[i];
            gw.addEvent(touch.identifier, touch.clientX / window.innerWidth, touch.clientY / window.innerHeight, eType, currentTime, touch.radiusX, touch.radiusY);
        }
    }
}

There are a few things going here, and for starters, lets look at the touchHandler. The first line is for insuring the GestureWorks instance is initialized and connected before attempting to send data. The second line is to prevent the default action a browser takes when touch events are fired. By preventing the default handling of events, we prevent undesired behavior such as window zooming and panning. The rest of the function converts the DOM touch event into a GestureWorks touch event that can be sent using the addEvent API call.

Before this code can be ran, the variable startTime needs to be defined in the global scope (preferably near the start of the script)

var startTime;

and then startTime needs to be initialized when the socket opens

startTime = new Date().getTime();

The startTime variable is used to provide a starting point (in time) for our application. The Javascript Date API call new Date().getTime() returns the number of milliseconds elapsed since January 1st, 1970. This is a rather large number, and we don't really need to send that over the websocket. As such, the currentTime variable declared in the touchHandler function subtracts the current time from the startTime to get a smaller (but still meaningful) time delta.

In all, the current code should look similar to the following:

var gw;
var startTime;
function touchHandler(ev) {
    if (gw == null || !gw.IsConnected()) { return; }
    ev.preventDefault(); //prevent the browser from doing its default actions with touch-gestures and prevent mouse events from firing.
    var eType = null;
    if (ev.type === 'touchmove') {
        eType = GestureWorks.TouchStatus.TOUCH_UPDATE;
    } else if (ev.type === 'touchstart') {
        eType = GestureWorks.TouchStatus.TOUCH_ADDED;
    } else if (ev.type === 'touchend') {
        eType = GestureWorks.TouchStatus.TOUCH_REMOVED;
    }
 
    if (eType != null) {
        var currentTime = new Date().getTime() - startTime;
        var touches = ev.changedTouches;
        var touch;
        var i;
        //iterate through each touch and inform GestureWorks.
        for (i = 0; i < touches.length; i++) {
            touch = touches[i];
            gw.addEvent(touch.identifier, touch.clientX / window.innerWidth, touch.clientY / window.innerHeight, eType, currentTime, touch.radiusX, touch.radiusY);
        }
    }
}
 
window.onload = function () {
    gw = new GestureWorks("ws://127.0.0.1:4000/");
    startime = new Date().getTime();
    gw.onSocketOpen = function(e) {
        console.log("GestureWorks socket opened.");
        gw.registerTouchObject(touchObjId);
        document.addEventListener('touchstart', touchHandler, false);
        document.addEventListener('touchmove', touchHandler, false);
        document.addEventListener('touchend', touchHandler, false);
    };
    gw.onSocketError = function(e) {
        console.log("GestureWorks error: "+e);
    };
    gw.onSocketClose = function(e) {
        console.log("GestureWorks socket closed.");
    };
    gw.connect();
};

4. Visual Feedback

To see if we are sending and receiving data, it would be useful to illustrate touch points visually. Before we can draw the touch points, we need to store the touch data. When GestureWorks processes data, touch points are sent as feedback after the processing has finished. The API call used for this is called onConsumePointEvents. In the window.onload callback, we need to set the callback before gw.connect():

gw.onConsumePointEvents = consumeTouchPoints;
The consumeTouchPoints callback can be defined as follows:
 
function consumeTouchPoints(points) {
    //update the array of points accordingly.
    var point;
    for (var i = 0; i < points.length; i++) {
        point = points[i];
        if (point.Status === GestureWorks.TouchStatus.TOUCH_ADDED) {
            trackedPoints[point.PointId] = point;
            gw.addTouchPoint(touchObjId, point.PointId);
        } else if (point.Status === GestureWorks.TouchStatus.TOUCH_UPDATE) {
            trackedPoints[point.PointId] = point;
        } else if (point.Status === GestureWorks.TouchStatus.TOUCH_REMOVED) {
            trackedPoints[point.PointId] = null;
        } else {
            console.log("could not process touch event");
        }
    }
}
 
For this function to work, we need to define the trackedPoints array (near the top of the script):
 
<code:javascript>
var trackedPoints = [];

The consumeTouchPoints callback takes an array of points as an argument. Each index of the array contains a GestureWorks touch point that is then stored in the trackedPoints. You may notice the addTouchPoint API call as well. This API call assigns a touch point to a touch object, in this case, we are assigning all touch points to the 'doc' touch object. Notice that this call is executed after the touch points have been sent to GestureWorks and then received as GestureWorks touch points.

Now that we are storing data, we can set up an animation loop to draw touch points using the requestAnimationFrame function. Inside the onSocketOpen callback, we need to 'initialize' the animation loop by using the following:

window.requestAnimationFrame(update);

The update function can be defined as follows:

function update() {
    window.requestAnimationFrame(update);
    //wipe the canvas
    context.clearRect(0, 0, context.canvas.clientWidth, context.canvas.clientHeight);
    //draw touch points
    context.fillStyle = "rgba(255, 127, 0, 255)";
    for (var i = 0; i < trackedPoints.length; i++) {
        var point = trackedPoints[i];
        if (point == null) return;
        context.beginPath();
        context.arc(point.Position.X, point.Position.Y, 32, 0, PI2, false);
        context.arc(point.Position.X, point.Position.Y, 30, 0, PI2, true);
        context.arc(point.Position.X, point.Position.Y, 28, 0, PI2, false);
        context.fill();
    }
}

For the update function to work, a few variables need to be defined and initialized. Near the top of the script, lets define the three following variables:

var PI2 = Math.PI * 2;
var canvas, context;

In the window.onload callback, the canvas and context variables can be initialized like this:

canvas = document.getElementById("gw_canvas");
context = canvas.getContext('2d');

The update function is fairly straightforward. Each active touch point (coming from GestureWorks) is drawn as a circle with an outline. This example serves as a good starting point for debugging any GestureWorks websocket application.


Next tutorial: HTML5: Interactive Bitmaps

tutorials/legacy/html5/getting_started.txt · Last modified: 2019/01/21 19:24 (external edit)