Início rápido: gestos estáticos (HTML)

[ Este artigo destina-se aos desenvolvedores do Windows 8.x e do Windows Phone 8.x que escrevem aplicativos do Windows Runtime. Se você estiver desenvolvendo para o Windows 10, consulte documentação mais recente]

Manipule eventos gestos básicos do Tempo de Execução do Windows e personalize a experiência do usuário para os gestos estáticos descritos na linguagem de toque do Windows (como tocar, dar um toque duplo, pressionar e segurar e soltar).

A maioria dos aplicativos processa gestos (toques, movimentos panorâmicos, zooms e assim por diante) e faz pouco uso dos dados de ponteiro brutos, com exceção de repassá-los para a detecção do gesto. Neste exemplo, usamos esses dados de ponteiro brutos no suporte ao processamento dos gestos de manipulação para ampliar o modelo de interação do aplicativo e desenvolver com base nos eventos de ponteiro básicos descritos em Guia de início rápido: ponteiros.

Atualizações para Windows 8.1: O Windows 8.1 introduz uma série de atualizações e melhorias para as APIs de entrada de ponteiro. Para saber mais, veja Alterações de API para Windows 8.1.

Se você for iniciante no desenvolvimento de aplicativos em JavaScript: Leia estes tópicos para se familiarizar com as tecnologias discutidas aqui.

Criar seu primeiro aplicativo em JavaScript

Mapa para aplicativos usando JavaScript

Saiba mais sobre eventos com o Guia de início rápido: adicionando controles HTML e manipulando eventos

Recursos de aplicativos, do início ao fim:

Explore esse recurso mais profundamente como parte da nossa série sobre recursos para aplicativos, do início ao fim

Interação do usuário, do início ao fim (HTML)

Personalização da interação do usuário, do início ao fim (HTML)

Diretrizes de experiência do usuário:

As bibliotecas de controle de plataforma (HTML e XAML) fornecem uma experiência de total interação do usuário, incluindo interações padrão, efeitos físicos animados e comentários visuais. Se não precisar de suporte para interação personalizada, use esses controles internos.

Se os controles de plataforma não forem suficientes, as diretrizes a seguir para interação do usuário podem ajudá-lo a proporcionar uma experiência de interação envolvente e imersiva, consistente entre os modos de entrada. Essas diretrizes têm como foco principal a entrada por toque, mas elas ainda são relevante para entrada por touchpad, mouse, teclado e caneta.

Exemplos: Veja essa funcionalidade em ação em nossos exemplos de aplicativos.

Exemplo de personalização da interação do usuário, do início ao fim

Entrada: amostra de manipulação de eventos de ponteiros DOM

Entrada: exemplo de manipulações e gestos (JavaScript)

Entrada: amostra de gestos no Windows 8

Objetivo: aprender a ouvir, manipular e processar gestos estáticos usando entrada de toque, mouse, interações de caneta e eventos de gestos de Tempo de Execução do Windows.

Pré-requisitos

Reveja Guia de Início Rápido: Ponteiros e Guia de Início Rápido: Manipulações e Gestos DOM.

Nós supomos que você possa criar um aplicativo básico em JavaScript que use o modelo da Biblioteca do Windows para JavaScript.

Para completar este tutorial, você precisa:

Tempo para conclusão: 30 minutos.

O que são eventos de gesto?

Um gesto é um ato ou movimento físico executado no ou pelo dispositivo de entrada (um ou mais dedos em uma superfície de toque, caneta digitalizadora, mouse, etc.). Essas interações naturais são mapeadas para operações de elementos no sistema e no seu aplicativo. Para saber mais, veja Gestos, manipulações e interações.

A tabela a seguir identifica os gestos estáticos abordados neste Guia de Início Rápido.

GestoDescrição
Tocar/dar um toque duploGesto de tocar

Um único contato é liberado ou encerrado imediatamente.

Tocar em um elemento invoca sua ação primária.

Um toque duplo significa dois toques em rápida sucessão e pode ser manipulado conforme seu aplicativo necessitar.

  • Estado de entrada: um contato detectado dentro do retângulo delimitador de um objeto.
  • Movimento: nenhum.
  • Estado de saída: contato liberado ou encerrado.
Pressionar e segurar/soltarGesto de pressionar e segurar

Um único contato não se move enquanto um limite de tempo não é cruzado.

Pressionar e segurar faz com que informações detalhadas ou elementos visuais de ensino (por exemplo, uma dica de ferramenta ou um menu de contexto) sejam exibidos sem o compromisso de executar uma ação.

O gesto de soltar está estreitamente associado ao gesto de pressionar e segurar. O evento de soltar é disparado quando o gesto de pressionar e segurar é liberado.

  • Estado de entrada: um contato detectado dentro do retângulo delimitador de um objeto.
  • Movimento: nenhum.
  • Estado de saída: contato liberado ou encerrado.

Para saber mais sobre esses gestos e como eles se relacionam à linguagem de toque do Windows, veja Design da interação por toque.

 

Importante  Se você implementar seu próprio suporte para interação, tenha em mente que os usuários esperam uma experiência intuitiva que envolva a interação direta com os elementos de interface do usuário do seu aplicativo. Recomendamos que você modele as interações personalizadas nas bibliotecas de controles de plataforma (HTML e XAML) para manter tudo consistente e detectável. Os controles nessas bibliotecas fornecem uma experiência de interação completa para o usuário, incluindo interações padrão, efeitos físicos animados, comentários visuais e acessibilidade. Crie interações personalizadas somente se houver uma exigência clara e bem definida e se nenhuma das interações básicas suportar o seu cenário.

 

Criar a interface do usuário

Este exemplo é um aplicativo de perguntas e respostas básicas. Um quadrado (inputBox) atua como objeto de destino para detectar e processar a entrada do ponteiro e de gestos estáticos. Todas as perguntas, dicas e respostas são exibidas dentro desse objeto.

O aplicativo fornece a seguinte funcionalidade de interação do usuário:

  • Dar um toque duplo: inicia e para as perguntas e o timer do aplicativo.
  • Tocar: movimenta as perguntas em ciclos.
  • Pressionar e segurar: exibe um conjunto de dicas para a pergunta atual, com uma nova dica exibida dentro de poucos segundos, enquanto o contato permanece inoperante. Esse comportamento de interação adere às Diretrizes para comentários visuais e às recomendações da linguagem de toque que declaram que o gesto de pressionar e segurar seja limitado à exibição da interface do usuário informacional.
  • Soltar (ou a liberação de pressionar e segurar): exibe um pop-up quando o contato é suspenso que pergunta ao usuário se ele quer responder. Novamente, aderimos às recomendações do menu de contexto nas Diretrizes para comentários visuais e de linguagem de toque do Windows. Observação  Para manter o foco no código de manipulação de gestos, a leitura de dados de perguntas e respostas em um arquivo XML e algumas funcionalidades de aplicativos e interfaces do usuário não são totalmente implementados.  

Este é o HTML do exemplo.

<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 são as CSS (Folhas de Estilos em Cascata) para este exemplo.

Observação  Eventos de ponteiro não são disparados durante uma interação de panorâmica ou zoom. Você pode desabilitar o movimento panorâmico e a aplicação de zoom em uma região por meio das propriedades CSS msTouchAction, overflow e -ms-content-zooming.

 

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 o aplicativo

Inicialize o objeto de pergunta e resposta.

Aqui declaramos variáveis globais e obtemos referências aos objetos da interface do usuário.

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();
}

Em seguida, posicionamos a interface do usuário de perguntas e respostas e configuramos o objeto de interação para processar os dados de perguntas e respostas do arquivo XML. Os detalhes de dados XML para este exemplo podem ser revistos na listagem completa ao final deste tópico.

// 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);
}

Configure o reconhecedor de gestos.

Aqui nós configuramos a manipulação de interações.

Na maioria dos casos, recomendamos que você obtenha informações sobre o ponteiro por meio do argumento de eventos dos manipuladores de eventos do ponteiro em sua estrutura de idioma escolhida.

Se o argumento não representar os detalhes de ponteiro exigidos pelo seu aplicativo, você poderá obter acesso a dados de ponteiro estendidos do argumento do evento por meio dos métodos getCurrentPoint e getIntermediatePoints ou das propriedades currentPoint e intermediatePoints. Nós recomendamos o uso dos métodos getCurrentPoint e getIntermediatePoints pois você pode especificar o contexto dos dados de ponteiro.

Dica  Para este exemplo, há apenas um objeto associado a um reconhecedor de gestos. Se o seu aplicativo tiver uma grande quantidade de objetos que podem ser manipulados (como um quebra-cabeça), considere criar dinamicamente um reconhecedor de gestos apenas quando a entrada do ponteiro é detectada em um objeto de destino. O reconhecedor de gestos pode ser destruído quando a manipulação é concluída (veja um exemplo em Entrada: exemplo de gestos instanciáveis). Para evitar a sobrecarga da criação e destruição de reconhecedores de gestos, crie um pequeno pool de reconhecedores de gestos na inicialização e atribua-os dinamicamente conforme necessário.

 

O objeto do processador de entrada inclui o reconhecedor de gestos (gr) que ouve e manipula todos os eventos de ponteiro e de gesto. A interface do usuário de perguntas e respostas é gerenciada pelos manipuladores de eventos dos reconhecedores 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 o gerenciador de dicas, que exibe uma série de dicas com base na pergunta atual durante o gesto de pressionar e segurar.

// 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
    });
})();

Confira os Tópicos relacionados no final desta página para acessar links para exemplos mais complexos.

Exemplo completo

Veja Código completo de gestos estáticos.

Resumo e próximas etapas

Neste Guia de início rápido, você aprendeu a manipular eventos de gestos básicos em aplicativos da Windows Store em JavaScript.

O reconhecimento de gestos básicos e os eventos de ponteiro são úteis para o gerenciamento de interações simples, como tocar, dar um toque duplo, pressionar e segurar e soltar.

Veja Entrada: exemplo de gestos instanciáveis para obter um exemplo mais complexo de manipulação de gestos.

Observação  Este exemplo não adere às diretrizes para linguagem de toque do Windows no que se refere a interações personalizadas. Alguns dos gestos estáticos foram redefinidos para fins instrutivos.

 

Para fazer manipulações mais sofisticadas (como deslizar, passar o dedo, virar, pinçar e ampliar) para oferecer uma experiência de manipulação totalmente personalizada ao usuário, veja Guia de início rápido: gestos de manipulação.

Para saber mais sobre a linguagem de toque do Windows, veja Design da interação por toque.

Tópicos relacionados

Desenvolvedores

Respondendo à interação do usuário

Desenvolvendo aplicativos da Windows Store (JavaScript e HTML)

Guia de início rápido: ponteiros

Guia de Início Rápido: Manipulações e Gestos DOM

Guia de início rápido: gestos de manipulação

Designers

Design de interação por toque