Dotykanie i mysz

Together again for the first time

Wprowadzenie

Od prawie 30 lat komputery stacjonarne korzystają z klawiatury i myszy lub trackpada jako głównych urządzeń do wprowadzania danych. W ostatniej dekadzie smartfony i tablety wprowadziły jednak nowy model interakcji: dotykowy. Wprowadzenie urządzeń z ekranem dotykowym z systemem Windows 8 i wprowadzenie Chromebooka Pixel z ekranem dotykowym sprawiają, że obsługa dotykowa staje się standardem na komputerach. Jednym z największych wyzwań jest tworzenie interfejsów, które działają nie tylko na urządzeniach dotykowych i z myszką, ale też na urządzeniach, na których użytkownik będzie używać obu tych metod wprowadzania danych – czasem nawet jednocześnie.

Z tego artykułu dowiesz się, jak działają funkcje dotykowe w przeglądarce, jak zintegrować ten nowy mechanizm interfejsu z dotychczasowymi aplikacjami oraz jak obsługa dotykowa może współpracować z myszką.

Stan dotyku na platformie internetowej

iPhone był pierwszą popularną platformą z wbudowanymi w przeglądarkę internetową interfejsami API dotykowymi. Kilku innych dostawców przeglądarek stworzyło podobne interfejsy API, które są zgodne z implementacją na iOS, opisaną w specyfikacji „Touch Events w wersji 1”. Zdarzenia dotykowe są obsługiwane w Chrome i Firefox na komputerach oraz w Safari na iOS i Chrome oraz przeglądarce Android na Androidzie, a także w innych przeglądarkach mobilnych, takich jak przeglądarka Blackberry.

Mój współpracownik Boris Smus napisał świetny samouczek HTML5Rocks na temat zdarzeń dotykowych, który jest świetnym wprowadzeniem do tej tematyki, jeśli nie zajmowałeś/zajmowałaś się wcześniej zdarzeniami dotykowymi. Jeśli nie pracowałeś(-aś) wcześniej z zdarzeniami dotyku, przeczytaj ten artykuł, zanim przejdziesz dalej. Proszę, zaczekam.

Wszystko gotowe? Teraz, gdy masz już podstawową wiedzę na temat zdarzeń dotykowych, możesz się zmierzyć z wyzwaniem, jakie stwarza pisanie interakcji z użyciem dotyku. Interakcje dotykowe mogą się znacznie różnić od zdarzeń myszy (i od zdarzeń myszy emulujących trackpad i myszkę kulkową). Chociaż interfejsy dotykowe zwykle próbują emulować mysz, emulacja ta nie jest idealna ani kompletna. Musisz więc pracować nad obydwoma stylami interakcji i być może musisz obsługiwać każdy interfejs osobno.

Najważniejsze: użytkownik może mieć dotykowy ekran i mysz

Wielu deweloperów stworzyło witryny, które statycznie wykrywają, czy środowisko obsługuje zdarzenia dotykowe, a potem zakładają, że muszą obsługiwać tylko zdarzenia dotykowe (a nie myszy). To założenie jest obecnie błędne – obecność zdarzeń dotykowych nie oznacza, że użytkownik korzysta głównie z urządzenia dotykowego. Urządzenia takie jak Chromebook Pixel i niektóre laptopy z systemem Windows 8 obsługują teraz obie metody wprowadzania danych: za pomocą myszy i dotykiem. Wkrótce dołączy do nich więcej urządzeń. Na tych urządzeniach użytkownicy naturalnie korzystają z myszy i ekranu dotykowego, aby wchodzić w interakcje z aplikacją, więc „obsługa ekranu dotykowego” nie jest tym samym, co „nie wymaga obsługi myszy”. Nie możesz potraktować tego problemu jako „muszę napisać 2 różne style interakcji i przełączać się między nimi”. Musisz zastanowić się, jak obie interakcje będą działać razem i osobno. Na moim Chromebooku Pixel często używam trackpada, ale czasami też dotykam ekranu – w tej samej aplikacji lub na tej samej stronie robię to, co wydaje mi się najbardziej naturalne w danym momencie. Z drugiej strony, niektórzy użytkownicy laptopów z ekranem dotykowym rzadko korzystają z tego ekranu, więc obecność ekranu dotykowego nie powinna uniemożliwiać korzystania z myszy.

Niestety trudno jest określić, czy środowisko przeglądarki użytkownika obsługuje wprowadzanie za pomocą dotyku.W idealnej sytuacji przeglądarka na komputerze stacjonarnym zawsze powinna wskazywać obsługę zdarzeń dotykowych, aby można było w dowolnym momencie podłączyć ekran dotykowy (np. gdy stanie się dostępny ekran dotykowy podłączony przez KVM). Z tych wszystkich powodów aplikacje nie powinny przełączać się między dotykiem a myszką – powinny obsługiwać obie te opcje.

Obsługa myszy i dotyku

#1 Klikanie i dotknięcie – „naturalny” porządek rzeczy

Pierwszym problemem jest to, że interfejsy dotykowe zwykle próbują emulować kliknięcia myszką. Jest to oczywiste, ponieważ interfejsy dotykowe muszą działać w aplikacjach, które wcześniej obsługiwały tylko zdarzenia myszy. Możesz użyć tego jako skrótu, ponieważ zdarzenia „kliknięcia” będą nadal wywoływane, niezależnie od tego, czy użytkownik kliknął myszką, czy palcem na ekranie. Jednak z tym skrótem wiąże się kilka problemów.

Po pierwsze, musisz zachować ostrożność podczas projektowania bardziej zaawansowanych interakcji dotykowych: gdy użytkownik używa myszy, aplikacja reaguje na to zdarzeniem kliknięcia, ale gdy dotyka ekranu, występują zarówno zdarzenia dotyku, jak i kliknięcia. W przypadku pojedynczego kliknięcia kolejność zdarzeń jest następująca:

  1. touchstart
  2. touchmove
  3. touchend
  4. ruch kursora myszy|najechanie kursorem myszy (na obiekt)
  5. mousemove
  6. mousedown
  7. mouseup
  8. click

Oznacza to oczywiście, że jeśli przetwarzasz zdarzenia dotykowe, takie jak touchstart, musisz się upewnić, że nie przetwarzasz też odpowiadających im zdarzeń mousedown ani click. Jeśli możesz anulować zdarzenia dotykowe (wywołując metodę preventDefault() w obiekcie event handler), żadne zdarzenia myszy nie będą generowane w przypadku dotyku. Jedno z najważniejszych reguł dotyczących elementów sterujących dotykiem:

Jednak spowoduje to również zablokowanie innych domyślnych zachowań przeglądarki (np. przewijania). Zwykle jednak zdarzenie dotyku obsługujesz całkowicie w obsługniku i chcesz wyłączyć domyślne działania. Zazwyczaj albo chcesz obsłużyć i anulować wszystkie zdarzenia dotyku, albo nie chcesz mieć dla nich żadnych obsługi.

Po drugie, gdy użytkownik klika element na stronie internetowej na urządzeniu mobilnym, strony, które nie zostały zaprojektowane pod kątem interakcji mobilnej, mają opóźnienie co najmniej 300 ms między zdarzeniem touchstart a przetwarzaniem zdarzeń myszy (mousedown). Możesz to zrobić w Chrome. W Narzędziach deweloperskich w Chrome możesz włączyć „Emulowanie zdarzeń dotykowych”, aby przetestować interfejsy dotykowe na systemie bez ekranu dotykowego.

To opóźnienie pozwala przeglądarce określić, czy użytkownik wykonuje inny gest, w szczególności powiększenie przez dwukrotne dotknięcie. Może to być oczywiście problematyczne w przypadku, gdy chcesz mieć natychmiastową reakcję na dotyk palca. Trwają prace nad ograniczeniem scenariuszy, w których to opóźnienie występuje automatycznie.

Chrome na Androida Przeglądarka w systemie Android Opera Mobile na Androida). Firefox na Androida Safari na iOS
Niezmienny widoczny obszar Bez opóźnienia 300 ms 300 ms Bez opóźnienia 300 ms
Brak widocznego obszaru 300 ms 300 ms 300 ms 300 ms 300 ms

Pierwszym i najłatwiejszym sposobem na uniknięcie tego opóźnienia jest „poinformowanie” przeglądarki mobilnej, że strona nie wymaga powiększania.Można to zrobić, używając stałego widocznego obszaru, np. wstawiając na stronie:

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

Nie zawsze jest to jednak odpowiednie – wyłącza to powiększanie za pomocą 2 palców, które może być wymagane ze względów związanych z dostępnością. Dlatego stosuj tę metodę z umiarkowaniem (jeśli w ogóle zdecydujesz się na jej użycie) (jeśli wyłączysz skalowanie przez użytkownika, możesz chcieć zapewnić inny sposób zwiększenia czytelności tekstu w aplikacji). Nie dotyczy to Chrome na urządzeniach klasy komputera, które obsługują dotyk, ani innych przeglądarek na platformach mobilnych, gdy strona ma widok nieskalowalny.

#2: Zdarzenia przesunięcia kursora nie są wywoływane przez dotyk

W tym miejscu warto zauważyć, że emulacja zdarzeń myszy w interfejsie dotykowym zwykle nie obejmuje emulacji zdarzeń mousemove. Oznacza to, że jeśli tworzysz piękny element sterujący obsługiwany przez mysz, który używa zdarzeń mousemove, prawdopodobnie nie będzie on działać na urządzeniu dotykowym, chyba że dodasz też odpowiednie przetwarzanie zdarzeń touchmove.

Przeglądarki zwykle automatycznie implementują odpowiednie interakcje dotykowe w przypadku elementów sterujących HTML. Oznacza to, że np. elementy sterujące zakresem HTML5 będą działać tylko wtedy, gdy użyjesz interakcji dotykowych. Jeśli jednak zaimplementujesz własne elementy sterujące, prawdopodobnie nie będą one działać w przypadku interakcji typu kliknij i przeciągnij. Niektóre powszechnie używane biblioteki (np. jQueryUI) nie obsługują jeszcze natywnej obsługi interakcji dotykowych w ten sposób (chociaż w przypadku jQueryUI istnieje kilka poprawek). Był to jeden z pierwszych problemów, które napotkałem podczas aktualizacji aplikacji Web Audio Playground do obsługi dotykowej – suwaki były oparte na jQueryUI, więc nie działały z interakcjami kliknięcia i przeciągania. Przełączyłem się na kontrolki zakresu HTML5 i one działają. Oczywiście można też po prostu dodać obsługę gestów dotykowych, aby aktualizować suwaki, ale jest z tym pewien problem.

#3: Dotknięcie i Ruch myszy to nie to samo

Pewnym pułapką, na którą natrafiłam, jest to, że uchwyty do obsługi zdarzeń touchmove i mousemove wywołują te same ścieżki kodu. Sposób działania tych zdarzeń jest bardzo podobny, ale nieznacznie się różni – w szczególności zdarzenia dotykowe zawsze dotyczą elementu, w którym dotyk się ZACZĄŁ, podczas gdy zdarzenia myszy dotyczą elementu, na którym znajduje się kursor myszy. Dlatego mamy zdarzenia mouseover i mouseout, ale nie ma odpowiednich zdarzeń touchover i touchout – tylko touchend.

Najczęstszym problemem jest usunięcie (lub przeniesienie) elementu, który użytkownik zaczął dotykać. Wyobraź sobie na przykład karuzel obrazów z elementem sterującym dotykowym na całej karuzeli, który umożliwia niestandardowe przewijanie. Gdy dostępne obrazy się zmieniają, usuwasz niektóre elementy <img> i dodasz inne. Jeśli użytkownik zacznie dotykać jeden z tych obrazów, a potem go usuniesz, Twój moduł obsługi (znajdujący się w przodku elementu img) przestanie otrzymywać zdarzenia dotykowe (ponieważ są one wysyłane do celu, który nie jest już w drzewie). Wyglądać to będzie tak, jakby użytkownik trzymał palec w jednym miejscu, mimo że może go przesunąć i ostatecznie usunąć.

Oczywiście możesz uniknąć tego problemu, nie usuwając elementów, które mają moduły obsługi dotyku (lub mają je ich przodkowie), gdy dotyk jest aktywny. Innym sposobem jest zaczekanie na zdarzenie touchstart i dodanie do targetu zdarzenia touchstart modułów obsługi touchmove/touchend/touchcancel (a potem usunięcie ich w przypadku zdarzeń touchend/cancel). Dzięki temu będziesz nadal otrzymywać zdarzenia dotyku, nawet jeśli element docelowy zostanie przeniesiony lub usunięty. Możesz trochę pobawić się tutaj – dotknij czerwonego pola i przytrzymując je, naciśnij klawisz Escape, aby usunąć je z DOM.

#4: Dotknij i najedź

Metafora wskaźnika myszy oddziela pozycję kursora od aktywnego wybierania, co pozwala deweloperom używać stanów najechania kursorem, aby ukrywać i wyświetlać informacje, które mogą być istotne dla użytkowników. Jednak większość interfejsów dotykowych nie wykrywa „najeżdżania” palcem na element – dlatego wyświetlanie ważnych informacji semantycznych (np. wyskakującego okienka z pytaniem „Co to za element sterujący?”) na podstawie najeżdżania jest niedopuszczalne, chyba że zapewnisz też przyjazny dla dotykowych ekranów sposób dostępu do tych informacji. Musisz uważać, jak używasz nawigacji kursorem, aby przekazywać informacje użytkownikom.

Co ciekawe, w niektórych przypadkach pseudoklasa CSS :hover może być aktywowana przez interfejsy dotykowe. Gdy użytkownik kliknie element, staje się on aktywny, dopóki trzyma palec na ekranie, i przyjmuje stan :hover. (W Internet Explorerze efekt :hover jest aktywny tylko wtedy, gdy palec użytkownika dotyka ekranu – inne przeglądarki zachowują efekt :hover do następnego kliknięcia lub przesunięcia kursora). To dobre podejście do tworzenia menu wyskakujących w interfejsach dotykowych – efektem aktywacji elementu jest też zastosowanie stanu :hover. Na przykład:

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

Gdy użytkownik kliknie inny element, ten pierwszy przestaje być aktywny i znika stan najechania, tak jakby użytkownik przesunął kursor myszy z tego elementu. Możesz owinąć zawartość w elemencie <a>, aby była też przyciskiem przewijania. Dzięki temu użytkownik będzie mógł wyświetlać dodatkowe informacje, najeżdżając na nie kursorem myszy lub klikając je, dotykając palcem lub naciskając klawisz. Nie będzie do tego potrzebny JavaScript. Gdy zaczęłam pracować nad Web Audio Playground, aby działał dobrze z interfejsami dotykowymi, byłam mile zaskoczona, że moje menu wyskakujące już działało dobrze z urządzeniami dotykowymi, ponieważ użyłam tego typu struktury.

Powyższa metoda sprawdza się zarówno w przypadku interfejsów opartych na wskaźniku myszy, jak i interfejsów dotykowych. W przeciwieństwie do atrybutów „title” (tytuł) wyświetlanych po najechaniu kursorem, które NIE będą widoczne po aktywowaniu elementu:

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

#5: Dokładność panelu dotykowego w porównaniu z myszą

Chociaż myszy są odrealnione, okazuje się, że są bardzo dokładne, ponieważ system operacyjny śledzi dokładną pozycję kursora w pikselach. Z drugiej strony deweloperzy aplikacji mobilnych wiedzą, że dotyk palców na ekranie dotykowym nie jest tak dokładny, głównie ze względu na rozmiar powierzchni palca w miejscu kontaktu z ekranem (i częściowo dlatego, że palce zasłaniają ekran).

Wiele osób i firm przeprowadziło obszerne badania dotyczące projektowania aplikacji i witryn, które umożliwiają interakcję za pomocą palców. Na ten temat napisano też wiele książek. Podstawową radą jest zwiększenie rozmiaru docelowych elementów dotykowych przez zwiększenie odstępów oraz zmniejszenie prawdopodobieństwa nieprawidłowego kliknięcia przez zwiększenie marginesu między elementami. (marginesy nie uwzględnia się w obsługiwaniu wykrywania trafień w przypadku zdarzeń dotyku i kliknięcia, ale uwzględnia się wypełnienie). Jednym z głównych rozwiązań, które musiałem wprowadzić w Web Audio Playground, było zwiększenie rozmiarów punktów połączenia, aby można było je dotykać z większą precyzją.

Wielu dostawców przeglądarek, które obsługują interfejsy dotykowe, wprowadziło w przeglądarce logikę, która pomaga nakierować na właściwy element, gdy użytkownik dotyka ekranu, oraz zmniejsza prawdopodobieństwo nieprawidłowego kliknięcia. Zwykle dotyczy to jednak tylko zdarzeń kliknięcia, a nie przesunięcia (chociaż Internet Explorer wydaje się również modyfikować zdarzenia mousedown/mousemove/mouseup).

#6: Nie pozwól, aby elementy sterujące dotykiem ograniczały Twoje przewijanie

Ważne jest też, aby ograniczyć elementy obsługi dotykowej tylko do tych elementów, w których są potrzebne. Elementy obsługi dotykowe mogą wymagać bardzo dużej przepustowości, dlatego ważne jest, aby unikać ich stosowania w elementach z przewijaniem (przetwarzanie może zakłócać optymalizację przeglądarki pod kątem szybkiego przewijania bez zacięć – współczesne przeglądarki próbują przewijać za pomocą wątku GPU, ale jest to niemożliwe, jeśli najpierw muszą sprawdzić za pomocą JavaScript, czy aplikacja ma obsłużyć każde zdarzenie dotykowe). Możesz sprawdzić przykład takiego zachowania.

Aby uniknąć tego problemu, pamiętaj, że jeśli zdarzenia dotykowe obsługujesz tylko w małej części interfejsu użytkownika, dołącz tam tylko moduły obsługi dotyku (nie na przykład w <body> strony). Krótko mówiąc, ogranicz zakres modułów obsługi dotyku do minimum.

#7: Wielodotykowy

Ostatnim interesującym wyzwaniem jest to, że chociaż nazywamy to interfejsem użytkownika „dotykowym”, w rzeczywistości interfejsy API obsługują prawie zawsze wielodotykowe wprowadzanie danych, czyli umożliwiają wprowadzanie więcej niż jednego dotyku naraz. Gdy zaczniesz obsługiwać w swoich aplikacjach dotyk, weź pod uwagę, jak na aplikację może wpływać wielokrotne dotykanie.

Jeśli aplikacje tworzysz głównie za pomocą myszy, prawdopodobnie przyzwyczajasz się do tworzenia z maksymalnie jednym kursorem – systemy zwykle nie obsługują wielu kursorów myszy. W wielu aplikacjach będziesz tylko mapować zdarzenia dotykowe na interfejs pojedynczego kursora, ale większość urządzeń dotykowych do komputerów obsługuje co najmniej 2 wejścia jednocześnie, a większość nowych urządzeń obsługuje co najmniej 5 wejść jednocześnie. Jeśli tworzysz klawiaturę fortepianową na ekranie, chcesz oczywiście obsługiwać wiele jednoczesnych wejść dotykowych.

Obecnie stosowane interfejsy W3C Touch API nie mają interfejsu API, który pozwalałby określić, ile punktów styczności z ekranem obsługuje sprzęt. Dlatego musisz oszacować, ile punktów styczności z ekranem będą chcieli mieć użytkownicy. Możesz też zwrócić uwagę na to, ile punktów styczności z ekranem widzisz w praktyce, i odpowiednio do tego dostosować interfejs. Jeśli np. w aplikacji do gry na pianinie nigdy nie ma więcej niż 2 punkty styczności z użytkownikiem, warto dodać interfejs „akordów”. Interfejs PointerEvents API ma interfejs API do określania możliwości urządzenia.

Poprawki

Mamy nadzieję, że ten artykuł dostarczył Ci wskazówek dotyczących typowych problemów związanych z wdrażaniem obsługi dotykowej i działania myszy. Najważniejsze jest jednak przetestowanie aplikacji na urządzeniach mobilnych, tabletach i komputerach z myszką i dotykiem. Jeśli nie masz urządzenia z ekranem dotykowym i myszką, możesz skorzystać z funkcji „Naśladowanie zdarzeń dotykowych” w Chrome, aby przetestować różne scenariusze.

Dzięki tym wskazówkom możesz nie tylko tworzyć interaktywne treści, ale też stosunkowo łatwo tworzyć angażujące interaktywne treści, które dobrze działają z użyciem ekranu dotykowego, myszy, a nawet obu tych urządzeń jednocześnie.