Twórz z Chrome

Klocki LEGO® w internecie na różne urządzenia

Build with Chrome – zabawny eksperyment dla użytkowników Chrome na komputery, który opublikowano w Australii, został ponownie uruchomiony w 2014 roku. Jest on dostępny na całym świecie, powiązany z filmem LEGO® MOVIETM i niedawno dodanym wsparciem na urządzenia mobilne. W tym artykule podzielimy się wnioskiem z projektu, w szczególności dotyczy to przejścia z trybu „Tylko komputer” na rozwiązanie obsługujące wiele urządzeń, które obsługuje zarówno sterowanie za pomocą myszy, jak i dotyku.

Historia Build with Chrome

Pierwsza wersja Build with Chrome została wprowadzona w Australii w 2012 roku. Chcieliśmy w zupełnie nowy sposób zademonstrować możliwości internetu i zaprezentować Chrome zupełnie nowym odbiorcom.

Witryna składała się z 2 głównych elementów: trybu „Budowanie”, w którym użytkownicy mogą tworzyć prace z klocków LEGO, i trybu „Odkrywaj”, gdzie można przeglądać te kompozycje w Mapach Google w wersji z klocków LEGO.

Aby zapewnić użytkownikom jak najlepsze wrażenia podczas budowania z klocków LEGO, konieczne było wprowadzenie interaktywnego trybu 3D. W 2012 roku technologia WebGL była dostępna publicznie tylko w przeglądarkach na komputerach, więc Build była kierowana tylko na komputery. Eksploracja wykorzystała Mapy Google do wyświetlania projektów, ale po powiększeniu dostatecznie zbliżona wersja mapy została przeprowadzona na mapie w technologii WebGL i nadal korzystała z Map Google jako tekstury płyty bazowej. Mieliśmy nadzieję, że stworzymy środowisko, w którym miłośnicy klocków LEGO w każdym wieku będą mogli łatwo i intuicyjnie wyrażać swoją kreatywność oraz wspólnie odkrywać swoje twórcze projekty.

W 2013 roku zdecydowaliśmy się rozszerzyć obsługę Build with Chrome o nowe technologie internetowe. Wśród tych technologii znalazły się WebGL w Chrome na Androida, które w naturalny sposób umożliwiłyby Build with Chrome ewoluowanie do wersji mobilnej. Na początek opracowaliśmy prototypy dotykowe, a następnie przyjrzeliśmy się sprzętowi „Narzędzia do tworzenia”, by poznać działanie gestów i responsywność dotykową, które występują w przeglądarce w porównaniu z aplikacją mobilną.

Elastyczny interfejs

Musieliśmy obsługiwać urządzenia z dotykiem i użyciem myszy. Jednak ze względu na ograniczoną przestrzeń korzystanie z tego samego interfejsu na małych ekranach dotykowych okazuje się nieoptymalne.

W Build jest z nami dużo interaktywności: powiększanie i pomniejszanie, zmiana koloru klocków oraz oczywiście wybieranie, obracanie i rozmieszczanie klocków. Jest to narzędzie, z którego użytkownicy często spędzają dużo czasu. Dlatego ważne jest, aby mieli szybki dostęp do wszystkich używanych często narzędzi i mogli bez trudu z niego korzystać.

Podczas projektowania wysoce interaktywnej aplikacji dotykowej zauważasz, że ekran szybko wydaje się mały, a palce użytkownika podczas interakcji zasłaniają znaczną część ekranu. Stało się to oczywiste podczas pracy z konstruktorem. Przy projektowaniu elementów graficznych należy brać pod uwagę fizyczny rozmiar ekranu, a nie liczbę pikseli na grafice. Aby uzyskać jak najwięcej miejsca na ekranie, należy zminimalizować liczbę przycisków i elementów sterujących.

Zależało nam na tym, aby aplikacja Build była przyjazna dla użytkowników urządzeń dotykowych, a nie tylko dotykowo dodana do oryginalnej implementacji na komputerze, lecz po prostu jej dotyk. W efekcie stworzyliśmy dwie wersje UI – jedną na komputery i tablety z dużymi ekranami, a drugą na urządzenia mobilne z mniejszymi ekranami. W miarę możliwości najlepiej jest używać jednej implementacji i zapewnić płynne przejście między trybami. W naszym przypadku stwierdziliśmy, że różnica w zastosowaniach obu trybów jest tak duża, że postanowiliśmy skupić się na konkretnym punkcie przerwania. Obie wersje mają wiele cech wspólnych. Staraliśmy się realizować większość zadań tylko w ramach jednej implementacji kodu, ale niektóre elementy interfejsu działają inaczej w obu wersjach.

Do wykrywania urządzeń mobilnych wykorzystujemy dane klienta użytkownika, a następnie sprawdzamy rozmiar widocznego obszaru, aby zdecydować, czy warto skorzystać z mobilnego interfejsu małego ekranu. Trudno jest wybrać punkt przerwania dla „dużego ekranu”, ponieważ trudno jest uzyskać wiarygodną wartość dla fizycznego rozmiaru ekranu. Na szczęście w naszym przypadku nie ma większego znaczenia, jeśli wyświetlimy interfejs na małym ekranie na urządzeniu dotykowym z dużym ekranem, bo narzędzie nadal będzie działać dobrze, ale niektóre przyciski mogą wydawać się trochę za duże. Ostatecznie punkt przerwania ustawiamy na 1000 pikseli. Jeśli wczytasz stronę z okna szerszyego niż 1000 pikseli (w trybie poziomym), otrzymasz wersję na duży ekran.

Porozmawiajmy teraz o dwóch rozmiarach ekranu i ich sposobie obsługi:

Duży ekran z obsługą myszy i dotyku

Wersja z dużym ekranem jest obsługiwana na wszystkich komputerach, które obsługują mysz, oraz na urządzeniach dotykowych z dużymi ekranami (np. Google Nexus 10). Ta wersja jest zbliżona do pierwotnego rozwiązania na komputery pod względem dostępnych opcji nawigacji, ale dodaliśmy obsługę dotykową i niektóre gesty. Dostosowujemy interfejs do rozmiaru okna, więc gdy użytkownik zmieni rozmiar okna, może usunąć niektóre elementy lub zmienić ich rozmiar. Robimy to za pomocą zapytań o media w CSS.

Przykład: gdy dostępna wysokość jest mniejsza niż 730 pikseli, suwak powiększenia w trybie Eksplorowanie jest ukryty:

@media only screen and (max-height: 730px) {
    .zoom-slider {
        display: none;
    }
}

Mały ekran, tylko obsługa dotykowa

Ta wersja jest przeznaczona na urządzenia mobilne i małe tablety (urządzenia docelowe Nexus 4 i Nexus 7). Ta wersja wymaga obsługi wielodotyku.

Na urządzeniach z małymi ekranami musimy zapewnić jak najwięcej miejsca na ekranie, dlatego wprowadziliśmy kilka poprawek, aby zmaksymalizować przestrzeń. Najczęściej nie da się usunąć rzadko używanych elementów:

  • Podczas budowania selektor klocków powiększa się do postaci selektora kolorów.
  • Zastąpiliśmy elementy sterujące powiększeniem i orientacją gestami wielodotykowymi.
  • Funkcja pełnego ekranu w Chrome pomaga uzyskać dodatkowe miejsce na ekranie.
Twórz na dużym ekranie
Twórz na dużym ekranie. Menu wyboru jest zawsze widoczne, a po prawej stronie jest kilka elementów sterujących.
Twórz na małym ekranie
Twórz na małym ekranie. Selektor klocków jest zminimalizowany, a niektóre przyciski usunięte.

Wydajność i pomoc WebGL

Nowoczesne urządzenia dotykowe mają dość wydajne GPU, ale nadal są daleko od ich komputerów. Wiedzieliśmy więc, że mamy pewne problemy z wydajnością, zwłaszcza w trybie Odkrywaj 3D, w którym musimy renderować wiele kompozycji jednocześnie.

W kreatywny sposób chcieliśmy dodać kilka nowych typów klocków o złożonych kształtach, a nawet przezroczystość – funkcje, które zwykle bardzo obciążają GPU. Musieliśmy jednak zachować zgodność wsteczną i kontynuować obsługę kompozycji z pierwszej wersji, więc nie mogliśmy wprowadzić żadnych nowych ograniczeń, takich jak znaczne zmniejszenie łącznej liczby klocków w kompozycjach.

W pierwszej wersji Build było z tego maksymalną liczbą klocków, których można użyć w jednej kompozycji. Na ekranie był widoczny miernik wskazujący, ile klocków zostało. W nowym wdrożeniu niektóre nowe klocki miały wpływ na jej metry w większym stopniu niż standardowe, co nieco zmniejszało całkowitą maksymalną liczbę klocków. Był to jeden ze sposobów dodania nowych klocków przy zachowaniu umiarkowanej skuteczności.

W trybie Eksplorowanie 3D w tym samym czasie wiele się dzieje, m.in. wczytywanie tekstur płyt ściennych, wczytywanie kompozycji, tworzenie animacji i renderowanie kompozycji. Zużywanie tego w dużym stopniu wymaga GPU i CPU, dlatego w Narzędziach deweloperskich w Chrome wybraliśmy dużo profilowania ramek, aby zoptymalizować te elementy w jak największym stopniu. Na urządzeniach mobilnych postanowiliśmy przybliżyć nieco kompozycje, aby nie wyświetlać tylu kompozycji w tym samym czasie.

Niektóre urządzenia wymagały ponownego przedstawienia i uproszczenia funkcji cieniowania WebGL, ale zawsze znaleźliśmy sposób, aby rozwiązać ten problem i kontynuować rozwój.

Obsługa urządzeń innych niż WebGL

Chcieliśmy, aby witryna była w pewnym stopniu użyteczna, nawet jeśli urządzenie użytkownika nie obsługuje WebGL. Czasami można przedstawić obiekty 3D w uproszczony sposób za pomocą rozwiązania Canvas lub funkcji CSS3D. Nie znaleźliśmy jednak wystarczającego rozwiązania do powielania funkcji Buduj i Eksplorowania 3D bez użycia WebGL.

Aby zachować spójność, styl wizualny kreacji musi być taki sam na wszystkich platformach. Potencjalnie moglibyśmy spróbować rozwiązania 2,5D, ale to działanie spowodowałoby zmianę wyglądu kreacji pod pewnymi względami. Musieliśmy też zadbać o to, by treści utworzone w pierwszej wersji Build with Chrome wyglądały tak samo i działały tak samo płynnie w nowej wersji.

Tryb Eksplorowanie 2D jest nadal dostępny na urządzeniach innych niż WebGL, mimo że nie możesz tworzyć nowych kompozycji ani odkrywać miejsc w 3D. Dzięki temu użytkownicy nadal będą mogli zorientować się, o czym jest projekt i co mogliby stworzyć za pomocą tego narzędzia, gdyby używały urządzenia z obsługą WebGL. Strona może nie być tak wartościowa dla użytkowników bez obsługi WebGL, ale powinna pełnić rolę zwiastuna i zachęcić ich do wypróbowania nowego interfejsu.

Utrzymanie wersji zastępczych rozwiązań WebGL jest czasem po prostu niemożliwe. Powodów może być wiele: wydajność, styl wizualny, koszty tworzenia i konserwacji itd. Jeśli jednak nie zdecydujesz się na stosowanie kreacji zastępczych, zadbaj o przynajmniej wygodę użytkowników bez obsługi WebGL, wyjaśnij im, dlaczego nie mogą wejść w pełni do witryny, i podaj instrukcje, jak rozwiązać ten problem, używając przeglądarki obsługującej WebGL.

Zarządzanie zasobami

W 2013 roku wprowadziliśmy nową wersję Map Google z najważniejszymi zmianami w interfejsie od czasu ich wprowadzenia. Postanowiliśmy więc przeprojektować Build with Chrome, by pasował do nowego interfejsu Map Google. Wzięliśmy też pod uwagę inne czynniki. Nowy wygląd jest stosunkowo płaski, z jednolitymi, jednolitymi kolorami i prostymi kształtami. Dzięki temu mogliśmy użyć czystego CSS w wielu elementach interfejsu, co minimalizuje wykorzystanie obrazów.

W trybie Eksplorowanie musimy wczytać wiele obrazów, w tym miniatury prac, tekstury map na płyty bazowe, a na koniec same kompozycje 3D. Dokładamy wszelkich starań, aby podczas ciągłego wczytywania nowych obrazów nie doszło do wycieków pamięci.

Kompozycje 3D są przechowywane w niestandardowym formacie pliku spakowanego jako obraz PNG. Przechowywanie danych dotyczących kompozycji 3D zapisanych w postaci obrazów pozwoliło nam przekazać je bezpośrednio do cieniowania.

W przypadku wszystkich obrazów generowanych przez użytkowników mogliśmy wykorzystać ten sam rozmiar obrazów na wszystkich platformach, co pozwoliło zminimalizować wykorzystanie miejsca i pasma.

Zarządzanie orientacją ekranu

Łatwo zapomnieć, jak bardzo zmienia się współczynnik proporcji ekranu po przejściu z orientacji pionowej do poziomej lub odwrotnie. Pamiętaj o tym od razu, dostosowując się do urządzeń mobilnych.

W przypadku tradycyjnej witryny z włączonym przewijaniem możesz zastosować reguły CSS, aby uzyskać elastyczną witrynę, która zmienia układ treści i menu. Jeśli można używać funkcji przewijania, jest to całkiem łatwe.

Wykorzystaliśmy tę metodę również w Builder, ale byliśmy w ograniczonym zakresie, jeśli chodzi o układ, ponieważ musieliśmy zawsze wyświetlać zawartość strony i mieć szybki dostęp do wielu elementów sterujących i przycisków. W przypadku witryn zawierających wyłącznie treść, takich jak witryny z wiadomościami, płynny układ jest bardzo istotny, ale dla gier, takich jak nasza, było to kłopotliwe. Trudno było znaleźć układ, który działał zarówno w orientacji poziomej, jak i pionowej, a jednocześnie zapewniał dobre omówienie treści i wygodny sposób interakcji. Ostatecznie zdecydowaliśmy się, że aplikacja ma tylko poziomą, a użytkownik powinien obrócić urządzenie.

W obu orientacjach o wiele łatwiej było rozwiązać zadanie Eksplorowanie. Musieliśmy tylko dostosować poziom powiększenia widoku 3D w zależności od orientacji, aby uzyskać spójny obraz.

Większość układu treści jest kontrolowana przez CSS, ale pewne kwestie związane z orientacją trzeba zaimplementować w JavaScript. Zauważyliśmy, że nie ma dobrego rozwiązania obejmującego różne urządzenia, które pozwalałyby na określenie orientacji urządzenia za pomocą parametru window.orientation. Na koniec porównaliśmy wartości window.innerWidth i window.innerHeight w celu określenia orientacji urządzenia.

if( window.innerWidth > window.innerHeight ){
  //landscape
} else {
  //portrait
}

Dodawanie obsługi dotykowej

Dodanie obsługi dotykowej do treści internetowych jest dość proste. Podstawowa interaktywność, np. zdarzenie kliknięcia, działa tak samo na komputerach i urządzeniach z funkcją dotykową, ale w przypadku bardziej zaawansowanych interakcji trzeba też obsługiwać zdarzenia dotykowe: Touchstart, Touchmove i Touchend. W tym artykule znajdziesz podstawowe informacje o korzystaniu z tych zdarzeń. Internet Explorer nie obsługuje zdarzeń dotknięcia, tylko korzysta ze zdarzeń wskaźnika (wskaźnik, wskaźnik w dół, „wskaźnik” i „wskaźnik”). Zdarzenia wskaźnika zostały przesłane do W3C w celu standaryzacji, ale obecnie są wdrażane tylko w Internet Explorerze.

W trybie Eksplorowanie 3D chcieliśmy, aby ta sama nawigacja była taka sama jak w standardowej implementacji Map Google: przesuwanie mapy jednym palcem i ściąganie dwóch palców, by ją powiększyć. Ponieważ kompozycje są w 3D, dodaliśmy też gest obracania 2 palcami. Zwykle wymaga to użycia zdarzeń dotknięcia.

Warto unikać intensywnych obliczeń, np. aktualizowania lub renderowania obrazu 3D w modułach obsługi zdarzeń. Zamiast tego przechowuj dane dotykowe w zmiennej i reaguj na te dane w pętli renderowania requestAnimationFrame. Ułatwia to też jednoczesne implementowanie reklam za pomocą myszy – po prostu przechowujesz odpowiednie wartości myszy w tych samych zmiennych.

Zacznij od zainicjowania obiektu, w którym zapisywane są dane wejściowe, i dodaj odbiornik zdarzeń Touchstart. W każdym module obsługi zdarzeń wywołujemy event.preventDefault(). Dzięki temu przeglądarka nie przetwarza zdarzenia dotknięcia, co może spowodować nieoczekiwane zachowania, takie jak przewijanie lub skalowanie całej strony.

var input = {dragStartX:0, dragStartY:0, dragX:0, dragY:0, dragDX:0, dragDY:0, dragging:false};
plateContainer.addEventListener('touchstart', onTouchStart);

function onTouchStart(event) {
  event.preventDefault();
  if( event.touches.length === 1){
    handleDragStart(event.touches[0].clientX , event.touches[0].clientY);
    //start listening to all needed touchevents to implement the dragging
    document.addEventListener('touchmove', onTouchMove);
    document.addEventListener('touchend', onTouchEnd);
    document.addEventListener('touchcancel', onTouchEnd);
  }
}

function onTouchMove(event) {
  event.preventDefault();
  if( event.touches.length === 1){
    handleDragging(event.touches[0].clientX, event.touches[0].clientY);
  }
}

function onTouchEnd(event) {
  event.preventDefault();
  if( event.touches.length === 0){
    handleDragStop();
    //remove all eventlisteners but touchstart to minimize number of eventlisteners
    document.removeEventListener('touchmove', onTouchMove);
    document.removeEventListener('touchend', onTouchEnd);
    //also listen to touchcancel event to avoid unexpected behavior when switching tabs and some other situations
    document.removeEventListener('touchcancel', onTouchEnd);
  }
}

Dane wejściowe nie są przechowywane w modułach obsługi zdarzeń, ale w osobnych modułach obsługi: handleDragStart, handleDragging i handDragStop. Dzieje się tak, ponieważ chcemy mieć możliwość ich wywoływania także z modułów obsługi zdarzeń myszy. Pamiętaj, że choć jest to mało prawdopodobne, użytkownik może jednocześnie korzystać z funkcji dotyku i myszy. Zamiast zajmować się tą sprawą bezpośrednio, dbamy o to, by nic nie spowodowało zagrożenia.

function handleDragStart(x ,y ){
  input.dragging = true;
  input.dragStartX = input.dragX = x;
  input.dragStartY = input.dragY = y;
}

function handleDragging(x ,y ){
  if(input.dragging) {
    input.dragDX = x - input.dragX;
    input.dragDY = y - input.dragY;
    input.dragX = x;
    input.dragY = y;
  }
}

function handleDragStop(){
  if(input.dragging) {
    input.dragging = false;
    input.dragDX = 0;
    input.dragDY = 0;
  }
}

Podczas tworzenia animacji opartych na ruchu dotykowym przydaje się również możliwość zapisania ruchu delta od ostatniego zdarzenia. Użyliśmy tego na przykład jako parametru prędkości kamery podczas poruszania się po wszystkich uchwytach ściennych w obszarze Eksplorowanie, ponieważ nie przeciągasz uchwytów ściennych, ale faktycznie poruszasz kamerą.

function onAnimationFrame() {
  requestAnimationFrame( onAnimationFrame );

  //execute animation based on input.dragDX, input.dragDY, input.dragX or input.dragY
 /*
  /
  */

  //because touchmove is only fired when finger is actually moving we need to reset the delta values each frame
  input.dragDX=0;
  input.dragDY=0;
}

Przykład umieszczony na stronie: przeciąganie obiektu za pomocą zdarzeń dotknięcia. Podobna implementacja jak w przypadku przeciągania mapy 3D w Build with Chrome: http://cdpn.io/qDxvo

Gesty wielodotykowe

Istnieje kilka platform lub bibliotek, takich jak Hammer czy QuoJS, które mogą uprościć zarządzanie gestami wielodotykowymi. Jeśli jednak chcesz połączyć kilka gestów i uzyskać pełną kontrolę, czasami najlepiej jest zrobić to od zera.

Na potrzeby zarządzania gestami ściągnięcia i obrócenia zapisujemy odległość i kąt między dwoma palcami po umieszczeniu drugiego palca na ekranie:

//variables representing the actual scale/rotation of the object we are affecting
var currentScale = 1;
var currentRotation = 0;

function onTouchStart(event) {
  event.preventDefault();
  if( event.touches.length === 1){
    handleDragStart(event.touches[0].clientX , event.touches[0].clientY);
  }else if( event.touches.length === 2 ){
    handleGestureStart(event.touches[0].clientX, event.touches[0].clientY, event.touches[1].clientX, event.touches[1].clientY );
  }
}

function handleGestureStart(x1, y1, x2, y2){
  input.isGesture = true;
  //calculate distance and angle between fingers
  var dx = x2 - x1;
  var dy = y2 - y1;
  input.touchStartDistance=Math.sqrt(dx*dx+dy*dy);
  input.touchStartAngle=Math.atan2(dy,dx);
  //we also store the current scale and rotation of the actual object we are affecting. This is needed to support incremental rotation/scaling. We can't assume that an object is always the same scale when gesture starts.
  input.startScale=currentScale;
  input.startAngle=currentRotation;
}

W przypadku ruchu dotykowego stale mierzymy odległość i kąt między tymi dwoma palcami. Następnie do określania skali używana jest różnica między odległością początkową a bieżącą. Do określania kąta używana jest różnica między kątem początkowym a bieżącym.

function onTouchMove(event) {
  event.preventDefault();
  if( event.touches.length  === 1){
    handleDragging(event.touches[0].clientX, event.touches[0].clientY);
  }else if( event.touches.length === 2 ){
    handleGesture(event.touches[0].clientX, event.touches[0].clientY, event.touches[1].clientX, event.touches[1].clientY );
  }
}

function handleGesture(x1, y1, x2, y2){
  if(input.isGesture){
    //calculate distance and angle between fingers
    var dx = x2 - x1;
    var dy = y2 - y1;
    var touchDistance = Math.sqrt(dx*dx+dy*dy);
    var touchAngle = Math.atan2(dy,dx);
    //calculate the difference between current touch values and the start values
    var scalePixelChange = touchDistance - input.touchStartDistance;
    var angleChange = touchAngle - input.touchStartAngle;
    //calculate how much this should affect the actual object
    currentScale = input.startScale + scalePixelChange*0.01;
    currentRotation = input.startAngle+(angleChange*180/Math.PI);
    //upper and lower limit of scaling
    if(currentScale<0.5) currentScale = 0.5;
    if(currentScale>3) currentScale = 3;
  }
}

Potencjalnie można wykorzystać zmianę odległości między poszczególnymi zdarzeniami ruchu dotykowego w podobny sposób, jak w przykładzie dotyczącym przeciągania, ale ta metoda jest bardziej przydatna, gdy zależy Ci na ciągłym ruchu.

function onAnimationFrame() {
  requestAnimationFrame( onAnimationFrame );
  //execute transform based on currentScale and currentRotation
  /*
  /
  */

  //because touchmove is only fired when finger is actually moving we need to reset the delta values each frame
  input.dragDX=0;
  input.dragDY=0;
}

Jeśli chcesz, możesz też włączyć przeciąganie obiektu podczas wykonywania gestów ściągania i obracania. W takim przypadku jako danych wejściowych dla uchwytu przeciągania będzie potrzebny punkt środkowy między dwoma palcami.

Przykład z wbudowanym: obracanie i skalowanie obiektu w 2D. Podobnie jak w przypadku mapy w sekcji Eksplorowanie: http://cdpn.io/izloq

Obsługa myszy i dotyku na tym samym sprzęcie

Obecnie istnieje kilka laptopów, takich jak Chromebook Pixel, które obsługują zarówno mysz, jak i dotykowe wprowadzanie danych. Jeśli nie zachowasz ostrożności, mogą wystąpić pewne nieoczekiwane zachowania.

Ważne jest, aby nie tylko wykrywanie obsługi dotykowej i ignorowanie sterowania za pomocą myszy było możliwe do wykonania w tym samym czasie.

Jeśli w modułach obsługi zdarzeń dotyku nie używasz event.preventDefault(), będą też uruchamiane emulowane zdarzenia myszy, aby większość witryn zoptymalizowanych pod kątem dotyku nadal działała. Na przykład jedno kliknięcie ekranu może wywołać te zdarzenia w krótkiej kolejności i w tej kolejności:

  1. Touchstart
  2. Ruch dotykowy
  3. Touchend
  4. ruch kursora myszy|najechanie kursorem myszy (na obiekt)
  5. mousemove
  6. najechanie kursorem myszy
  7. mysz na ekranie
  8. click

W przypadku bardziej złożonych interakcji te zdarzenia myszy mogą spowodować nieoczekiwane zachowanie i zakłócić implementację. Często najlepiej jest używać interfejsu event.preventDefault() w modułach obsługi zdarzeń dotyku, a wprowadzanie danych myszą w osobnych modułach obsługi zdarzeń. Pamiętaj, że korzystanie z elementu event.preventDefault() w modułach obsługi zdarzeń dotykowych uniemożliwi też niektóre domyślne działania, takie jak przewijanie i zdarzenia kliknięcia.

„W Build with Chrome nie chcieliśmy, aby powiększenie nastąpiło po dwukrotnym dotknięciu strony, mimo że w większości przeglądarek jest to standard. Używamy więc tagu widocznego obszaru, by informować przeglądarkę, by nie powiększała ekranu, gdy użytkownik kliknie go dwukrotnie. Pozwala to też wyeliminować 300-ms opóźnienia kliknięcia, co zwiększa responsywność witryny. (Czas opóźnienia kliknięcia pozwala odróżnić jedno dotknięcie od dwukrotnego dotknięcia przy włączonym powiększeniu dwukrotnym).

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

Pamiętaj, że w przypadku korzystania z tej funkcji to Ty musisz zadbać o to, aby strona była czytelna na wszystkich ekranach, bo użytkownik nie będzie mógł powiększyć jej.

Sterowanie za pomocą myszy, dotyku i klawiatury

W trybie Odkrywanie 3D chcieliśmy udostępnić trzy sposoby nawigacji po mapie: mysz (przeciąganie), dotyk (przeciąganie, ściąganie, aby powiększyć i obrócić) oraz klawiaturę (nawigowanie za pomocą klawiszy strzałek). Wszystkie te metody nawigacji działają nieco inaczej, ale we wszystkich zastosowaliśmy to samo podejście – ustawialiśmy zmienne w modułach obsługi zdarzeń i wykorzystywaliśmy je w pętli requestAnimationFrame. Pętla requestAnimationFrame nie musi wiedzieć, która metoda jest używana do nawigacji.

Możemy na przykład ustawić w ruchu mapy (dragDX i dragDY) za pomocą wszystkich trzech metod wprowadzania. Oto implementacja klawiatury:

document.addEventListener('keydown', onKeyDown );
document.addEventListener('keyup', onKeyUp );

function onKeyDown( event ) {
  input.keyCodes[ "k" + event.keyCode ] = true;
  input.shiftKey = event.shiftKey;
}

function onKeyUp( event ) {
  input.keyCodes[ "k" + event.keyCode ] = false;
  input.shiftKey = event.shiftKey;
}

//this needs to be called every frame before animation is executed
function handleKeyInput(){
  if(input.keyCodes.k37){
    input.dragDX = -5; //37 arrow left
  } else if(input.keyCodes.k39){
    input.dragDX = 5; //39 arrow right
  }
  if(input.keyCodes.k38){
    input.dragDY = -5; //38 arrow up
  } else if(input.keyCodes.k40){
    input.dragDY = 5; //40 arrow down
  }
}

function onAnimationFrame() {
  requestAnimationFrame( onAnimationFrame );
  //because keydown events are not fired every frame we need to process the keyboard state first
  handleKeyInput();
  //implement animations based on what is stored in input
   /*
  /
  */

  //because touchmove is only fired when finger is actually moving we need to reset the delta values each frame
  input.dragDX = 0;
  input.dragDY = 0;
}

Przykład umieszczony na stronie: poruszanie się za pomocą myszy, dotyku i klawiatury: http://cdpn.io/catlf

Podsumowanie

Dostosowanie Build with Chrome pod kątem obsługi urządzeń dotykowych o różnych rozmiarach ekranów okazał się przydatnym doświadczeniem. Nasz zespół nie miał zbyt dużego doświadczenia w zakresie reagowania na takie elementy interaktywne na urządzeniach dotykowych, a przy okazji wiele się nauczyło.

Największym wyzwaniem okazało się więc zadbanie o wrażenia użytkownika i projektowanie strony. Wyzwaniami technicznymi była radzenie sobie z rozmiarami ekranów o wielu rozmiarach, zdarzeniami dotknięciami i problemami z wydajnością.

Choć korzystanie z cieniowania WebGL na urządzeniach dotykowych wymagało od nas pewnych wyzwań, okazało się, że efekt był niemal lepszy niż oczekiwano. Urządzenia stają się coraz bardziej wydajne, a implementacje WebGL działają gwałtownie. Mamy wrażenie, że w najbliższej przyszłości będziemy korzystać z WebGL na urządzeniach znacznie częściej.

Jeśli jeszcze tego nie zrobiłeś, stwórz coś niesamowitego.