Einführung
Um das Interesse der Entwickler an der Website der Google I/O 2013 zu wecken, bevor die Registrierung für die Konferenz begann, haben wir eine Reihe von mobilen Experimenten und Spielen entwickelt, die sich auf Touch-Interaktionen, generative Audioinhalte und die Freude am Entdecken 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 eine zufällige Sprungkraft, 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 jedes Partikel in jedem Frame verlangsamt und kehrt mit diesem Ansatz im Code langsam zum Gleichgewicht zurück:
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. Außerdem haben wir bei dieser Implementierung eine Reihe zusätzlicher Optionen hinzugefügt. 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 ist im Vollbildmodus. Wenn wir annehmen, dass das Display 1.024 × 768 Pixel hat (wie bei einem iPad), hat 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 erfassen, da Ihr Browser dadurch extrem verlangsamt oder sogar ganz zum Absturz gebracht wird.
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. Mit der Option „Kill Screen“ wird ein merkwürdiger Effekt angezeigt, der durch eine falsche Stichprobenerhebung der Quellpixel zustande kam. Wir haben es schließlich als „responsives“ Osterei verwendet, wenn der Eightbit-Modus auf unwahrscheinliche Seitenverhältnisse skaliert wird. Ein glücklicher Zufall!
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 zweiter Canvas zeichnet an jeder Ballposition radiale Farbverläufe. Anschließend wird die Form verwendet, um die Farbverläufe zu maskieren, 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
Die Vielfalt der Techniken, die wir verwenden und die Technologien, die wir implementiert haben (z. B. Canvas, SVG, CSS-Animation, JS-Animation, Web Audio), haben die Entwicklung des Projekts unglaublich spannend gemacht.
Es gibt noch viel mehr zu entdecken, als hier zu sehen ist. 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:
- Thomas Reynolds
- Brian Hefter
- Stefanie Hatcher
- Paul Farning
Designer:
- Dan Schechter
- Salbeibraun
- Kyle Beck
Erstellung:
- Amie Pascal
- Andrea Nelson