Toque e mouse

Juntos de novo pela primeira vez

Introdução

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

Este artigo o ajudará a entender como os recursos de toque são incorporados ao navegador, como você pode integrar esse novo mecanismo de interface em seus aplicativos existentes e como o toque pode funcionar bem com a entrada do mouse.

O estado de contato na plataforma 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 criadas para serem compatíveis com a implementação do iOS, que agora é descrita pela especificação"Eventos de toque versão 1" (em inglês). Os eventos de toque são compatíveis com o Chrome e o Firefox no computador, o Safari no iOS e o Chrome e o navegador do 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, que ainda é uma boa maneira de começar. Se você nunca trabalhou com eventos de toque, leia esse artigo antes de continuar. Vamos lá, eu espero.

Terminou? Agora que você tem um embasamento básico em eventos de toque, o desafio de programar interações ativadas por toque é que as interações de toque podem ser bem diferentes dos eventos de mouse (e trackpad e trackpad de emulação de mouse). Embora as interfaces de toque normalmente tentem emular mouses, essa emulação não é perfeita ou completa. Você realmente precisa trabalhar com os dois estilos de interação e pode ter que oferecer suporte a cada interface de forma independente.

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

Muitos desenvolvedores criaram sites que detectam estaticamente se um ambiente oferece suporte a eventos de toque e supõem que só precisam oferecer suporte a eventos de toque (e não a do mouse). Agora essa é uma suposição incorreta. Em vez disso, o fato de os eventos de toque estarem presentes não significa que o usuário está usando o dispositivo de entrada por toque principalmente. Dispositivos como o Chromebook Pixel e alguns laptops com Windows 8 agora oferecem suporte aos métodos de entrada por mouse e toque, e muito mais em breve. Nesses dispositivos, é bem natural que os usuários usem o mouse e a tela sensível ao toque para interagir com aplicativos. Portanto, "oferece suporte ao toque" não é o mesmo que "não precisa de suporte para mouse". Você não pode pensar no problema como "Tenho que escrever dois estilos de interação diferentes e alternar entre eles", você precisa pensar em como ambas as interações funcionarão juntas e de forma independente. No meu Chromebook Pixel, uso o trackpad com frequência, mas também alcanço a tela e 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 tela touch raramente usam a tela sensível ao toque, portanto, a presença de entrada por toque não deve desativar ou prejudicar o controle do mouse.

Infelizmente, pode ser difícil saber se o ambiente do navegador do usuário é compatível com entrada por toque ou não. O ideal é que um navegador em um computador sempre indique suporte a eventos de toque para que uma tela touchscreen possa ser conectada a qualquer momento (por exemplo, se uma tela touchscreen conectada por meio de uma KVM for disponibilizada). Por todos esses motivos, seus aplicativos não devem tentar alternar entre toque e mouse; apenas ofereça suporte aos dois!

Compatibilidade com mouse e toque juntos

No 1 - Clicar e tocar - a ordem "natural" das coisas

O primeiro problema é que as interfaces de toque normalmente tentam emular cliques do mouse, obviamente, já que elas precisam funcionar em aplicativos que só interagiram com eventos de mouse antes. Você pode usá-la como um atalho, porque os eventos de "clique" continuarão a ser disparados, independentemente de o usuário ter clicado com o mouse ou tocado na tela. No entanto, esse atalho tem alguns problemas.

Primeiro, tenha cuidado ao projetar interações de toque mais avançadas: quando o usuário usar um mouse, ele responderá por um evento de clique, mas quando o usuário tocar na tela, eventos de toque e clique ocorrerão. Com um único clique, a ordem dos eventos é:

  1. touchstart
  2. movimento de toque
  3. Touchend
  4. mouseover
  5. mousemove
  6. mousedown
  7. mouseup
  8. click

Isso significa que, se você estiver processando eventos de toque como touchstart, também precisará processar o evento de mousedown e/ou clique 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 para gerenciadores de toque é:

No entanto, isso também impede outros comportamentos padrão do navegador (como rolagem), embora normalmente você lide com o evento de toque inteiramente no gerenciador e QUER desativar as ações padrão. Em geral, convém processar e cancelar todos os eventos de toque ou evitar ter um manipulador 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 móvel têm um atraso de pelo menos 300 milissegundos entre o evento de início de toque e o processamento de eventos de mouse (mousedown). Isso pode ser feito no Chrome. Por isso, ative a opção Emular eventos de toque nas Ferramentas para desenvolvedores do Chrome para testar as interfaces de toque em um sistema sem toque.

Esse atraso serve para permitir que o navegador tenha tempo para determinar se o usuário está realizando outro gesto, mais especificamente, aplicar zoom com dois toques. Obviamente, isso pode ser problemático nos casos em que você quer ter uma resposta instantânea ao toque do dedo. Há trabalho em andamento para tentar limitar os cenários em que esse atraso ocorre automaticamente.

Google Chrome para Android Navegador do 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
Nenhuma janela de visualização 300 ms 300 ms 300 ms 300 ms 300 ms

A primeira maneira, e a mais fácil de evitar esse atraso, é "informar" ao navegador para dispositivos móveis que a página não precisará de zoom. Para fazer isso, use uma janela de visualização fixa, por exemplo, inserindo o código na página:

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

Isso nem sempre é adequado, obviamente. O recurso desativa o zoom por gesto de pinça, que pode ser necessário por motivos de acessibilidade. Portanto, use esse recurso com moderação. Se você desativar o dimensionamento do usuário, convém fornecer outra maneira de aumentar a legibilidade do texto no seu aplicativo. Além disso, no Chrome em dispositivos desktop compatíveis com toque e em outros navegadores em plataformas móveis em que a página tem janelas de visualização não escalonáveis, esse atraso não se aplica.

2: os eventos de movimentação do mouse não são acionados pelo toque

É importante observar que, neste ponto, a emulação de eventos do mouse em uma interface de toque normalmente não se estende à emulação de eventos de movimentação do mouse. Portanto, se você criar um controle orientado por mouse que use eventos desse tipo, ele provavelmente não funcionará com um dispositivo de toque, a menos que você também adicione manipuladores de movimentos de toque especificamente.

Em geral, os navegadores implementam automaticamente a interação adequada para interações de toque nos controles HTML. Assim, por exemplo, os controles de intervalo HTML5 só funcionarão quando você usar interações de toque. No entanto, se você tiver implementado seus próprios controles, eles provavelmente não funcionarão 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 de toque dessa forma (embora, para jQueryUI, existam várias correções de monkey-patch para esse problema). Esse foi um dos primeiros problemas que encontrei ao atualizar meu aplicativo Web Audio Playground para funcionar com toque - os controles deslizantes eram baseados em jQueryUI, portanto, não funcionavam com interações de clicar e arrastar. Mudei para os controles de intervalo HTML5 e eles funcionaram. Como alternativa, é claro que eu poderia simplesmente ter adicionado gerenciadores touchmove para atualizar os controles deslizantes, mas há um problema com isso...

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

Uma armadilha que vi alguns desenvolvedores se enquadrarem é fazer com que os gerenciadores touchmove e mousemove chamem os mesmos caminhos de código. O comportamento desses eventos é muito próximo, mas ligeiramente diferente. Em particular, os eventos de toque sempre são direcionados ao elemento em que o toque INICIADO, enquanto os eventos de mouse segmentam o elemento que está atualmente sob o cursor do mouse. É por isso que temos eventos mouseover e mouseout, mas não há eventos de touchover e de touchout correspondentes, somente touchend.

A maneira mais comum que isso pode causar é se você remover (ou realocar) o elemento em 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. À medida que 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ê removê-la, seu gerenciador (que está em um antecedente do elemento img) simplesmente deixará de receber eventos de toque (porque eles estão sendo enviados para um alvo que não está mais na árvore). Parece que o usuário está segurando o dedo em um lugar, mesmo que tenha movido e finalmente o removido.

Obviamente, você pode evitar esse problema evitando remover elementos que tenham ou que tenham ancestrais que tenham) gerenciadores de toque enquanto um toque estiver ativo. Como alternativa, a melhor orientação é: em vez de registrar gerenciadores estáticos touchend/touchmove, aguardar até receber um evento touchstart e, em seguida, adicionar gerenciadores touchmove/touchend/touchcancel ao target do evento touchstart (e removê-los ao encerrar/cancelar). Dessa forma, você continuará a receber eventos para o toque, mesmo que o elemento de destino seja movido/removido. Para brincar com isso aqui, toque na caixa vermelha e pressione "Esc" para removê-lo do DOM.

No 4: toque e :Passe o mouse

A metáfora do ponteiro do mouse separou a posição do cursor da seleção ativa, e isso permitiu que os desenvolvedores usassem estados de passagem do cursor para ocultar e mostrar informações que possam ser pertinentes aos usuários. No entanto, a maioria das interfaces de toque no momento não detectam um dedo "passando o cursor" sobre um alvo. Portanto, fornecer informações semanticamente importantes (por exemplo, fornecer o pop-up "o que é esse controle?") com base na passagem do cursor não é aceito, a menos que você também ofereça uma maneira amigável de acessar essas informações. Você precisa ter cuidado ao usar o passar o cursor para transmitir informações aos usuários.

Curiosamente, porém, a pseudoclasse :hover do CSS PODE ser acionada por interfaces de toque em alguns casos. O toque em um elemento o torna :ativo enquanto o dedo está abaixado e também adquire o estado :hover. No Internet Explorer, o :hover só tem efeito enquanto o dedo do usuário está pressionado. Outros navegadores mantêm o :hover ativo até o próximo toque ou movimento do mouse. Essa é uma boa abordagem para fazer com que 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, ele não fica mais ativo, e o estado de passar cursor desaparece, como se o usuário estivesse usando um ponteiro do mouse e o tivesse movido para fora do elemento. Também é possível unir o conteúdo em um elemento <a> para torná-lo uma parada de tabulação. Assim, o usuário pode alternar as informações extras ao passar o cursor ou clicar, tocar ou pressionar uma tecla, sem precisar do JavaScript. Fiquei muito surpreso quando comecei a trabalhar para fazer com que meu Web Audio Playground funcionasse bem com interfaces de toque nas quais os menus suspensos já funcionavam bem, porque eu tinha usado esse tipo de estrutura.

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

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

No 5: precisão de toque x mouse

Embora os mouses tenham uma dissociação conceitual da realidade, eles são extremamente precisos, pois o sistema operacional subjacente geralmente rastreia a precisão exata de pixel do cursor. Os desenvolvedores de dispositivos móveis, por outro lado, descobriram que os toques do dedo em uma tela sensível ao toque não são tão precisos, principalmente devido ao tamanho da área da superfície do dedo quando está em contato com a tela e em parte porque seus dedos obstruem a tela.

Muitos indivíduos e empresas fizeram uma extensa pesquisa de usuários sobre como projetar aplicativos e sites que acomodam a interação baseada nos 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 estão incluídas no processamento da detecção de hits de eventos de toque e clique, ao contrário do preenchimento. Uma das principais correções que eu tive que fazer no Web Audio Playground foi aumentar o tamanho dos pontos de conexão para que fossem mais precisos.

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

No 6: mantenha os manipuladores de toque controlados ou eles vão atrapalhar a rolagem

Também é importante manter os gerenciadores de toque confinados apenas aos elementos que você precisa deles. Os elementos de toque podem ter largura de banda muito alta, por isso é importante evitar manipuladores em elementos de rolagem (pois seu processamento pode interferir em otimizações do navegador para uma rolagem rápida e sem instabilidade. Os navegadores modernos tentam rolar em uma linha de execução de GPU, mas isso é impossível se eles precisarem verificar primeiro com JavaScript se cada evento de toque será tratado pelo aplicativo). Veja um exemplo desse comportamento.

Uma orientação a ser seguida para evitar esse problema é garantir que, se você estiver processando eventos de toque apenas em uma pequena parte da interface, só conecte gerenciadores de toque a ela (não, por exemplo, no <body> da página). Em resumo, limite o máximo possível o escopo dos gerenciadores de toque.

No 7: multitoque

O último desafio interessante é que, embora tenhamos nos referido como interface do usuário "Tocar", quase universalmente o suporte é, na verdade, para multitoque, ou seja, as APIs fornecem mais de uma entrada de toque por vez. À medida que você começar a oferecer suporte ao toque nos seus aplicativos, considere como eles podem ser afetados por vários toques.

Se você já cria aplicativos orientados principalmente pelo mouse, está acostumado a criar com no máximo um ponto de cursor. Os sistemas normalmente não são compatíveis com vários cursores de mouse. Para muitos aplicativos, você estará apenas mapeando eventos de toque para uma única interface de cursor, mas a maior parte do hardware que vimos para entrada de toque de computador 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, é importante oferecer suporte a várias entradas por toque simultâneas.

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

Retoque

Esperamos que este artigo tenha trazido algumas orientações sobre desafios comuns na implementação de toque junto com interações do mouse. Mais importante do que qualquer outro conselho, é claro, é que você precisa testar seu app em celular, tablet e em ambientes de computador combinados com mouse e toque. Se você não tiver o hardware de toque e mouse, use a opção "Emular eventos de toque" do Chrome para testar em diferentes cenários.

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