Einführung
Um das Interesse von Entwicklern auf der Google I/O 2013-Website zu wecken, bevor die Konferenz sich eröffnet hat, haben wir eine Reihe von Mobile-First-Experimenten und -Spielen entwickelt, die sich auf Touch-Interaktionen, generatives Audio und die Freude des Entdeckens konzentrieren. Diese interaktive Erfahrung, die vom Potenzial von Code und der Kraft des Spielens inspiriert ist, beginnt mit den einfachen Tönen „I“ und „O“, wenn Sie auf das neue I/O-Logo tippen.
Organic Motion
Wir haben uns entschieden, die Animationen für „I“ und „O“ mit einem wackeligen, organischen Effekt zu implementieren, der bei HTML5-Interaktionen nicht oft zu sehen ist. Es hat ein wenig gedauert, die Optionen so einzustellen, dass es Spaß macht und reaktionsschnell ist.
Beispiel für Code für elastische Physik
Um diesen Effekt zu erzielen, haben wir eine einfache Physiksimulation auf eine Reihe von Punkten angewendet, die die Kanten der beiden Formen darstellen. Wenn auf eine der Formen getippt wird, werden alle Punkte vom Ort des Tippens weg beschleunigt. Sie strecken sich nach vorne und zur Seite, bevor sie wieder eingezogen werden.
Bei der Instanziierung erhält jeder Punkt eine zufällige Beschleunigung und einen zufälligen Rückprall, damit sie sich nicht einheitlich animieren, wie in diesem Code zu sehen ist:
this.paperO_['vectors'] = [];
// Add an array of vector points and properties to the object.
for (var i = 0; i < this.paperO_['segments'].length; i++) {
var point = this.paperO_['segments'][i]['point']['clone']();
point = point['subtract'](this.oCenter);
point['velocity'] = 0;
point['acceleration'] = Math.random() * 5 + 10;
point['bounce'] = Math.random() * 0.1 + 1.05;
this.paperO_['vectors'].push(point);
}
Wenn sie dann angetippt werden, werden sie mit dem folgenden Code von der Position des Tippens nach außen beschleunigt:
for (var i = 0; i < path['vectors'].length; i++) {
var point = path['vectors'][i];
var vector;
var distance;
if (path === this.paperO_) {
vector = point['add'](this.oCenter);
vector = vector['subtract'](clickPoint);
distance = Math.max(0, this.oRad - vector['length']);
} else {
vector = point['add'](this.iCenter);
vector = vector['subtract'](clickPoint);
distance = Math.max(0, this.iWidth - vector['length']);
}
point['length'] += Math.max(distance, 20);
point['velocity'] += speed;
}
Schließlich wird mit diesem Codeansatz jedes Partikel in jedem Frame verlangsamt und langsam wieder ins Gleichgewicht gebracht:
for (var i = 0; i < path['segments'].length; i++) {
var point = path['vectors'][i];
var tempPoint = new paper['Point'](this.iX, this.iY);
if (path === this.paperO_) {
point['velocity'] = ((this.oRad - point['length']) /
point['acceleration'] + point['velocity']) / point['bounce'];
} else {
point['velocity'] = ((tempPoint['getDistance'](this.iCenter) -
point['length']) / point['acceleration'] + point['velocity']) /
point['bounce'];
}
point['length'] = Math.max(0, point['length'] + point['velocity']);
}
Demo für organische Bewegung
Hier ist der I/O-Zuhausemodus, mit dem Sie herumspielen können. Wir haben auch eine Reihe zusätzlicher Optionen für diese Implementierung vorgestellt. Wenn Sie „Punkte anzeigen“ aktivieren, sehen Sie die einzelnen Punkte, auf die die Physiksimulation und die Kräfte wirken.
Neugestaltung
Nachdem wir mit der Bewegung im Modus „Zuhause“ zufrieden waren, wollten wir denselben Effekt für zwei Retro-Modi verwenden: „Achtbit“ und „Ascii“.
Dazu haben wir denselben Canvas wie im Modus „Zuhause“ verwendet und die Pixeldaten genutzt, um die beiden Effekte zu generieren. Dieser Ansatz erinnert an einen OpenGL-Fragment-Shader, bei dem jedes Pixel der Szene geprüft und manipuliert wird. Sehen wir uns das genauer an.
Codebeispiel für Canvas-Shader
Pixel auf einem Canvas können mit der Methode getImageData
gelesen werden. Das zurückgegebene Array enthält 4 Werte pro Pixel, die den RGBA-Wert jedes Pixels darstellen. Diese Pixel sind in einer riesigen, arraysähnlichen Struktur aneinandergereiht. Ein Canvas mit 2 x 2 Pixeln hat beispielsweise 4 Pixel und 16 Einträge im Array „imageData“.
Unser Canvas-Element wird im Vollbildmodus angezeigt. Wenn wir also wie auf einem iPad eine Größe von 1024 x 768 annehmen, umfasst das Array 3.145.728 Einträge. Da es sich um eine Animation handelt, wird das gesamte Array 60-mal pro Sekunde aktualisiert. Moderne JavaScript-Engines können diese Datenmenge schnell genug verarbeiten, um die Framerate konstant zu halten. Tipp: Versuchen Sie nicht, diese Daten in der Entwicklerkonsole zu protokollieren, da dies das Crawlen Ihres Browsers verlangsamt oder ihn vollständig abstürzt.
So funktioniert der Eightbit-Modus:
var pixelData = pctx.getImageData(0, 0, sourceCanvas.width, sourceCanvas.height);
// tctx is the Target Context for the output Canvas element
tctx.clearRect(0, 0, targetCanvas.width + 1, targetCanvas.height + 1);
var size = ~~(this.width_ * 0.0625);
if (this.height_ * 6 < this.width_) {
size /= 8;
}
var increment = Math.min(Math.round(size * 80) / 4, 980);
for (i = 0; i < pixelData.data.length; i += increment) {
if (pixelData.data[i + 3] !== 0) {
var r = pixelData.data[i];
var g = pixelData.data[i + 1];
var b = pixelData.data[i + 2];
var pixel = Math.ceil(i / 4);
var x = pixel % this.width_;
var y = Math.floor(pixel / this.width_);
var color = 'rgba(' + r + ', ' + g + ', ' + b + ', 1)';
tctx.fillStyle = color;
/**
* The ~~ operator is a micro-optimization to round a number down
* without using Math.floor. Math.floor has to look up the prototype
* tree on every invocation, but ~~ is a direct bitwise operation.
*/
tctx.fillRect(x - ~~(size / 2), y - ~~(size / 2), size, size);
}
}
Eightbit Shader Demo
Unten sehen wir das Eightbit-Overlay ohne die Originalanimation. Die Option „Bildschirm beenden“ zeigt Ihnen einen seltsamen Effekt, auf den wir durch eine falsche Erfassung der Quellpixel gestoßen sind. Wir haben es schließlich als „responsives“ Osterei verwendet, wenn der Eightbit-Modus auf unwahrscheinliche Seitenverhältnisse skaliert wird. Alles Gute!
Canvas-Compositing
Es ist ziemlich erstaunlich, was Sie mit mehreren Renderingschritten und Masken erreichen können. Wir haben eine 2D-Metakugel erstellt, für die jede Kugel einen eigenen radialen Farbverlauf haben muss und diese Farbverläufe an den Überlappungen der Kugeln zusammengeführt werden müssen. (Siehe Demo unten.)
Dazu haben wir zwei separate Canvasse verwendet. Im ersten Canvas wird die Metaball-Form berechnet und gezeichnet. Ein zweites Canvas zeichnet an jeder Ballposition radiale Farbverläufe. Dann maskiert die Form die Farbverläufe und wir rendern die endgültige Ausgabe.
Beispiel für Code zum Zusammenführen
Hier ist der Code, der alles zum Laufen bringt:
// Loop through every ball and draw it and its gradient.
for (var i = 0; i < this.ballCount_; i++) {
var target = this.world_.particles[i];
// Set the size of the ball radial gradients.
this.gradSize_ = target.radius * 4;
this.gctx_.translate(target.pos.x - this.gradSize_,
target.pos.y - this.gradSize_);
var radGrad = this.gctx_.createRadialGradient(this.gradSize_,
this.gradSize_, 0, this.gradSize_, this.gradSize_, this.gradSize_);
radGrad.addColorStop(0, target['color'] + '1)');
radGrad.addColorStop(1, target['color'] + '0)');
this.gctx_.fillStyle = radGrad;
this.gctx_.fillRect(0, 0, this.gradSize_ * 4, this.gradSize_ * 4);
};
Richten Sie dann den Canvas für die Maskierung ein und zeichnen Sie Folgendes:
// Make the ball canvas the source of the mask.
this.pctx_.globalCompositeOperation = 'source-atop';
// Draw the ball canvas onto the gradient canvas to complete the mask.
this.pctx_.drawImage(this.gcanvas_, 0, 0);
this.ctx_.drawImage(this.paperCanvas_, 0, 0);
Fazit
Durch die verschiedenen Techniken, die wir nutzen konnten, und die von uns implementierten Technologien (wie Canvas, SVG, CSS Animation, JS Animation, Web Audio usw.) hat die Entwicklung des Projekts unglaublich viel Spaß gemacht.
Es gibt noch viel mehr zu entdecken als das, was du hier siehst. Wenn Sie weiter auf das I/O-Logo tippen, werden durch die richtigen Abfolgen weitere Mini-Experimente, Spiele, psychedelische Bilder und vielleicht sogar Frühstücksgerichte freigeschaltet. Wir empfehlen, die App auf einem Smartphone oder Tablet zu verwenden.
Hier ist eine Kombination für den Einstieg: O-I-I-I-I-I-I-I. Jetzt ausprobieren: google.com/io
Open Source
Wir haben unseren Code unter der Apache 2.0-Lizenz als Open Source veröffentlicht. Sie finden es auf unserer GitHub-Seite unter http://github.com/Instrument/google-io-2013.
Gutschriften
Entwickler:
- Reynolds
- Hefter
- Stefanie Hatcher
- Paul Farning
Designer:
- Dan Schechter
- Salbeibraun
- Kyle Beck
Erstellung:
- Amie Pascal
- Andrea Nelson