Mit Chrome entwickeln

LEGO®-Bausteine im Multi-Device-Web

Hans Eklund
Hans Eklund

Build with Chrome, ein unterhaltsames Experiment für Chrome-Desktop-Nutzer, das ursprünglich in Australien eingeführt wurde, wurde 2014 noch einmal veröffentlicht und weltweit verfügbar gemacht. Außerdem wurde es in Verbindung mit THE LEGO® MovieTM hergestellt und nun auch Mobilgeräte unterstützt. In diesem Artikel stellen wir einige Erkenntnisse aus dem Projekt vor, insbesondere in Bezug auf den Wechsel von einer reinen Desktop-Funktion zu einer Multi-Screen-Lösung, die sowohl die Maus als auch die Touch-Eingabe unterstützt.

Die Geschichte von Build with Chrome

Die erste Version von Build with Chrome kam 2012 in Australien auf den Markt. Wir wollten die Möglichkeiten des Webs auf ganz neue Weise demonstrieren und Chrome einer ganz neuen Zielgruppe zugänglich machen.

Die Website bestand aus zwei Hauptteilen: dem „Build“-Modus, in dem Nutzer anhand von LEGO-Steinen erschaffen können, und dem „Explore“-Modus, in dem die Kreationen in einer LEGO-Version von Google Maps zu sehen sind.

Interaktives 3D war ein wesentlicher Faktor, um den Nutzern das beste Erlebnis beim Bauen von LEGO zu bieten. 2012 war WebGL nur in Desktop-Browsern öffentlich verfügbar, sodass Build nur für Desktop-Computer entwickelt wurde. Explore verwendete Google Maps, um die Kreationen anzuzeigen, aber wenn es nah genug herangezoomt hatte, wechselte es zu einer WebGL-Implementierung der Karte, die die Kreationen in 3D zeigte. Dabei wurden weiterhin Google Maps als Grundplattentextur verwendet. Unser Ziel war es, eine Umgebung zu schaffen, in der LEGO-Fans aller Altersgruppen ihre Kreativität und die Kreationen der anderen einfach und intuitiv ausleben können.

2013 haben wir beschlossen, Build with Chrome auf neue Webtechnologien auszuweiten. Zu diesen Technologien gehörte WebGL in Chrome für Android. Damit wurde „Build with Chrome“ automatisch zu einer mobilen Version. Zunächst haben wir Touch-Prototypen entwickelt, bevor wir die Hardware für das "Builder-Tool" infrage gestellt haben, um das Gestenverhalten und die haptische Reaktionsfähigkeit eines Browsers im Vergleich zu einer mobilen App zu verstehen.

Ein responsives Frontend

Wir mussten Geräte mit Touch- und Mauseingabe unterstützen. Die Verwendung derselben Benutzeroberfläche auf kleinen Touchscreens erwies sich jedoch aufgrund von Platzbeschränkungen als suboptimal.

In Build findet eine Menge Interaktivität statt: Heran- und Herauszoomen, Ändern der Steinfarben und natürlich das Auswählen, Drehen und Platzieren von Bausteinen. Da Nutzende oft viel Zeit verbringen, ist es wichtig, dass sie schnell auf alles zugreifen können, was sie häufig verwenden, und dass sie problemlos damit interagieren können.

Beim Entwerfen einer stark interaktiven Touch-Anwendung werden Sie feststellen, dass sich der Bildschirm schnell klein anfühlt und die Finger der Nutzenden während der Interaktion einen großen Teil des Bildschirms bedecken. Das wurde uns bei der Arbeit mit dem Builder offensichtlich. Beim Design müssen Sie wirklich die physische Bildschirmgröße und nicht die Pixel in der Grafik berücksichtigen. Es ist wichtig, die Anzahl der Schaltflächen und Steuerelemente zu minimieren, um möglichst viel Platz auf dem Bildschirm für den eigentlichen Inhalt zu haben.

Unser Ziel war es, dafür zu sorgen, dass sich Build auf Touch-Geräten natürlich anfühlt. Dabei wird nicht nur die Eingabe per Berührung der ursprünglichen Desktop-Implementierung hinzugefügt, sondern auch so gestaltet, dass es sich so anfühlt, als wäre es für Touch-Geräte gedacht. Schließlich haben wir zwei Varianten der Benutzeroberfläche entwickelt: eine für Desktop-Computer und Tablets mit großen Bildschirmen und eine für Mobilgeräte mit kleineren Bildschirmen. Wenn möglich, empfiehlt es sich, eine einzelne Implementierung zu verwenden und einen fließenden Wechsel zwischen den Modi zu ermöglichen. In unserem Fall stellten wir fest, dass es einen so großen Unterschied in der Erfahrung zwischen diesen beiden Modi gibt, dass wir uns für einen bestimmten Haltepunkt entschieden haben. Die beiden Versionen haben viele Funktionen gemeinsam und wir haben versucht, die meisten Dinge mit nur einer Codeimplementierung zu tun, aber einige Aspekte der Benutzeroberfläche funktionieren zwischen den beiden unterschiedlich.

Wir verwenden User-Agent-Daten, um Mobilgeräte zu erkennen, und prüfen dann die Größe des Darstellungsbereichs, um zu entscheiden, ob die mobile Benutzeroberfläche mit kleinem Bildschirm verwendet werden soll. Es ist etwas schwierig, einen Haltepunkt für einen „großen Bildschirm“ auszuwählen, da es schwierig ist, einen zuverlässigen Wert für die physische Bildschirmgröße zu erhalten. Glücklicherweise spielt es in unserem Fall keine Rolle, ob wir die Benutzeroberfläche des kleinen Bildschirms auf einem Touch-Gerät mit einem großen Bildschirm anzeigen, da das Tool trotzdem einwandfrei funktioniert, nur dass sich einige der Schaltflächen etwas zu groß anfühlen. Am Ende legen wir den Haltepunkt auf 1.000 Pixel fest. Wenn Sie die Website aus einem Fenster laden, das mehr als 1.000 Pixel umfasst (im Querformat), erhalten Sie die Version mit dem großen Bildschirm.

Sprechen wir ein wenig über die beiden Bildschirmgrößen und -funktionen:

Großer Bildschirm mit Maus- und Touch-Unterstützung

Die Version mit großem Bildschirm wird für alle Desktop-Computer mit Mausunterstützung und für Touchgeräte mit großen Bildschirmen (wie das Google Nexus 10) bereitgestellt. Diese Version entspricht weitgehend der ursprünglichen Desktop-Lösung hinsichtlich der verfügbaren Navigationssteuerelemente. Wir haben jedoch Touch-Unterstützung und einige Touch-Gesten hinzugefügt. Wir passen die Benutzeroberfläche in Abhängigkeit von der Fenstergröße an. Wenn also ein Benutzer die Größe des Fensters ändert, wird möglicherweise ein Teil der Benutzeroberfläche entfernt oder ihre Größe verändert. Dazu verwenden wir CSS-Medienabfragen.

Beispiel: Wenn die verfügbare Höhe weniger als 730 Pixel beträgt, wird das Steuerelement für den Zoomregler im Modus „Erkunden“ ausgeblendet:

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

Kleines Display, nur Touchbedienung

Diese Version wird für Mobilgeräte und kleine Tablets bereitgestellt, die auf das Nexus 4 und das Nexus 7 ausgerichtet sind. Für diese Version ist Multi-Touch-Unterstützung erforderlich.

Auf den Geräten mit kleinen Bildschirmen müssen wir dem Inhalt so viel Platz auf dem Bildschirm wie möglich geben. Daher haben wir einige Optimierungen vorgenommen, um den Platz zu maximieren, hauptsächlich indem wir selten verwendete Elemente aus dem sichtbaren Bereich verschoben haben:

  • Die Option „Bausteine erstellen“ wird beim Bauen zu einer Farbauswahl minimiert.
  • Wir haben die Steuerelemente für Zoom und Ausrichtung durch Multi-Touch-Gesten ersetzt.
  • Die Chrome-Vollbildfunktion ist außerdem hilfreich, um zusätzlichen Platz auf dem Bildschirm zu erhalten.
Auf großen Bildschirmen arbeiten
Nutzen Sie den großen Bildschirm Die Backsteinauswahl ist immer sichtbar. Auf der rechten Seite befinden sich einige Steuerelemente.
Auf kleinen Bildschirmen arbeiten
Auf dem kleinen Display arbeiten Die Backsteinauswahl ist minimiert und einige Schaltflächen wurden entfernt.

WebGL – Leistung und Support

Moderne Touch-Geräte verfügen über recht leistungsstarke GPUs, sind aber noch weit von ihren Desktop-Gegenstücken entfernt. Wir wussten also, dass wir einige Herausforderungen in Bezug auf die Leistung haben werden, insbesondere im 3D-Modus „Explore“, wo wir viele Kreationen gleichzeitig rendern müssen.

Aus kreativer Sicht wollten wir einige neue Arten von Bausteinen mit komplexen Formen und sogar Transparenz hinzufügen – Funktionen, die die GPU in der Regel stark belasten. Wir mussten jedoch abwärtskompatibel sein und Kreationen ab der ersten Version unterstützen. Daher konnten wir keine neuen Einschränkungen festlegen, z. B. die Gesamtzahl der Bausteine in den Kreationen erheblich reduzieren.

In der ersten Version von Build gab es eine Höchstanzahl von Bausteinen, die in einer Kreation verwendet werden konnten. Es gab einen „Bausteinmeter“, der anzeigt, wie viele Bausteine übrig geblieben sind. Bei der neuen Implementierung hatten einige der neuen Bausteine eine stärkere Auswirkung auf den Bausteinmeter als die Standardbausteine, wodurch sich die maximale Anzahl der Bausteine geringfügig verringerte. Das war eine Möglichkeit, neue Bausteine zu installieren und gleichzeitig eine ordentliche Leistung zu erzielen.

Im 3D-Modus „Erkunden“ ist ziemlich viel gleichzeitig los: Das Laden von Texturen der Grundplatte, das Laden von Kreationen, das Animieren und Rendern von Kreationen und vieles mehr. Dies erfordert eine Menge sowohl von der GPU als auch von der CPU. Deshalb haben wir in den Chrome-Entwicklertools viel Frame-Profiling durchgeführt, um diese Teile so weit wie möglich zu optimieren. Für Mobilgeräte haben wir beschlossen, die Kreationen etwas näher heranzuzoomen, damit nicht so viele Kreationen gleichzeitig gerendert werden müssen.

Bei einigen Geräten mussten wir einige WebGL-Shader überprüfen und vereinfachen, aber wir fanden immer einen Weg, das Problem zu lösen und weiterzumachen.

Unterstützung für Nicht-WebGL-Geräte

Wir wollten die Website nutzerfreundlich gestalten, auch wenn das Gerät des Besuchers WebGL nicht unterstützt. Manchmal gibt es Möglichkeiten, 3D mit einer Canvas-Lösung oder CSS3D-Funktionen vereinfacht darzustellen. Leider haben wir keine geeignete Lösung gefunden, um 3D-Funktionen vom Typ „Build“ und „Erkunden“ ohne WebGL zu replizieren.

Aus Gründen der Einheitlichkeit muss der visuelle Stil der Kreationen auf allen Plattformen gleich sein. Wir hätten eventuell eine 2,5D-Lösung ausprobieren können, doch dadurch hätten die Kreationen in irgendeiner Weise anders aussehen. Wir mussten uns auch überlegen, wie wir sicherstellen können, dass Kreationen, die mit der ersten Version von Build with Chrome erstellt wurden, in der neuen Version der Website genauso aussehen und genauso reibungslos funktionieren wie in der ersten Version.

Der 2D-Modus ist weiterhin für Geräte ohne WebGL verfügbar, auch wenn Sie keine neuen Kreationen erstellen oder in 3D erkunden können. So können sich die Nutzer trotzdem eine Vorstellung von der Tiefe des Projekts und von den Inhalten machen, die sie mit diesem Tool erstellen könnten, wenn sie ein WebGL-fähiges Gerät verwenden würden. Für Nutzer ohne WebGL-Unterstützung ist die Website möglicherweise nicht so wertvoll, aber sie sollte zumindest ein kleiner Vorgeschmack sein, der die Nutzer zum Ausprobieren motiviert.

Manchmal ist es nicht möglich, Fallback-Versionen für WebGL-Lösungen beizubehalten. Es gibt viele mögliche Gründe, z. B. Leistung, visuellen Stil, Entwicklungs- und Wartungskosten. Wenn Sie sich gegen die Implementierung eines Fallbacks entscheiden, sollten Sie sich zumindest um die Besucher kümmern, die WebGL nicht unterstützen, erklären, warum sie nicht vollständig auf die Website zugreifen können, und Anweisungen geben, wie sie das Problem mithilfe eines Browsers lösen können, der WebGL unterstützt.

Vermögensverwaltung

2013 hat Google eine neue Version von Google Maps mit den wichtigsten Änderungen an der Benutzeroberfläche seit der Einführung eingeführt. Deshalb haben wir uns entschieden, Build with Chrome so umzugestalten, dass es zur neuen Benutzeroberfläche von Google Maps passt. Dabei wurden auch weitere Faktoren berücksichtigt. Das neue Design ist relativ flach mit klaren, klaren Farben und einfachen Formen. So konnten wir für viele UI-Elemente reines CSS verwenden und dabei die Verwendung von Bildern minimieren.

Im Explore müssen wir viele Bilder laden: Miniaturansichten der Kreationen, Kartentexturen für die Grundplatten und schließlich die eigentlichen 3D-Kreationen. Wir achten sehr darauf, dass keine Speicherlecks entstehen, wenn wir ständig neue Bilder laden.

Die 3D-Kreationen werden in einem benutzerdefinierten Dateiformat gespeichert und als PNG-Bild gepackt. Da die Daten der 3D-Kreationen als Bild gespeichert waren, konnten wir die Daten direkt an die Shader weitergeben, die die Kreationen rendern.

Dank des Designs konnten wir für alle von Nutzern erstellten Bilder dieselben Bildgrößen für alle Plattformen verwenden und so den Speicherplatz- und Bandbreitenverbrauch minimieren.

Bildschirmausrichtung verwalten

Man vergisst schnell, wie sehr sich das Seitenverhältnis des Bildschirms ändert, wenn du vom Hoch- ins Querformat oder umgekehrt wechselst. Dies müssen Sie von Anfang an berücksichtigen, wenn Sie Ihre Kampagnen für Mobilgeräte anpassen.

Auf einer herkömmlichen Website mit aktiviertem Scrollen können Sie CSS-Regeln anwenden, um eine responsive Website zu erhalten, die Inhalte und Menüs neu anordnet. Solange du die Scrollfunktion nutzen kannst, ist das recht überschaubar.

Wir haben diese Methode auch mit Build verwendet, waren jedoch etwas begrenzt, um das Layout zu lösen, da wir den Inhalt immer sichtbar haben und trotzdem schnellen Zugriff auf eine Reihe von Steuerelementen und Schaltflächen haben mussten. Für reine Content-Websites wie Nachrichtenwebsites ist ein fließendes Layout sinnvoll, aber für eine Spiele-App wie unsere war es eine Herausforderung. Es wurde zu einer Herausforderung, ein Layout zu finden, das sowohl im Quer- als auch im Hochformat funktionierte, aber dennoch einen guten Überblick über den Inhalt und eine angenehme Art der Interaktion behielt. Am Ende haben wir uns entschieden, Build nur im Querformat zu verwenden, und wir bitten den Nutzer, sein Gerät zu drehen.

Das Erkunden war in beiden Ausrichtungen deutlich einfacher zu lösen. Wir mussten nur die Zoomstufe des 3D je nach Ausrichtung anpassen, um ein einheitliches Erlebnis zu schaffen.

Der größte Teil des Inhaltslayouts wird von CSS gesteuert, aber einige Dinge bezüglich der Ausrichtung mussten in JavaScript implementiert werden. Wir haben festgestellt, dass es keine gute geräteübergreifende Lösung zur Verwendung von window.orientation zur Ermittlung der Ausrichtung gibt. Am Ende haben wir also nur window.innerWidth und window.innerHeight verglichen, um die Ausrichtung des Geräts zu ermitteln.

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

Touch-Unterstützung hinzufügen

Das Hinzufügen von Touch-Unterstützung zu Webinhalten ist relativ einfach. Grundlegende Interaktivität, wie das Klickereignis, funktioniert auf Desktop-Computern und Geräten mit Touchscreen gleich. Bei komplexeren Interaktionen müssen Sie jedoch auch die Touch-Ereignisse „touchstart“, „touchmove“ und „touchend“ verarbeiten. In diesem Artikel finden Sie grundlegende Informationen zur Verwendung dieser Ereignisse. Internet Explorer unterstützt keine Touch-Ereignisse, verwendet stattdessen Pointer-Ereignisse (pointerdown, pointermove, pointerup). Zeiger-Ereignisse wurden zur Standardisierung an W3C übermittelt, werden derzeit aber nur im Internet Explorer implementiert.

Im 3D-Explore-Modus war die gleiche Navigation wie bei der Standardimplementierung von Google Maps vorgesehen: mit einem Finger die Karte schwenken und mit zwei Fingern heran- bzw. herauszoomen. Da die Kreationen in 3D vorliegen, haben wir auch eine Drehbewegung mit zwei Fingern hinzugefügt. Hierzu werden normalerweise Touch-Ereignisse benötigt.

Es empfiehlt sich, ressourcenintensive Daten wie das Aktualisieren oder Rendern von 3D in den Event-Handlern zu vermeiden. Speichern Sie stattdessen die Eingabe per Berührung in einer Variablen und reagieren Sie auf die Eingabe in der Rendering-Schleife "requestAnimationFrame". Das macht es auch einfacher, gleichzeitig eine Mausimplementierung zu implementieren, da Sie lediglich die entsprechenden Mauswerte in denselben Variablen speichern.

Beginnen Sie mit der Initialisierung eines Objekts, in dem die Eingabe gespeichert werden soll, und fügen Sie den Touchstart-Ereignis-Listener hinzu. In jedem Event-Handler wird „event.preventDefault()“ aufgerufen. Dadurch wird verhindert, dass der Browser das Touch-Ereignis weiter verarbeitet, was zu unerwartetem Verhalten wie Scrollen oder Skalieren der gesamten Seite führen kann.

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);
  }
}

Die Eingabe wird nicht tatsächlich in den Event-Handlern gespeichert, sondern in separaten Handlern: handleDragStart, handleDragging und handleDragStop. Der Grund dafür ist, dass wir diese auch von den Maus-Event-Handlern aufrufen können möchten. Denken Sie daran, dass es zwar unwahrscheinlich ist, dass der Nutzer Touch und Maus gleichzeitig verwenden kann. Anstatt diesen Fall direkt zu bearbeiten, sorgen wir nur dafür, dass nichts zu schnell wird.

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

Bei Animationen, die auf „Touchmove“ basieren, ist es oft nützlich, auch die Deltabewegungen seit dem letzten Ereignis zu speichern. Wir haben dies beispielsweise als Parameter für die Geschwindigkeit der Kamera verwendet, wenn sie sich über alle Basisplatten in Explore bewegt, da Sie nicht die Halterungen ziehen, sondern die Kamera tatsächlich bewegen.

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

Eingebettetes Beispiel:Ziehen eines Objekts mithilfe von Touch-Ereignissen Ähnliche Implementierung wie beim Ziehen der 3D-Karte in Build with Chrome: http://cdpn.io/qDxvo

Multi-Touch-Gesten

Es gibt verschiedene Frameworks oder Bibliotheken wie Hammer oder QuoJS, die die Verwaltung von Multi-Touch-Gesten vereinfachen können. Wenn Sie jedoch mehrere Gesten kombinieren und volle Kontrolle haben möchten, ist es manchmal am besten, sie von Grund auf neu zu erstellen.

Zum Verwalten der Touch-Gesten zum Auseinander- und Zusammenziehen und Drehen speichern wir den Abstand und den Winkel zwischen zwei Fingern, wenn der zweite Finger auf den Bildschirm gelegt wird:

//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;
}

Beim Touchmove-Ereignis messen wir dann kontinuierlich den Abstand und den Winkel zwischen diesen beiden Fingern. Die Differenz zwischen der Startstrecke und der aktuellen Entfernung wird dann verwendet, um die Skala einzustellen, und die Differenz zwischen dem Startwinkel und dem aktuellen Winkel wird zum Festlegen des Winkels verwendet.

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

Sie könnten die Entfernungsänderung zwischen den einzelnen Touchmove-Ereignissen ähnlich wie im Beispiel mit Drag verwenden. Diese Methode ist jedoch häufig sinnvoller, wenn Sie eine kontinuierliche Bewegung wünschen.

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

Sie können auch das Ziehen des Objekts aktivieren, während Sie Auseinander- und Zusammenziehen und Drehen ausführen. In diesem Fall verwenden Sie den Mittelpunkt zwischen den beiden Fingern als Eingabe für den Zieh-Handler.

Eingebettetes Beispiel:Drehen und skalieren Sie ein Objekt in 2D. Ähnlich wie die Implementierung der Karte in Explore: http://cdpn.io/izloq

Unterstützung für Maus und Touch auf derselben Hardware

Heute gibt es mehrere Laptops, die sowohl die Maus als auch die Touchbedienung unterstützen, darunter das Chromebook Pixel. Wenn Sie nicht vorsichtig sind, kann dies zu unerwartetem Verhalten führen.

Wichtig ist, dass Sie nicht nur die Unterstützung durch Berührung erkennen und dann die Mauseingabe ignorieren, sondern beide gleichzeitig unterstützen.

Wenn du event.preventDefault() nicht in deinen Touch-Event-Handlern verwendest, werden auch einige emulierte Mausereignisse ausgelöst, damit die meisten Websites ohne Touchscreen weiterhin funktionieren. Beispielsweise können diese Ereignisse durch einmaliges Tippen auf den Bildschirm in einer schnellen Abfolge und in dieser Reihenfolge ausgelöst werden:

  1. Touchstart
  2. Touchmove
  3. Touchende
  4. Mouseover
  5. mousemove
  6. Mousedown
  7. Mouseup
  8. Klick

Bei etwas komplexeren Interaktionen können diese Mausereignisse unerwartetes Verhalten verursachen und Ihre Implementierung durcheinanderbringen. Häufig ist es am besten, event.preventDefault() in den Touch-Event-Handlern zu verwenden und die Mauseingabe in separaten Event-Handlern zu verwalten. Beachten Sie, dass durch die Verwendung von event.preventDefault() in Touch-Event-Handlern auch einige Standardfunktionen wie Scrollen und das Klickereignis verhindert werden.

„In Build with Chrome wollten wir nicht, dass Nutzer zoomen, wenn jemand zweimal auf die Website tippt, obwohl das in den meisten Browsern der Standard ist. Daher verwenden wir das Meta-Tag für den Darstellungsbereich, um den Browser anzuweisen, beim zweimaligen Tippen nicht zu zoomen. Dadurch entfällt auch die Klickverzögerung von 300 ms, was die Reaktionszeit der Website verbessert. Die Klickverzögerung dient dazu, zwischen einmaligem Tippen und Doppeltippen zu unterscheiden, wenn das Zoomen durch zweimaliges Tippen aktiviert ist.

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

Denken Sie daran: Wenn Sie diese Funktion verwenden, liegt es an Ihnen, die Website auf allen Bildschirmgrößen lesbar zu machen, da der Nutzer dann nicht näher heranzoomen kann.

Maus-, Touch- und Tastatureingabe

Im 3D-Modus "Erkunden" gibt es drei Möglichkeiten zur Navigation in der Karte: Maus (Ziehen), Berühren (Ziehen, Auseinander- und Zusammenziehen zum Zoomen und Drehen) und Tastatur (Mit den Pfeiltasten navigieren). All diese Navigationsmethoden funktionieren etwas anders, aber wir haben bei allen den gleichen Ansatz verwendet. Dabei wurden Variablen in Event-Handlern festgelegt und in der requestAnimationFrame-Schleife entsprechend angepasst. Die requestAnimationFrame-Schleife muss nicht wissen, welche Methode zum Navigieren verwendet wird.

Wir können beispielsweise die Bewegung der Karte („dragDX“ und „DragDY“) mit allen drei Eingabemethoden festlegen. Hier ist die Tastaturimplementierung:

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

Eingebettetes Beispiel:Zum Navigieren mit Maus, Touch und Tastatur: http://cdpn.io/catlf

Zusammenfassung

Die Anpassung von Build with Chrome an Touch-Geräte mit vielen verschiedenen Bildschirmgrößen war eine Lernerfahrung. Das Team hatte nicht viel Erfahrung mit dieser Interaktivität auf Touch-Geräten und wir haben dabei viel gelernt.

Die größte Herausforderung bestand darin, wie die User Experience und das Design gelöst werden konnten. Die technischen Herausforderungen bestand darin, viele Bildschirmgrößen, Touchereignisse und Leistungsprobleme zu bewältigen.

Obwohl es bei den WebGL-Shadern auf Touchgeräten einige Probleme gab, funktionierte dies fast besser als erwartet. Die Geräte werden immer leistungsfähiger und WebGL-Implementierungen werden immer besser. Wir gehen davon aus, dass wir WebGL in naher Zukunft viel häufiger auf Geräten nutzen werden.

Wenn du es noch nicht getan hast, kannst du jetzt dann etwas Tolles schaffen!