Quickstart: Capturing ink data (HTML)

This Quickstart walks you through capturing ink data from an input digitizer.

Note  The code used in this topic is taken from a fully functional Microsoft Visual Studio 2013 JavaScript project. While the project is not available for download, the full Cascading Style Sheets (CSS), HTML, and JavaScript files can be found in Capturing ink data complete code.

 

Updates for Windows 8.1: Windows 8.1 introduces a number of updates and improvements to the pointer input APIs. See API changes for Windows 8.1 for more info.

Objective: After completing this Quickstart you will understand how to use the ink platform to detect and capture input from a pointer device (mouse, pen/stylus, or touch) in a Windows Store app using JavaScript.

Prerequisites

We assume that you can create a basic Windows Store app using JavaScript that uses the Windows Library for JavaScript template.

To complete this tutorial, you need to:

Instructions

1. Create a new "Blank App" project in Visual Studio and add HTML, CSS, and JavaScript files

For this example, we have one HTML file ("InkPage.html"), one CSS file ("InkPage.css"), and one JavaScript file ("InkPage.js").

The full CSS, HTML, and JavaScript files can be found in Capturing ink data complete code.

2. Set up a drawing surface in your UI

This example uses a Canvas element as the ink drawing and rendering surface.

A canvas is an HTML5 element that acts as a surface for dynamically drawing, rendering, and manipulating graphical elements in a Windows Store app using JavaScript.

Note  A Scalable Vector Graphics (SVG) object can also be used.

 

In the HTML file, declare a canvas element and give it an id of "inkCanvas". Use this id to reference the element from the JavaScript file.

<body>
<div id="applicationTitle">Ink sample</div>
<div>
    <canvas id="inkCanvas"></canvas>
    <div>
        <button id="load">Load</button>
        <button id="save">Save</button>
        <button id="draw">Draw</button>
        <button id="select">Select</button>
        <button id="selectall">Select all</button>
        <button id="erase">Erase</button>
        <button id="eraseAll">Erase all</button>
        <button id="recognize" value="selected">Handwriting recognition</button>
    </div>
</div>
<div id="modeMessage"></div>
<div id="deviceMessage"></div>
<div id="statusMessage"></div>
</body>

3. Create an ink manager

An InkManager object processes and manipulates the ink-related data obtained from pointer input.

In the JavaScript file, create the ink manager. For this example, the InkManager object is global.

        // Create an ink manager.
        // InkManager is documented at https://go.microsoft.com/fwlink/?LinkID=260648.
        var inkManager = new Windows.UI.Input.Inking.InkManager();

4. Connect your app to the drawing surface

To work with the canvas and its child elements, you need to define two variables.

The first variable (inkCanvas) is assigned a reference to the canvas element, "inkCanvas", using getElementById. The second variable (inkContext) is assigned the drawing context of the canvas element (a 2-D surface in this case), using the getContext method.

// Obtain reference to the specified element.
function get(elementId)
{
    return document.getElementById(elementId);
}
inkCanvas = get("inkCanvas");
inkContext = inkCanvas.getContext("2d");

5. Define the event handler functions

In this section, we define the various event handlers required for pointer input. These will be associated with the event listeners you add in the next step.

  • pointerdown is the event that is used to initiate ink capture.

    In this example, the beginPath and moveTo methods are used to set the location in screen coordinates at which to begin displaying the ink data. (Capturing ink and displaying it are two separate actions.) The pointerdown event is then processed through inkManager by passing the pointer data (currentPoint) of the event to ProcessPointerDown.

    The global variable, penID, is used to store the pointerId of the input pointer associated with this event. We'll discuss the need for this later.

    Note  This example filters the pointer input (using the pointerType property) so that ink capture is performed for pen/stylus input and mouse input only when the left button is pressed. Touch input is reserved for manipulating the UI of the app.

     

        function getPointerDeviceType(pId)
        {
            var pointerDeviceType;
            var pointerPoint = Windows.UI.Input.PointerPoint.getCurrentPoint(pId);
            switch (pointerPoint.pointerDevice.pointerDeviceType)
            {
                case Windows.Devices.Input.PointerDeviceType.touch:
                    pointerDeviceType = "Touch";
                    break;
    
                case Windows.Devices.Input.PointerDeviceType.pen:
                    pointerDeviceType = "Pen";
                    break;
    
                case Windows.Devices.Input.PointerDeviceType.mouse:
                    pointerDeviceType = "Mouse";
                    break;
                default:
                    pointerDeviceType = "Undefined";
            }
            deviceMessage.innerText = pointerDeviceType;
            return pointerDeviceType;
        }
    
        // Occurs when the pointer (touch, pen, mouse) is detected by the canvas.
        // Each stroke begins with onPointerDown.
        function onPointerDown(evt)
        {
            // Get the device type for the pointer input.
            pointerDeviceType = getPointerDeviceType(evt.pointerId);
    
            // Process pen and mouse (with left button) only. Reserve touch for manipulations.
            if ((pointerDeviceType === "Pen") || ((pointerDeviceType === "Mouse") && (evt.button === 0)))
            {
                statusMessage.innerText = pointerDeviceType + " pointer down: Start stroke. "
    
                // Process one pointer at a time.
                if (pointerId === -1)
                {
                    var current = evt.currentPoint;
    
                    // Start drawing the stroke.
                    inkContext.beginPath();
                    inkContext.lineWidth = strokeWidth;
                    inkContext.strokeStyle = strokeColor;
    
                    inkContext.moveTo(current.position.x, current.position.y);
    
                    // Add current pointer to the ink manager (begin stroke).
                    inkManager.processPointerDown(current);
    
                    // The pointer id is used to restrict input processing to the current stroke.
                    pointerId = evt.pointerId;
                }
            }
            else
            {
                // Process touch input.
            }
        }
    
  • Ink data is captured when an pointermove event occurs.

    In the following example, the global variable, penId, is used to ensure that the pointerId for this event is identical to that of the associated pointerdown event. If it's not, the input is ignored and no ink data is captured. This is useful, for example, to filter input from a mouse that is moved accidentally during a pen stroke.

    The lineTo (using the RawPosition of the pointer as reported by the digitizer) and stroke methods are called to draw and display the ink data immediately as discrete line segments. (Capturing ink and displaying it are two separate actions.) The pointermove event is then processed through inkManager by passing the pointer data (currentPoint) of the event to ProcessPointerUpdate.

        // Mouse: Occurs when the pointer moves.
        // Pen/Touch: Occurs at a steady rate (approx. 100 messages/second) whether the pointer moves or not.
        function onPointerMove(evt)
        {
            // Process pen and mouse (with left button) only. Reserve touch for manipulations.
            if ((pointerDeviceType === "Pen") || ((pointerDeviceType === "Mouse") && (evt.button === -1)))
            {
                statusMessage.innerText = pointerDeviceType + " pointer move: Draw stroke as lines. "
                // The pointer Id is used to restrict input processing to the current stroke.
                // pointerId is updated in onPointerDown().
                if (evt.pointerId === pointerId)
                {
                    var current = evt.currentPoint;
    
                    // Draw stroke in real time.
                    inkContext.lineTo(current.rawPosition.x, current.rawPosition.y);
                    inkContext.stroke();
    
                    // Add current pointer to the ink manager (update stroke).
                    inkManager.processPointerUpdate(current);
                }
            }
            else
            {
                // Process touch input.
            }
        }
    
  • Ink data capture is complete when an pointerup event occurs.

    As in the previous example, this function uses the global variable, penId, to ensure that the pointerId for this event is identical to that of the associated pointerdown and pointermove events. If it's not, the input is ignored and no ink data is captured.

    The lineTo, stroke, and closePath methods are called to complete and close the path created in the handlePointerDown function. The pointerup event is then processed through inkManager by passing the pointer data (currentPoint) of the event to ProcessPointerUp.

    The renderAllStrokes function in this example is optional and is called to process the ink data and render the raw stroke segments on the canvas element as smooth curves (See How to How to render ink data).

    // Occurs when the pointer (touch, pen, mouse) is lifted from the canvas.
    // Each stroke ends with onPointerUp.
    function onPointerUp(evt)
    {
        // Process pen and mouse (with left button) only. Reserve touch for manipulations.
        if ((pointerDeviceType === "Pen") || ((pointerDeviceType === "Mouse") && (evt.button === 0)))
        {
            statusMessage.innerText = pointerDeviceType + " pointer up: Finish stroke. "
            if (evt.pointerId === pointerId) {
                // Add current pointer to the ink manager (end stroke).
                inkManager.processPointerUp(evt.currentPoint);
    
                // End live drawing.
                inkContext.closePath();
    
                // Render strokes using bezier curves.
                renderAllStrokes();
    
                // Reset pointer Id.
                pointerId = -1;
            }
        }
        else
        {
            // Process touch input.
        }
    }
    

See Related topics at the bottom of this page for links to more complex samples.

6. Attach input event listeners to the drawing surface

Using the reference to the canvas element, attach PointerEvent listeners and associate them with the pointer event handlers defined in the previous step.

  • pointerdown fires when a user presses down on the digitizer surface with a pen or finger, or they click the left button on a mouse.
  • pointermove fires when the pointer associated with the pointerdown event moves across the canvas.
  • pointerup fires when the user lifts the pen or finger from the digitizer surface, or they release the left mouse button.
// Set up the handlers for input processing.
inkCanvas.addEventListener("pointerdown", onPointerDown, false);
inkCanvas.addEventListener("pointermove", onPointerMove, false);
inkCanvas.addEventListener("pointerup", onPointerUp, false);

Summary

You now have a basic idea of how to capture ink data with your Windows Store app.

To see this code in action, build and run the following ink samples at the Windows Store app sample home page:

  • Input: simplified ink sample - this sample demonstrates ink functionality such as saving and loading ink, selecting and deleting ink, and converting ink strokes to text through handwriting recognition.
  • Input: ink sample - in addition to the functionality demonstrated in Input: simplified ink sample, this sample provides a richer UI and demonstrates how to search within recognition results.

Conceptual

Responding to pen and stylus input

Reference

Windows.Devices.Input

Windows.UI.Core

Windows.UI.Input

Windows.UI.Input.Inking

Samples (DOM)

Input: DOM pointer event handling sample

Samples (Windows Store app APIs)

Input: Device capabilities sample

Input: Ink sample

Input: Simplified ink sample