Desenvolvimento da Web multitoque

Introdução

Dispositivos móveis, como smartphones e tablets, geralmente têm uma tela sensível ao toque capacitiva para capturar interações feitas com os dedos do usuário. À medida que a Web para dispositivos móveis evolui para possibilitar aplicativos cada vez mais sofisticados, os desenvolvedores precisam de uma maneira de lidar com esses eventos. Por exemplo, quase todos os jogos de ritmo acelerado exigem que o jogador pressione vários botões ao mesmo tempo, o que, no contexto de uma tela touchscreen, implica multitoque.

A Apple introduziu a API de eventos de toque no iOS 2.0. O Android vem alcançando esse padrão padrão e diminuindo a lacuna. Recentemente, um grupo de trabalho do W3C se reuniu para trabalhar nesta especificação de eventos de toque.

Neste artigo, vou me aprofundar na API de eventos de toque fornecida por dispositivos iOS e Android, bem como no Chrome para computador em hardware com suporte a toque, e mostrar quais tipos de aplicativos você pode criar, apresentar algumas práticas recomendadas e abordar técnicas úteis que facilitam o desenvolvimento de aplicativos com recursos de toque.

Eventos de toque

Três eventos de toque básicos são descritos na especificação e implementados amplamente em dispositivos móveis:

  • touchstart: um dedo é colocado em um elemento DOM.
  • touchmove: um dedo é arrastado por um elemento DOM.
  • touchend: um dedo é removido de um elemento DOM.

Cada evento de toque inclui três listas de toques:

  • touches: uma lista de todos os dedos atualmente na tela.
  • targetTouches: uma lista de dedos no elemento DOM atual.
  • changedTouches: uma lista de dedos envolvidos no evento atual. Por exemplo, em um evento touchend, esse será o dedo que foi removido.

Essas listas consistem em objetos que contêm informações de toque:

  • Identificador: um número que identifica exclusivamente o dedo atual na sessão de toque.
  • target: o elemento DOM que foi o destino da ação.
  • Coordenadas do cliente/da página/da tela: onde a ação ocorreu na tela.
  • Coordenada de raio e rotationAngle: descrevem a elipse que se aproxima da forma do dedo.

Apps com recursos de toque

Os eventos touchstart, touchmove e touchend oferecem um conjunto de recursos rico o suficiente para oferecer suporte a quase qualquer tipo de interação baseada em toque, incluindo todos os gestos multitoque usuais, como zoom de pinça, rotação e assim por diante.

Este snippet permite arrastar um elemento do DOM usando o toque de um dedo:

var obj = document.getElementById('id');
obj.addEventListener('touchmove', function(event) {
  // If there's exactly one finger inside this element
  if (event.targetTouches.length == 1) {
    var touch = event.targetTouches[0];
    // Place element where the finger is
    obj.style.left = touch.pageX + 'px';
    obj.style.top = touch.pageY + 'px';
  }
}, false);

Confira abaixo um exemplo que mostra todos os toques atuais na tela. Ele é útil para ter uma ideia da capacidade de resposta do dispositivo.

Rastreamento de dedos.
// Setup canvas and expose context via ctx variable
canvas.addEventListener('touchmove', function(event) {
  for (var i = 0; i < event.touches.length; i++) {
    var touch = event.touches[i];
    ctx.beginPath();
    ctx.arc(touch.pageX, touch.pageY, 20, 0, 2*Math.PI, true);
    ctx.fill();
    ctx.stroke();
  }
}, false);

Demonstrações

Várias demonstrações interessantes em multitoque já estão disponíveis, como esta demonstração de desenho baseado em tela (em inglês) de Paul Ireland, entre outros.

Captura de tela do desenho

E Browser Ninja, uma demonstração de tecnologia que é um clone do Fruit Ninja usando transformações e transições CSS3, além de tela:

Navegador Ninja

Práticas recomendadas

Impedir zoom

As configurações padrão não funcionam muito bem para multitoque, já que os gestos e deslizes geralmente são associados ao comportamento do navegador, como rolagem e zoom.

Para desativar o zoom, configure a janela de visualização para que ela não seja dimensionável pelo usuário usando a seguinte meta tag:

<meta name="viewport" 
  content="width=device-width, initial-scale=1.0, user-scalable=no>

Confira este artigo sobre HTML5 para dispositivos móveis para mais informações sobre como definir a viewport.

Impedir a rolagem

Alguns dispositivos móveis têm comportamentos padrão para a movimentação de toque, como o efeito clássico de rolagem do iOS, que faz com que a visualização retorne quando a rolagem excede os limites do conteúdo. Isso é confuso em muitos aplicativos multitoque e pode ser desativado facilmente:

document.body.addEventListener('touchmove', function(event) {
  event.preventDefault();
}, false); 

Renderizar com cuidado

Se você estiver escrevendo um aplicativo multitoque que envolva gestos complexos com vários dedos, tenha cuidado ao reagir a eventos de toque, já que você estará processando muitos de uma só vez. Considere o exemplo na seção anterior que desenha todos os toques na tela. Você pode desenhar assim que houver uma entrada de toque:

canvas.addEventListener('touchmove', function(event) {
  renderTouches(event.touches);
}, false);

Mas essa técnica não é escalonada com o número de dedos na tela. Em vez disso, você pode rastrear todos os dedos e renderizar em loop para conseguir um desempenho muito melhor:

var touches = []
canvas.addEventListener('touchmove', function(event) {
  touches = event.touches;
}, false);

// Setup a 60fps timer
timer = setInterval(function() {
  renderTouches(touches);
}, 15);

Usar targetTouches e changedTouches

Lembre-se de que event.touches é uma matriz de TODOS os dedos em contato com a tela, não apenas os que estão no destino do elemento do DOM. Talvez seja muito mais útil usar event.targetTouches ou event.changedTouches.

Por fim, como você está desenvolvendo para dispositivos móveis, é importante conhecer as práticas recomendadas gerais para dispositivos móveis, que são abordadas no artigo de Eric Bidelman e neste documento do W3C.

Suporte do dispositivo

Infelizmente, as implementações de eventos de toque variam muito em termos de integridade e qualidade. Escrevi um script de diagnóstico que mostra algumas informações básicas sobre a implementação da API de toque, incluindo quais eventos têm suporte e a resolução de disparos de movimentos de toque. Testei o Android 2.3.3 no hardware Nexus One e Nexus S, o Android 3.0.1 no Xoom e o iOS 4.2 no iPad e no iPhone.

Em resumo, todos os navegadores testados oferecem suporte aos eventos touchstart, touchend e touchmove.

A especificação fornece três eventos de toque adicionais, mas nenhum navegador testado oferece suporte a eles:

  • touchenter: um dedo em movimento entra em um elemento DOM.
  • touchleave: um dedo em movimento deixa um elemento DOM.
  • touchcancel: um toque é interrompido (específico da implementação).

Dentro de cada lista de toques, os navegadores testados também fornecem as listas de toques touches, targetTouches e changedTouches. No entanto, nenhum navegador testado oferece suporte a radiusX, radiusY ou rotationAngle, que especificam a forma do dedo tocando a tela.

Durante um toque, os eventos são acionados cerca de 60 vezes por segundo em todos os dispositivos testados.

Android 2.3.3 (Nexus)

No navegador Android Gingerbread (testado no Nexus One e Nexus S), não há suporte para multitoque. Esse é um problema conhecido.

Android 3.0.1 (Xoom)

No navegador Xoom, há suporte básico para multitoque, mas ele só funciona em um único elemento DOM. O navegador não responde corretamente a dois toques simultâneos em diferentes elementos DOM. Em outras palavras, o seguinte reagerá a dois toques simultâneos:

obj1.addEventListener('touchmove', function(event) {
  for (var i = 0; i < event.targetTouches; i++) {
    var touch = event.targetTouches[i];
    console.log('touched ' + touch.identifier);
  }
}, false);

Mas os seguintes não serão:

var objs = [obj1, obj2];
for (var i = 0; i < objs.length; i++) {
  var obj = objs[i];
  obj.addEventListener('touchmove', function(event) {
    if (event.targetTouches.length == 1) {
      console.log('touched ' + event.targetTouches[0].identifier);
    }
  }, false);
}

iOS 4.x (iPad, iPhone)

Os dispositivos iOS oferecem suporte total ao multitoque, são capazes de rastrear vários dedos e oferecem uma experiência de toque muito responsiva no navegador.

Ferramentas para desenvolvedores

No desenvolvimento para dispositivos móveis, geralmente é mais fácil começar a criar protótipos no computador e depois lidar com as partes específicas para dispositivos móveis nos dispositivos que você pretende oferecer suporte. O multitoque é um desses recursos difíceis de testar no PC, já que a maioria deles não tem entrada de toque.

Ter que testar em dispositivos móveis pode prolongar seu ciclo de desenvolvimento, já que cada mudança precisa ser enviada para um servidor e depois carregada no dispositivo. Depois, quando estiver em execução, não será possível depurar o aplicativo, já que tablets e smartphones não têm ferramentas para desenvolvedores da Web.

Uma solução para esse problema é simular eventos de toque na máquina de desenvolvimento. Para toques únicos, os eventos de toque podem ser simulados com base em eventos do mouse. Os eventos multitoque podem ser simulados se você tiver um dispositivo com entrada por toque, como um MacBook moderno da Apple.

Eventos de um toque

Se você quiser simular eventos de toque único no computador, o Chrome oferece a emulação de eventos de toque nas ferramentas para desenvolvedores. Abra as ferramentas para desenvolvedores, selecione a engrenagem de configurações, depois "Substituições" ou "Emulação" e ative a opção "Emular eventos de toque".

Para outros navegadores, teste o Phantom Limb, que simula eventos de toque nas páginas e facilita a inicialização.

Há também o plug-in jQuery Touchable (em inglês), que unifica eventos de toque e mouse em todas as plataformas.

Eventos multitoque

Para permitir que seu aplicativo da Web multitoque funcione no navegador no trackpad multitoque (como um Apple MacBook ou MagicPad), criei o polyfill MagicTouch.js. Ele captura eventos de toque do trackpad e os transforma em eventos de toque compatíveis com o padrão.

  1. Faça o download e instale o plug-in NPAPI do npTuioClient em ~/Library/Internet Plug-Ins/.
  2. Faça o download do app TongSeng TUIO para o MagicPad do Mac e inicie o servidor.
  3. Faça o download de MagicTouch.js, uma biblioteca JavaScript para simular eventos de toque compatíveis com as especificações com base em callbacks do npTuioClient.
  4. Inclua o script magictouch.js e o plug-in npTuioClient no aplicativo da seguinte maneira:
<head>
  ...
  <script src="/path/to/magictouch.js"></script>
</head>

<body>
  ...
  <object id="tuio" type="application/x-tuio" style="width: 0px; height: 0px;">
    Touch input plugin failed to load!
  </object>
</body>

Talvez seja necessário ativar o plug-in.

Uma demonstração ao vivo com magictouch.js está disponível em paulirish.com/demo/multi:

Testei essa abordagem apenas com o Chrome 10, mas ela deve funcionar em outros navegadores modernos com apenas pequenas alterações.

Se o computador não tiver entrada multitoque, você poderá simular eventos de toque usando outros rastreadores TUIO, como o reacTIVision. Para mais informações, consulte a página do projeto TUIO.

Seus gestos podem ser idênticos aos gestos multitoque no nível do SO. No OS X, é possível configurar eventos do sistema acessando o painel de preferências do trackpad em Preferências do Sistema.

À medida que os recursos multitoque recebem mais suporte em navegadores para dispositivos móveis, fico muito feliz em ver novos aplicativos da Web aproveitando ao máximo essa API rica.