Toque e mouse

Juntos de novo pela primeira vez

Introdução

Por quase trinta anos, as experiências de computação em computadores desktop se concentraram em um teclado e um mouse ou trackpad como os principais dispositivos de entrada do usuário. Na última década, no entanto, os smartphones e tablets trouxeram um novo paradigma de interação: o toque. Com o lançamento das máquinas Windows 8 com tela touchscreen e, agora, com o lançamento do incrível Chromebook Pixel, o toque está se tornando parte da experiência esperada em computadores. Um dos maiores desafios é criar experiências que funcionem não apenas em dispositivos de toque e mouse, mas também em dispositivos em que o usuário use os dois métodos de entrada, às vezes simultaneamente.

Este artigo vai ajudar você a entender como os recursos de toque são integrados ao navegador, como integrar esse novo mecanismo de interface aos seus apps atuais e como o toque pode funcionar bem com a entrada do mouse.

O estado do toque na plataforma da Web

O iPhone foi a primeira plataforma popular a ter APIs de toque dedicadas integradas ao navegador da Web. Vários outros fornecedores de navegadores criaram interfaces de API semelhantes para serem compatíveis com a implementação do iOS, que agora é descrita pela especificação "Eventos de toque versão 1". Os eventos de toque são compatíveis com o Chrome e o Firefox em computadores, com o Safari no iOS, com o Chrome e o navegador Android no Android, além de outros navegadores para dispositivos móveis, como o Blackberry.

Meu colega Boris Smus escreveu um ótimo tutorial do HTML5Rocks sobre eventos de toque (link em inglês) que ainda é uma boa maneira de começar se você nunca tiver analisado eventos de toque antes. Se você nunca trabalhou com eventos de toque, leia esse artigo antes de continuar. Pode ir, eu espero.

Terminou? Agora que você tem uma base sobre eventos de toque, o desafio de criar interações por toque é que elas podem ser bem diferentes dos eventos de mouse (e de trackpad e trackball que emulam mouse). Embora as interfaces de toque geralmente tentem emular mouses, essa emulação não é perfeita nem completa. Você realmente precisa trabalhar com os dois estilos de interação e talvez precise oferecer suporte a cada interface de forma independente.

O mais importante: o usuário pode ter toque e um mouse

Muitos desenvolvedores criaram sites que detectam estaticamente se um ambiente é compatível com eventos de toque e, em seguida, presumem que só precisam oferecer suporte a eventos de toque (e não de mouse). Essa é uma suposição incorreta. A presença de eventos de toque não significa que o usuário está usando principalmente esse dispositivo de entrada por toque. Dispositivos como o Chromebook Pixel e alguns laptops com Windows 8 agora são compatíveis com os métodos de entrada por mouse e toque, e mais dispositivos terão essa compatibilidade em breve. Nesses dispositivos, é bastante natural que os usuários usem o mouse e a tela touchscreen para interagir com os aplicativos. Portanto, "oferece suporte ao toque" não é o mesmo que "não precisa de suporte ao mouse". Não pense no problema como "Preciso escrever dois estilos de interação diferentes e alternar entre eles". Você precisa pensar em como as duas interações vão funcionar juntas e de forma independente. No meu Chromebook Pixel, uso o trackpad com frequência, mas também toco na tela. No mesmo aplicativo ou página, faço o que parece mais natural no momento. Por outro lado, alguns usuários de laptops com touch screen raramente usam esse recurso. Portanto, a presença da entrada por toque não deve desativar ou prejudicar o controle do mouse.

Infelizmente, pode ser difícil saber se o ambiente do navegador de um usuário é compatível com entrada por toque. O ideal é que um navegador em uma máquina desktop sempre indique suporte a eventos de toque para que uma tela touch screen possa ser conectada a qualquer momento (por exemplo, se uma tela touch screen conectada por um KVM for disponibilizada). Por todos esses motivos, seus aplicativos não devem tentar alternar entre toque e mouse. Basta oferecer suporte para ambos.

Compatibilidade com mouse e toque juntos

#1 - Clicar e tocar: a ordem "natural" das coisas

O primeiro problema é que as interfaces de toque normalmente tentam emular cliques do mouse, já que elas precisam funcionar em aplicativos que antes só interagiam com eventos do mouse. Você pode usar isso como um atalho, porque os eventos de "clique" vão continuar sendo acionados, seja com um clique do mouse ou um toque na tela. No entanto, há alguns problemas com esse atalho.

Primeiro, é preciso ter cuidado ao projetar interações de toque mais avançadas: quando o usuário usa um mouse, ele responde por um evento de clique, mas quando o usuário toca na tela, ocorrem eventos de toque e de clique. Para um único clique, a ordem dos eventos é:

  1. touchstart
  2. touchmove
  3. touchend
  4. mouseover
  5. mousemove
  6. mousedown
  7. mouseup
  8. clique

Isso significa que, se você estiver processando eventos de toque, como touchstart, precisa garantir que não vai processar também o evento mousedown e/ou click correspondente. Se você puder cancelar os eventos de toque (chamar preventDefault() dentro do manipulador de eventos), nenhum evento de mouse será gerado para o toque. Uma das regras mais importantes dos manipuladores de toque é:

No entanto, isso também impede outros comportamentos padrão do navegador (como a rolagem), embora geralmente você esteja lidando com o evento de toque inteiramente no seu gerenciador e QUEIRA desativar as ações padrão. Em geral, você vai querer processar e cancelar todos os eventos de toque ou evitar ter um gerenciador para esse evento.

Em segundo lugar, quando um usuário toca em um elemento de uma página da Web em um dispositivo móvel, as páginas que não foram projetadas para interação em dispositivos móveis têm um atraso de pelo menos 300 milissegundos entre o evento touchstart e o processamento de eventos do mouse (mousedown). Isso pode ser feito usando o Chrome. Ative a opção Emular eventos de toque nas Ferramentas para Desenvolvedores do Chrome para testar interfaces de toque em um sistema sem toque.

Esse atraso permite que o navegador determine se o usuário está realizando outro gesto, em especial o zoom com toque duplo. Obviamente, isso pode ser problemático nos casos em que você quer ter uma resposta instantânea ao toque de um dedo. Há um trabalho em andamento para tentar limitar os cenários em que esse atraso ocorre automaticamente.

Google Chrome para Android Navegador Android Opera Mobile para Android) Firefox para Android Safari para iOS
Janela de visualização não escalonável Sem atraso 300 ms 300 ms Sem atraso 300 ms
Sem janela de visualização 300 ms 300 ms 300 ms 300 ms 300 ms

A primeira e mais fácil maneira de evitar esse atraso é "informar" ao navegador para dispositivos móveis que sua página não vai precisar de zoom, o que pode ser feito usando uma janela de visualização fixa, por exemplo, inserindo na página:

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

Isso nem sempre é apropriado, é claro. Isso desativa o zoom com gesto de pinça, que pode ser necessário por motivos de acessibilidade. Portanto, use com moderação, se for o caso. Se você desativar o dimensionamento do usuário, talvez seja necessário fornecer outra maneira de aumentar a legibilidade do texto no aplicativo. Além disso, para o Chrome em dispositivos de classe de computador que oferecem suporte ao toque e outros navegadores em plataformas móveis quando a página tem janelas de visualização que não são escalonáveis, esse atraso não se aplica.

Nº 2: os eventos mousemove não são acionados por toque

É importante observar que a emulação de eventos de mouse em uma interface de toque geralmente não se estende à emulação de eventos mousemove. Portanto, se você criar um controle acionado por mouse que use eventos mousemove, ele provavelmente não vai funcionar com um dispositivo de toque, a menos que você também adicione gerenciadores touchmove.

Os navegadores geralmente implementam de forma automática a interação adequada para interações por toque nos controles HTML. Por exemplo, os controles de intervalo do HTML5 funcionam normalmente quando você usa interações por toque. No entanto, se você tiver implementado seus próprios controles, é provável que eles não funcionem em interações do tipo clicar e arrastar. Na verdade, algumas bibliotecas usadas com frequência (como jQueryUI) ainda não oferecem suporte nativo a interações por toque dessa maneira (embora para jQueryUI, existam várias correções de monkey-patch para esse problema). Esse foi um dos primeiros problemas que encontrei ao fazer upgrade do meu aplicativo Web Audio Playground para funcionar com toque: os controles deslizantes eram baseados em jQueryUI, então não funcionavam com interações de clicar e arrastar. Mudei para os controles de intervalo HTML5 e eles funcionaram. Como alternativa, eu poderia ter adicionado manipuladores touchmove para atualizar os controles deslizantes, mas há um problema com isso…

Nº 3: Touchmove e MouseMove não são a mesma coisa

Uma armadilha que já vi alguns desenvolvedores caírem é fazer com que os manipuladores touchmove e mousemove chamem os mesmos caminhos de código. O comportamento desses eventos é muito parecido, mas com diferenças sutis. Em especial, os eventos de toque sempre têm como alvo o elemento em que o toque COMEÇOU, enquanto os eventos de mouse têm como alvo o elemento que está sob o cursor do mouse. É por isso que temos eventos mouseover e mouseout, mas não temos eventos touchover e touchout correspondentes, apenas touchend.

A maneira mais comum de isso acontecer é se você remover (ou realocar) o elemento que o usuário começou a tocar. Por exemplo, imagine um carrossel de imagens com um gerenciador de toque em todo o carrossel para oferecer suporte ao comportamento de rolagem personalizado. Conforme as imagens disponíveis mudam, você remove alguns elementos <img> e adiciona outros. Se o usuário começar a tocar em uma dessas imagens e você a remover, o gerenciador (que está em um ancestral do elemento img) vai parar de receber eventos de toque (porque eles estão sendo enviados para um destino que não está mais na árvore). Vai parecer que o usuário está segurando o dedo em um lugar, mesmo que ele tenha se movido e, por fim, removido.

Para evitar esse problema, não remova elementos que tenham (ou tenham ancestrais que tenham) manipuladores de toque enquanto um toque estiver ativo. Como alternativa, a melhor orientação é, em vez de registrar manipuladores estáticos de touchend/touchmove, aguardar até receber um evento touchstart e, em seguida, adicionar manipuladores de touchmove/touchend/touchcancel ao destino do evento touchstart (e removê-los no final/cancelamento). Dessa forma, você vai continuar recebendo eventos para o toque, mesmo que o elemento de destino seja movido/removido. Você pode testar isso aqui: toque na caixa vermelha e, enquanto segura, pressione Esc para removê-la do DOM.

4: Toque e :hover

A metáfora do ponteiro do mouse separou a posição do cursor da seleção ativa, o que permitiu que os desenvolvedores usassem estados de passagem de cursor para ocultar e mostrar informações que poderiam ser relevantes para os usuários. No entanto, a maioria das interfaces de toque atuais não detecta um dedo "passando" sobre um destino. Portanto, fornecer informações semanticamente importantes (por exemplo, um pop-up "o que é este controle?") com base na passagem do dedo não é uma boa ideia, a menos que você também ofereça uma maneira de acessar essas informações que seja compatível com toque. É preciso ter cuidado ao usar o recurso de passar o cursor para transmitir informações aos usuários.

No entanto, a pseudoclasse CSS :hover PODE ser acionada por interfaces de toque em alguns casos. Tocar em um elemento o torna :active enquanto o dedo está pressionado, e ele também adquire o estado :hover. No Internet Explorer, o :hover só funciona enquanto o usuário mantém o dedo pressionado. Outros navegadores mantêm o :hover em vigor até o próximo toque ou movimento do mouse. Essa é uma boa abordagem para fazer com que os menus pop-out funcionem em interfaces de toque. Um efeito colateral de ativar um elemento é que o estado :hover também é aplicado. Exemplo:

<style>
img ~ .content {
  display:none;
}

img:hover ~ .content {
  display:block;
}
</style>

<img src="/awesome.png">
<div class="content">This is an awesome picture of me</div>

Quando outro elemento é tocado, o elemento anterior deixa de estar ativo, e o estado de passagem do cursor desaparece, como se o usuário estivesse usando um ponteiro do mouse e o afastasse do elemento. Você pode colocar o conteúdo em um elemento <a> para que ele também seja uma parada de tabulação. Assim, o usuário pode alternar as informações extras com um clique ou passagem do mouse, um toque ou uma tecla, sem precisar de JavaScript. Fiquei agradavelmente surpreso quando comecei a trabalhar para que meu Web Audio Playground funcionasse bem com interfaces de toque, porque meus menus pop-out já funcionavam bem com toque, já que eu tinha usado esse tipo de estrutura.

O método acima funciona bem para interfaces baseadas em ponteiro do mouse e interfaces de toque. Isso é diferente de usar atributos "title" ao passar o cursor, que NÃO aparecem quando o elemento é ativado:

<img src="/awesome.png" title="this doesn't show up in touch">

Nº 5: precisão do toque x do mouse

Embora os mouses tenham uma dissociação conceitual da realidade, eles são extremamente precisos, já que o sistema operacional geralmente rastreia a precisão exata de pixels do cursor. Por outro lado, os desenvolvedores de dispositivos móveis aprenderam que os toques em uma tela touch screen não são tão precisos, principalmente devido ao tamanho da área de superfície do dedo quando em contato com a tela (e em parte porque os dedos obstruem a tela).

Muitas pessoas e empresas fizeram pesquisas extensas de usuários sobre como projetar aplicativos e sites que acomodam a interação baseada em dedos, e muitos livros foram escritos sobre o assunto. O conselho básico é aumentar o tamanho das áreas de toque aumentando o padding e reduzir a probabilidade de toques incorretos aumentando a margem entre os elementos. As margens não são incluídas no processamento de detecção de hit de eventos de toque e clique, mas o padding é. Uma das principais correções que tive que fazer no Web Audio Playground foi aumentar o tamanho dos pontos de conexão para que pudessem ser tocados com mais facilidade e precisão.

Muitos fornecedores de navegadores que lidam com interfaces baseadas em toque também introduziram lógica no navegador para ajudar a segmentar o elemento correto quando um usuário toca na tela e reduzir a probabilidade de cliques incorretos, embora isso geralmente corrija apenas eventos de clique, não movimentos (embora o Internet Explorer pareça modificar os eventos mousedown/mousemove/mouseup também).

#6: mantenha os manipuladores de toque contidos ou eles vão causar instabilidade na rolagem

Também é importante manter os manipuladores de toque confinados apenas aos elementos em que são necessários. Os elementos de toque podem ter uma largura de banda muito alta, por isso é importante evitar manipuladores de toque em elementos de rolagem, já que o processamento pode interferir nas otimizações do navegador para uma rolagem de toque rápida e sem instabilidade. Os navegadores mais recentes tentam rolar em uma linha de execução da GPU, mas isso é impossível se eles precisarem verificar primeiro com o JavaScript se cada evento de toque será processado pelo app. Confira um exemplo desse comportamento.

Uma orientação para evitar esse problema é garantir que, se você estiver lidando com eventos de toque apenas em uma pequena parte da interface, anexe os manipuladores de toque somente ali (não, por exemplo, no <body> da página). Em resumo, limite o escopo dos manipuladores de toque o máximo possível.

#7: Multitoque

O último desafio interessante é que, embora tenhamos nos referido a isso como interface do usuário por toque, quase universalmente o suporte é para multitoque, ou seja, as APIs fornecem mais de uma entrada por toque por vez. Ao começar a oferecer suporte ao toque nos seus aplicativos, considere como vários toques podem afetar o aplicativo.

Se você cria apps principalmente para uso com mouse, está acostumado a usar no máximo um ponto de cursor, já que os sistemas geralmente não são compatíveis com vários cursores de mouse. Para muitos aplicativos, você só vai mapear eventos de toque para uma única interface de cursor, mas a maioria dos hardwares que vimos para entrada por toque em computadores pode lidar com pelo menos duas entradas simultâneas, e a maioria dos novos hardwares parece oferecer suporte a pelo menos cinco entradas simultâneas. Para desenvolver um teclado de piano na tela, é claro que você precisa de suporte para várias entradas por toque simultâneas.

As APIs de toque do W3C implementadas atualmente não têm uma API para determinar quantos pontos de toque o hardware aceita. Portanto, você terá que usar sua melhor estimativa para quantos pontos de toque seus usuários vão querer ou, é claro, prestar atenção em quantos pontos de toque você vê na prática e se adaptar. Por exemplo, em um aplicativo de piano, se você nunca vê mais de dois pontos de toque, pode ser interessante adicionar uma interface de "acordes". A API PointerEvents tem uma API para determinar os recursos do dispositivo.

Retocando

Esperamos que este artigo tenha ajudado você a entender os desafios comuns na implementação de interações de toque e mouse. Mais importante do que qualquer outro conselho, é claro, é que você precisa testar seu app em ambientes de dispositivos móveis, tablets e computadores com mouse e toque combinados. Se você não tiver um hardware com toque e mouse, use a opção Emular eventos de toque do Chrome para testar os diferentes cenários.

Não é apenas possível, mas relativamente fácil, seguindo estas orientações, criar experiências interativas envolventes que funcionam bem com entrada por toque, entrada do mouse e até mesmo os dois estilos de interação ao mesmo tempo.