Inicio rápido: gestos estáticos (HTML)

[ Este artículo está destinado a desarrolladores de Windows 8.x y Windows Phone 8.x que escriben aplicaciones de Windows en tiempo de ejecución. Si estás desarrollando para Windows 10, consulta la documentación más reciente

Controla eventos de gestos básicos de Windows en tiempo de ejecución y personaliza la experiencia del usuario para los gestos estáticos descritos en el lenguaje táctil de Windows (como pulsar, pulsar dos veces, mantener presionado y pulsación derecha).

La mayoría de las aplicaciones procesa los gestos (pulsaciones, movimientos panorámicos, zoom, etc.) y no hace prácticamente nada con los datos de puntero sin procesar, salvo pasarlos a la detección de gestos. En este ejemplo, usamos estos datos de puntero sin procesar en favor del control y procesamiento de los gestos estáticos para ampliar el modelo de interacción de la aplicación y basarse en los eventos de puntero descritos en Inicio rápido: punteros.

Actualizaciones para Windows 8.1: Windows 8.1 incorpora varias actualizaciones y mejoras para las API para entrada de puntero. Para obtener más información, consulta el tema sobre cambios en las API para Windows 8.1.

Si no estás familiarizado con el desarrollo de aplicaciones con JavaScript: Echa un vistazo a estos temas para familiarizarte con las tecnologías que se mencionan aquí.

Crear tu primera aplicación con JavaScript

Guía básica para aplicaciones con JavaScript

Obtén información acerca de los eventos con Inicio rápido: agregar controles HTML y controlar eventos

Funciones de la aplicación, de principio a fin:

Consulta esta funcionalidad con más detalle como parte de la serie Funciones de la aplicación, de principio a fin

Interacción del usuario, de principio a fin (HTML)

Personalización de la interacción del usuario, de principio a fin (HTML)

Directrices sobre la experiencia del usuario:

Las bibliotecas de control de la plataforma (HTML y XAML) proporcionan una experiencia de interacción del usuario completa, incluidas interacciones estándar, efectos físicos animados y comentarios visuales. Si no necesitas compatibilidad para interacción personalizada, usa estos controles integrados.

Si los controles de la plataforma no son suficientes, estas directrices para la interacción del usuario te pueden ayudar a proporcionar una experiencia de interacción atractiva y envolvente que sea coherente en todos los modos de entrada. Estas instrucciones se centran principalmente en la entrada táctil, pero también son válidas para entradas de panel táctil, mouse, teclado y lápiz.

Muestras: Verás esta funcionalidad en acción en nuestras muestras de aplicaciones.

Personalización de la interacción del usuario, muestra de principio a fin

Entrada: muestra de control de eventos de puntero de DOM

Entrada: muestra de manipulaciones y gestos (JavaScript)

Entrada: muestra de gestos de Windows 8

Objetivo: aprender a escuchar, controlar y procesar gestos estáticos mediante entradas de interacciones táctiles, del mouse, de lápiz o pluma y eventos de gestos de Windows en tiempo de ejecución.

Requisitos previos

Revisa los temas de inicio rápido: punteros y de inicio rápido: gestos y manipulaciones de DOM.

Damos por sentado que puedes crear una aplicación básica con JavaScript que use la plantilla de la Biblioteca de Windows para JavaScript.

Para completar este tutorial, necesitas:

Tiempo para finalizar: 30 minutos.

¿Qué son los eventos de gesto?

Un gesto es el acto físico o movimiento ejecutado en el dispositivo de entrada o por él (uno o varios dedos en una superficie táctil, un digitalizador de pluma o lápiz, un mouse, etc.). Estas interacciones naturales se asignan a operaciones en elementos tanto del sistema como de tu aplicación. Para más información, consulta el tema sobre gestos, manipulaciones e interacciones.

Esta tabla identifica los gestos estáticos que comentamos en este inicio rápido.

GestoDescripción
Pulsar/pulsar dos vecesGesto de pulsar

Un único contacto que se libera o finaliza inmediatamente.

La pulsación sobre un elemento invoca esta acción principal.

Por pulsar dos veces entendemos dos pulsaciones rápidas sucesivas, que pueden controlarse según lo requiera la aplicación.

  • Estado de entrada: se ha detectado un contacto dentro de los límites rectangulares de un objeto.
  • Movimiento: ninguno.
  • Estado de salida: el contacto se libera o finaliza.
Pulsar y sostener/pulsación derechaGesto de pulsar y sostener

Un único contacto que no se mueve hasta que se supera un umbral de tiempo.

Pulsar y sostener hace que se muestre información detallada o elementos visuales didácticos (por ejemplo, información sobre herramientas o un menú contextual) sin confirmar una acción.

La pulsación derecha está estrechamente relacionada con el gesto de pulsar y sostener. El evento de pulsación derecha se desencadena cuando se libera el gesto de pulsar y sostener.

  • Estado de entrada: se ha detectado un contacto dentro de los límites rectangulares de un objeto.
  • Movimiento: ninguno.
  • Estado de salida: el contacto se libera o finaliza.

Para más información sobre estos gestos y cómo se relacionan con el lenguaje táctil de Windows, consulta Diseño de la interacción táctil.

 

Importante  Si implementas tu propia compatibilidad con la interacción, ten presente que los usuarios esperan una experiencia intuitiva en la que interactúen en forma directa con los elementos de la interfaz de usuario de tu aplicación. Te recomendamos que modeles tus interacciones personalizadas sobre la biblioteca de controles de la plataforma (HTML y XAML) para que todo sea coherente y pueda detectarse. Los controles de estas bibliotecas proporcionan una experiencia de interacción de usuario completa, incluidas interacciones estándar, efectos físicos animados, comentarios visuales y accesibilidad. Crea únicamente interacciones personalizadas solo si existe un requisito claro y bien definido y no hay ninguna interacción básica que sea compatible con el escenario.

 

Crear la interfaz de usuario

Este ejemplo es una aplicación básica de preguntas y respuestas. Un cuadrado (inputBox) sirve de objeto de destino para detectar y procesar entradas de puntero y gestos estáticos. Todas las preguntas, pistas y respuestas se muestran dentro de este objeto.

La aplicación proporciona las siguientes funciones de interacción del usuario:

  • Pulsar dos veces: inicia y detiene las preguntas y el temporizador de la aplicación.
  • Pulsación: recorre las preguntas.
  • Pulsar y sostener: muestra un conjunto de pistas para la pregunta actual revelándolas de a una cada pocos segundos mientras se mantiene el contacto presionado. Este comportamiento de interacción cumple las directrices para información visual y las recomendaciones del lenguaje táctil que indican que el gesto de pulsar y sostener debe estar limitado a la visualización de la interfaz de usuario informativa.
  • Pulsación derecha (o liberación de pulsar y sostener): cuando se levanta el contacto, muestra un elemento emergente que pregunta al usuario si quiere ver la respuesta. De nuevo, cumplimos las recomendaciones para menús contextuales de las directrices para información visual y el lenguaje táctil de Windows. Nota  Para concentrar la atención sobre el código de control de gestos, no se implementan completamente ciertas funciones de la aplicación y la interfaz de usuario ni la lectura de datos de preguntas y respuestas de un archivo XML.  

Este es el HTML para este ejemplo.

<html>
<head>
    <meta charset="utf-8" />
    <title>js</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.2.0/css/ui-dark.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.2.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.2.0/js/ui.js"></script>

    <!-- BasicGesture references -->
    <link href="/css/default.css" rel="stylesheet" />
    <script src="/js/default.js"></script>
    <script src="/js/inputprocessor.js"></script>
    <script src="/js/datamanager.js"></script>
    <script src="/js/cluemanager.js"></script>
</head>
<body>
    <div class="TargetContainer" id="targetContainer">
        <div id="inputBox">
            <div id="instructions">Tap gray box below: Double tap to start questions, tap for next question, press and hold to show clues.</div>
            <div id="questions">&nbsp;</div>
            <div id="answers">
                <label for="answer">Answer:</label>
                <input type="text" id="answer" maxlength="30" size="30" style="z-index:1" />
                <button id="submit">Submit</button>
                <button id="stumped">Stumped</button>                
            </div>
            <div id="clues">
            </div>
            <div id="timerBox"></div>
        </div>
        <div id="eventLog"></div>

        <div id="answerFloater">
            <p>Show answer?</p>
            <button id="yes">Yes</button>
            <button id="no">No</button>
        </div>
    </div>
</body>
</html>

Estas son las hojas de estilo CSS de este ejemplo.

Nota  Los eventos de puntero no se desencadenan durante una interacción de zoom o desplazamiento lateral. Puedes deshabilitar el zoom y el desplazamiento lateral en una región mediante las propiedades msTouchAction, overflow y -ms-content-zooming de CSS.

 

body {
/*
A manipulation-blocking element is defined as an element that explicitly 
blocks direct manipulation via declarative markup, and instead fires gesture 
events such as MSGestureStart, MSGestureChange, and MSGestureEnd.
*/
    overflow: hidden;
    position: absolute;
    font-family: 'Segoe UI';
    font-size: small;
    touch-action: none;
    background-color: black;
}

div #targetContainer {
    position: relative;
    height: fill-available;
    width: fill-available;
}

div #inputBox {
    position: relative;
    width: 640px;
    height: 640px;
    color: black;
    overflow: hidden;
    background-color: darkgrey;
    margin: 0px;
    padding: 0px;
    border-width: 1px;
    border-color: white;
    border-style: solid;
}

div #instructions {
    position: relative;
    width: 100%;
    height: fit-content;
    color: black;
    background-color: white;
    visibility: visible;
}

div #questions {
    position: relative;
    width: 100%;
    height: fit-content;
    color: white;
    background-color: black;
    visibility: visible;
}

div #answers {
    position: relative;
    width: 100%;
    height: fit-content;
    color: white;
    background-color: black;
    visibility: visible;
}

div #clues {
    position: relative;
    width: 100%;
    height: 100%;
    background-color: DimGray;
}

div #timerBox {
    background-color: red;
    color: black;
    position: absolute;
    width: 100%;
    bottom: 0px;
    height: 20px;
    text-align: center;
}

div #answerFloater {
    position: absolute;
    visibility: hidden;
    top: 0px;
    left: 0px;
    background-color: blue;
}

div #eventLog {
    font-size: xx-small;
    position: absolute;
    left: 0px;
    top: 0px;
    width: 640px;
    height: 50px;
    overflow: auto;
    overflow-style: auto;
}

Inicializar la aplicación

Inicializa el objeto de preguntas y respuestas.

Aquí declaramos variables globales y obtenemos referencias a los objetos de la interfaz de usuario.

var _applicationData;
var _localSettings;
var _data;
var _inputBox;
var _instructions;
var _answers;
var _questions;
var _clues;
var _eventLog;
var _floater;

function initialize() {
    // Get our UI objects.
    _inputBox = document.getElementById("inputBox");
    _instructions = document.getElementById("instructions");
    _questions = document.getElementById("questions");
    _answers = document.getElementById("answers");
    _clues = document.getElementById("clues");
    _eventLog = document.getElementById("eventLog");
    _floater = document.getElementById("answerFloater");

    // Configure the target.
    setTarget();
}

Después, colocamos la interfaz de usuario de preguntas y respuestas y configuramos el objeto de interacción para procesar los datos de preguntas y respuestas del archivo XML en nuestro objeto de datos. Los detalles de los datos XML para este ejemplo se pueden revisar en la lista completa al final de este tema.

// Configure the interaction target.
function setTarget() {
    //  Set the position of the input target.
    var inputLeft = (window.innerWidth - _inputBox.clientWidth) / 2.0;
    var inputTop = (window.innerHeight - _inputBox.clientHeight) / 2.0;
    var transform = (new MSCSSMatrix()).translate(inputLeft, inputTop);
    _inputBox.style.msTransform = transform;

    // Set the position of the event log.
    transform = (new MSCSSMatrix()).translate(inputLeft, inputTop + _inputBox.clientHeight);
    _eventLog.style.msTransform = transform;

    // Associate interaction target with our input manager.
    // Scope input to clue area only.
    _clues.inputProcessor = new QandA.InputProcessor(_clues);
}

Configurar el reconocedor de gestos

Aquí, configuramos el control de interacciones.

En la mayoría de los casos, te recomendamos obtener información de puntero mediante el argumento de eventos de los controladores de eventos de puntero en el marco de lenguaje que hayas elegido.

Si el argumento de evento no expone los detalles de puntero que necesita tu aplicación, puedes acceder a los datos extendidos de puntero del argumento de evento con los métodos getCurrentPoint y getIntermediatePoints, o las propiedades currentPoint y intermediatePoints. Te recomendamos usar los métodos getCurrentPoint y getIntermediatePoints, que permiten especificar el contexto de los datos de puntero.

Sugerencia  Para este ejemplo, hay un solo objeto asociado con un reconocedor de gestos. Si tu aplicación contiene una gran cantidad de objetos que pueden manipularse (como un puzzle), considera crear un reconocedor de gestos en forma dinámica solo cuando se detecte una entrada de puntero en un objeto de destino. El reconocedor de gestos puede destruirse cuando finaliza la manipulación (consulta Entrada: muestra de gestos instanciables para ver un ejemplo). Para evitar la sobrecarga de crear y destruir reconocedores de gestos, crea un grupo pequeño de reconocedores de gestos en la inicialización y asígnalos en forma dinámica según sea necesario.

 

El objeto Procesador de entrada incluye el reconocedor de gestos (gr), que escucha y controla todos los eventos de gestos y de puntero. La interfaz de usuario de preguntas y respuestas está administrada por los controladores de eventos del reconocedor de gestos.

// Handle gesture recognition for this sample.
(function () {
    "use strict";
    var InputProcessor = WinJS.Class.define(
    // Constructor
    function InputProcessor_ctor(target) {
        this._questionsStarted = false;
        this._tapCount = 0;
        // Create a clue manager.
        this._clueManager = new QandA.ClueManager();
        // Load xml data from file into local app settings.
        var _dataObject = new QandA.DataManager();
        _data = _dataObject.getData();

        this._questionTotal = _data.selectNodes("questions/question").length;
        this._doubleTap = false;
        this._startTime;
        this._intervalTimerId;

        // Initialize the gesture recognizer.
        this.gr = new Windows.UI.Input.GestureRecognizer();

        // Turn off visual feedback for gestures.
        // Visual feedback for pointer input is still displayed. 
        this.gr.showGestureFeedback = false;

        // Configure gesture recognizer to process the following:
        // double tap               - start questions and timer.
        // tap                      - move to next question.
        // right tap                - show answer.
        // hold and hold with mouse - start clues.
        this.gr.gestureSettings =
            Windows.UI.Input.GestureSettings.tap |
            Windows.UI.Input.GestureSettings.doubleTap |
            Windows.UI.Input.GestureSettings.rightTap |
            Windows.UI.Input.GestureSettings.hold |
            Windows.UI.Input.GestureSettings.holdWithMouse;

        //
        // Set event listeners.
        //
        // Get our context.
        var that = this;

        // Register event listeners for these gestures.
        this.gr.addEventListener('tapped', tappedHandler);
        this.gr.addEventListener("holding", holdingHandler);
        this.gr.addEventListener("righttapped", rightTappedHandler);

        // The following functions are registered to handle DOM pointer events
        //
        // Basic pointer handling to highlight input area.
        target.addEventListener("pointerover", function onPointerOver(eventInfo) {
            eventInfo.stopImmediatePropagation = true;
            _eventLog.innerText += "pointer over || ";
            eventInfo.target.style.backgroundColor = "DarkGray";
        }, false);
        // Basic pointer handling to highlight input area.
        target.addEventListener("pointerout", function onPointerOut(eventInfo) {
            eventInfo.stopImmediatePropagation = true;
            _eventLog.innerText += "pointer out || ";
            eventInfo.target.style.backgroundColor = "DimGray";
        }, false);
        // Handle the pointer move event.
        // The holding gesture is routed through this event.
        // If pointer move is not handled, holding will not fire.
        target.addEventListener("pointermove", function onPointerMove(eventInfo) {
            eventInfo.stopImmediatePropagation = true;
            // Get intermediate PointerPoints
            var pps = eventInfo.intermediatePoints;

            // Pass the array of PointerPoints to the gesture recognizer.
            that.gr.processMoveEvents(pps);
        }, false);
        // Handle the pointer down event.
        target.addEventListener("pointerdown", function onPointerDown(eventInfo) {
            eventInfo.stopImmediatePropagation = true;
            _eventLog.innerText += "pointer down || ";

            // Hide the floater if visible.
            _floater.style.visibility = "hidden";

            // Get the PointerPoint for the pointer event.
            var pp = eventInfo.currentPoint;

            // Get whether this pointer down event is within
            // the time threshold for a double tap.
            that._doubleTap = that.gr.canBeDoubleTap(pp);

            // Pass the PointerPoint to the gesture recognizer.
            that.gr.processDownEvent(pp);
        }, false);
        // Handle the pointer up event.
        target.addEventListener("pointerup", function onPointerUp(eventInfo) {
            eventInfo.stopImmediatePropagation = true;
            _eventLog.innerText += "pointer up || ";

            // Get the current PointerPoint
            var pp = eventInfo.currentPoint;

            // Pass the PointerPoint to the gesture recognizer.
            that.gr.processUpEvent(pp);
        }, false);

        // The following functions are registered to handle gesture events.
        //
        // This handler processes taps and double taps.
        // Potential double taps are identified in the pointer down handler.
        function tappedHandler(evt) {
            // Single tap and questions started: Display next question.
            if (!that._doubleTap && that._questionsStarted) {
                _eventLog.innerText += "tapped || ";
                _instructions.innerText = "Double tap to stop questions.";
                _clues.innerText = "";
                that._tapCount++;
                that._clueManager.tapCount = that.tapCount;
                if (that._tapCount > that._questionTotal) {
                    _questions.innerText = "No more questions.";
                } else {
                    var xpath = "questions/question[" + (that._tapCount % (that._questionTotal + 1)) + "]/q";
                    // Read data from a simple setting
                    _questions.innerText = _data.selectSingleNode(xpath).innerText;
                }
            }
                // Single tap and questions not started: Don't do much.
            else if (!that._doubleTap && !that._questionsStarted) {
                _eventLog.innerText += "tapped || ";
                _instructions.innerText = "Double tap to start questions.";
            }
                // Double tap and questions not started: Display first question.
            else if (that._doubleTap && !that._questionsStarted) {
                _eventLog.innerText += "double-tapped || ";
                // Return if last question displayed.
                if (that._tapCount > that._questionTotal) {
                    _questions.innerText = "No more questions.";
                    return;
                }
                // Start questions.
                that._questionsStarted = true;
                _instructions.innerText = "Starting questions (double tap to stop questions).";

                // Question number is based on tap count.
                that._tapCount++;

                // Select question from XML data object.
                var xpath = "questions/question[" + (that._tapCount % (that._questionTotal + 1)) + "]/q";
                _questions.innerText = _data.selectSingleNode(xpath).innerText;

                // Display a basic timer once questions started.
                that._startTime = new Date().getTime();
                that._intervalTimerId = setInterval(displayTimer, 100);
            }
                // Double tap and questions started: Stop questions and timer.
            else if (that._doubleTap && that._questionsStarted) {
                _eventLog.innerText += "double-tapped || ";
                _instructions.innerText = "Questions stopped (double tap to start questions).";
                that._questionsStarted = false;
                clearInterval(that._intervalTimerId);
            }
        };

        // For this app, we display a basic timer once questions start.
        // In a more robust app, could be used for achievements.
        function displayTimer() {
            var x = new Date().getTime();
            timerBox.innerText = (x - that._startTime) / 1000;
        }

        // This handler processes right taps.
        // For all pointer devices a right tap is fired on
        // the release of a press and hold gesture.
        // For mouse devices, righttapped is also fired on a right button click.
        // For pen devices, 
        function rightTappedHandler(evt) {
            if (!that._questionsStarted) {
                return;
            }
            var transform = (new MSCSSMatrix()).
                translate(
                (window.innerWidth - _inputBox.clientWidth) / 2.0 + evt.position.x,
                (window.innerHeight - _inputBox.clientHeight) / 2.0 + evt.position.y);
            _floater.style.visibility = "visible";
            _floater.style.msTransform = transform;
            eventLog.innerText = "right-tap || ";
        }

        // The pointer move event must also be handled because the 
        // holding gesture is routed through this event.
        // If pointer move is not handled, holding will not fire.
        // A holding event is fired approximately one second after 
        // a pointer down if no subsequent movement is detected.
        function holdingHandler(evt) {
            if (!that._questionsStarted)
                return;
            if (evt.holdingState == Windows.UI.Input.HoldingState.started) {
                _eventLog.innerText += "holding || ";
                // Create a clue manager.
                that._clueManager.tapCount = that._tapCount;
                // Start displaying clues.
                that._clueManager.displayClues();
            } else if (evt.holdingState == Windows.UI.Input.HoldingState.completed) {
                that._clueManager.destroy();
                _eventLog.innerText += "holding completed || ";
            } else {
                _eventLog.innerText += "holding canceled || ";
            }
        }
    },
    {},
    {});

    WinJS.Namespace.define("QandA", {
        InputProcessor: InputProcessor
    });
})();

Finalmente, configuramos el administrador de pistas que muestra una serie de pistas para la pregunta actual durante el gesto de pulsar y sostener.

// Handle data for this sample.
(function () {
    "use strict";
    var ClueManager = WinJS.Class.define(
    // Constructor
    function ClueManager_ctor() {
        this._clueTimerId = null;
    },
    {
        displayClues: function () {
            var clue;
            var clueCount = 0;
            var clueCollection = _data.selectNodes("questions/question[" + this.tapCount + "]/clues/clue");

            this._clueTimerId = setInterval(function () {
                clueCount++;

                if (clueCount > clueCollection.length) {
                    _clues.innerText += "\nNo more clues.";
                    clearInterval(_clueTimerId);
                    return;
                }

                if (clueCount == 1)
                    clue = clueCollection.first();

                _clues.innerText += "\n" + clue.current.innerText;
                clue.moveNext();
            }, 2000);
        },
        destroy: function () {
            clearInterval(this._clueTimerId);
        },
        tapCount: {
            get: function () {
                return this._tapCount;
            },
            set: function (tapCount) {
                this._tapCount = tapCount;
            }
        }
    },
    {});

    WinJS.Namespace.define("QandA", {
        ClueManager: ClueManager
    });
})();

Consulta Temas relacionados en la parte inferior de esta página para obtener vínculos a muestras más complejas.

Ejemplo completo

Consulta Código completo de gestos estáticos.

Resumen y siguientes pasos

En este inicio rápido has aprendido a controlar los eventos de gesto estático en aplicaciones de la Tienda Windows con JavaScript.

El reconocimiento de gestos básicos, junto con los eventos de puntero, es útil para administrar interacciones simples, como pulsación, pulsar dos veces, pulsar y sostener, y pulsación derecha.

Consulta el tema sobre entrada: muestra de gestos instanciables para ver un ejemplo más complejo del control de gestos.

Nota  Esta muestra no cumple con las directrices del lenguaje táctil de Windows en cuanto a las interacciones personalizadas. Algunos de los gestos estáticos se han redefinido para fines instructivos.

 

Consulta Inicio rápido: gestos de manipulación para controlar interacciones de manipulación más elaboradas (como deslizar, deslizar rápidamente, girar, reducir y ampliar) con el fin de proporcionar una experiencia de interacción de usuario totalmente personalizada.

Para obtener más información sobre el lenguaje táctil de Windows, consulta Diseño de interacción táctil.

Temas relacionados

Desarrolladores

Responder a la interacción del usuario

Desarrollar aplicaciones de la Tienda Windows (JavaScript y HTML)

Inicio rápido: punteros

Inicio rápido: gestos y manipulaciones de DOM

Inicio rápido: gestos de manipulación

Diseñadores

Diseño de la interacción táctil