Dotykanie i mysz

Razem znowu po raz pierwszy

Wprowadzenie

Od prawie 30 lat komputery osobiste 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. Wraz z wprowadzeniem urządzeń z ekranem dotykowym i systemem Windows 8 oraz wydaniem wspaniałego Chromebooka Pixel z ekranem dotykowym dotykowe interfejsy stają 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 dotykowych. 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 sposobem na rozpoczęcie pracy, jeśli wcześniej nie zajmowałeś/zajmowałaś się 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 ekranu dotykowego. Interakcje dotykowe mogą się znacznie różnić od zdarzeń myszy (i zdarzeń myszy emulujących trackpad i trackball). 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 urządzenia lub w ogóle z niego nie korzystają – dlatego obecność ekranu dotykowego nie powinna uniemożliwiać korzystania z myszy ani utrudniać tego.

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 powinna zawsze 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 aplikacji, która wcześniej obsługiwała 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. Jedną z najważniejszych zasad dotyczących elementów sterujących dotykiem jest:

Jednak spowoduje to również wyłączenie innych domyślnych zachowań przeglądarki (np. przewijania). Zwykle jednak zdarzenie dotkowe obsługujesz całkowicie w obiekcie obsługi, więc i tak będziesz musiał 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 liczby scenariuszy, w których występuje to opóźnienie.

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 uniknięcia 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 przez rozciąganie, które może być wymagane ze względów związanych z dostępnością. Dlatego stosuj tę metodę oszczędnie (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 procedury obsługi zdarzeń touchmove.

Przeglądarki zwykle automatycznie implementują odpowiednie interakcje dotykowe w przypadku elementów sterujących HTML. Oznacza to, że na przykład 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, które rozwiązują ten problem). 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 ustawienia zakresu HTML5 i one działają. Oczywiście można też po prostu dodać moduły obsługi dotykowego przesuwania, aby aktualizować suwaki, ale jest z tym pewien problem.

#3: Dotknięcie i poruszenie kursora to nie to samo

Pewnym pułapką, na którą natrafiłam, jest to, że niektórzy deweloperzy wywołują te same ścieżki kodu w obsługach zdarzeń touchmove i mousemove. 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 obsługi dotykowej 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) – będzie wyglądać na to, że użytkownik trzyma palec w tym samym miejscu, mimo że może go przesunąć lub w ogóle go usunąć.

Oczywiście możesz uniknąć tego problemu, unikając usuwania elementów, które mają moduły obsługi dotyku (lub mają je ich przodkowie) podczas aktywnego dotyku. Innym sposobem jest rejestrowanie statycznych modułów obsługi zdarzeń touchend/touchmove zamiast czekania na zdarzenie touchstart, a następnie dodanie modułów obsługi zdarzeń touchmove/touchend/touchcancel do elementu docelowego zdarzenia touchstart (i usunięcie ich po zakończeniu/anulowaniu). Dzięki temu nadal będziesz 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 palcem 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 interfejsy dotykowe mogą wywoływać pseudoklasę CSS :hover. W takim przypadku kliknięcie elementu powoduje jego aktywację, dopóki palec jest wciśnięty, a także wywołuje stan :hover. (W Internet Explorerze :hover jest aktywne tylko wtedy, gdy palec użytkownika jest wciśnięty – inne przeglądarki zachowują :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 stan najechania znika, tak jakby użytkownik przesunął kursor myszy z tego elementu. Możesz owinąć zawartość w elemencie <a>, aby uczynić ją też przyciskiem przewijania. Dzięki temu użytkownik będzie mógł przełączać 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 dotykiem, 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 (a 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 wykrywaniu trafień w przypadku zdarzeń dotyku i kliknięcia, ale uwzględnia się je w przypadku wypełnień). Jednym z głównych rozwiązań, które wprowadziłem 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órzy obsługują interfejsy dotykowe, wprowadziło w przeglądarce logikę, która pomaga wskazywać 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ć obsługę dotyku tylko do elementów, w których jest ona potrzebna. Elementy dotykowe mogą wymagać bardzo dużej przepustowości, dlatego ważne jest, aby unikać obsługi dotyku 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ć wątek GPU, ale jest to niemożliwe, jeśli najpierw muszą sprawdzić w JavaScript, czy aplikacja ma obsłużyć każde zdarzenie dotyku). 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 dotykowych (nie na przykład w miejscu <body> na stronie). Krótko mówiąc, ogranicz zakres modułów obsługi dotykowych do minimum.

#7: Wielodotykowy

Ostatnim interesującym wyzwaniem jest to, że chociaż mówimy o interfejsie użytkownika „dotykowym”, w praktyce interfejsy API obsługują prawie zawsze wielodotykowe wprowadzanie danych, czyli umożliwiają wprowadzanie więcej niż jednego dotknięcia naraz. Gdy zaczniesz obsługiwać w swoich aplikacjach gesty dotykowe, zastanów się, 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 1 wskaźnikiem kursora – 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 klientem obsługuje sprzęt. Dlatego musisz oszacować, ile punktów styczności z klientem będą chcieli mieć użytkownicy. Możesz też zwrócić uwagę na to, ile punktów styczności z klientem 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.

Touching Up

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ć atrakcyjne interaktywne treści, które dobrze działają z użyciem ekranu dotykowego, myszy, a nawet obu tych urządzeń jednocześnie, ale też robić to stosunkowo łatwo.