Studi Kasus - Eksperimen Google I/O 2013

Thomas Reynolds
Thomas Reynolds

Pengantar

Untuk membangun minat developer pada situs Google I/O 2013 sebelum pendaftaran konferensi dibuka, kami mengembangkan serangkaian eksperimen dan game berorientasi seluler yang berfokus pada interaksi sentuh, audio generatif, dan kegembiraan dalam penemuan. Terinspirasi oleh potensi kode dan kekuatan permainan, pengalaman interaktif ini dimulai dengan suara sederhana "I" dan "O" saat Anda mengetuk logo I/O baru.

Gerakan Organik

Kami memutuskan untuk menerapkan animasi I dan O dalam efek organik yang goyah dan tidak sering terlihat dalam interaksi HTML5. Menelepon opsi agar terasa menyenangkan dan reaktif memerlukan waktu.

Contoh Kode Fisika Bouncy

Untuk mencapai efek ini, kami menggunakan simulasi fisika sederhana pada serangkaian titik yang mewakili tepi kedua bentuk tersebut. Saat salah satu bentuk diketuk, semua titik dipercepat dari lokasi ketukan. Ia melakukan peregangan sebelum ditarik kembali.

Saat pembuatan instance, setiap titik mendapatkan jumlah akselerasi acak dan "pantul" memantul kembali sehingga tidak bergerak secara seragam, seperti yang dapat Anda lihat dalam kode ini:

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

Kemudian, saat diketuk, gestur dipercepat dari posisi ketukan menggunakan kode di sini:

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

Terakhir, setiap partikel melambat di setiap frame dan perlahan kembali ke keseimbangan dengan pendekatan ini dalam kode:

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 Gerakan Organik

Ini dia mode beranda I/O yang bisa Anda mainkan. Kita juga telah menampilkan banyak opsi tambahan dalam implementasi ini. Jika Anda mengaktifkan "tampilkan titik", Anda akan melihat titik individual tempat simulasi dan gaya fisik bekerja.

Pencerminan Ulang

Setelah puas dengan gerakan mode rumahan, kami ingin menggunakan efek yang sama untuk dua mode retro: Eightbit dan Ascii.

Untuk menyelesaikan pengubahan ukuran ini, kami menggunakan kanvas yang sama dari mode rumahan dan menggunakan data piksel untuk menghasilkan kedua efek tersebut. Pendekatan ini mengingatkan pada shader fragmen OpenGL, dengan setiap piksel scene diperiksa dan dimanipulasi. Mari kita bahas lebih dalam.

Contoh Kode "Shader" Canvas

Piksel di Canvas dapat dibaca menggunakan metode getImageData. Array yang ditampilkan berisi 4 nilai per piksel yang mewakili setiap nilai RGBA piksel. Piksel ini dirangkai dalam struktur seperti array yang besar. Misalnya, kanvas 2x2 akan memiliki 4 {i>pixel<i} dan 16 entri dalam array imageData-nya.

Kanvas kita adalah layar penuh, jadi jika kita berpura-pura layarnya 1024x768 (seperti pada iPad), maka array memiliki 3.145.728 entri. Karena ini adalah animasi, seluruh array ini diupdate 60 kali per detik. Mesin JavaScript modern dapat menangani loop dan tindakan pada data sebanyak ini dengan cukup cepat agar kecepatan frame tetap konsisten. (Tips: jangan mencoba mencatat data tersebut ke konsol developer, karena akan memperlambat browser untuk meng-crawl atau membuat error sepenuhnya.)

Berikut cara mode Eightbit membaca kanvas mode layar utama dan memperbesar piksel agar memiliki efek memblokir:

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

Demo Shader Eightbit

Di bawah ini, kita menghapus overlay Eightbit dan melihat animasi asli di bawahnya. Opsi "matikan layar" akan menunjukkan kepada Anda efek aneh yang kami temukan karena salah mengambil sampel piksel sumber. Kami akhirnya menggunakannya sebagai pesan tersembunyi yang "responsif" saat ukuran mode Eightbit diubah ke rasio aspek yang kecil. Selamat!

Komposisi Kanvas

Sungguh menakjubkan apa yang dapat Anda capai dengan menggabungkan beberapa langkah render dan mask. Kami membuat metaball 2D yang mengharuskan setiap bola memiliki gradien radialnya sendiri dan gradien tersebut dicampur bersama tempat bola-bola tumpang tindih. (Anda dapat melihatnya dalam demo di bawah.)

Untuk melakukannya, kami menggunakan dua kanvas terpisah. Kanvas pertama menghitung dan menggambar bentuk metaball. Kanvas kedua menggambar gradien radial di setiap posisi bola. Kemudian bentuk ini menyamarkan gradien dan kita merender output akhir.

Contoh Kode Komposisi

Berikut adalah kode yang membuat semuanya terjadi:

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

Kemudian, siapkan kanvas untuk penyamaran dan gambar:

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

Kesimpulan

Berbagai teknik yang kami gunakan dan teknologi yang kami terapkan (seperti Canvas, SVG, Animasi CSS, JS Animation, Audio Web, dll.) membuat proyek ini menjadi sangat menyenangkan untuk dikembangkan.

Ada lebih banyak lagi yang dapat dijelajahi daripada yang Anda lihat di sini. Terus ketuk logo I/O dan urutan yang benar akan membuka lebih banyak eksperimen mini, game, visual yang tiga kali, dan bahkan mungkin beberapa makanan untuk sarapan. Sebaiknya coba di smartphone atau tablet untuk mendapatkan pengalaman terbaik.

Berikut ini kombinasi untuk memulai: O-I-I-I-I-I-I. Coba sekarang: google.com/io

Open Source

Kami telah menjadikan kode Apache 2.0 sebagai kode {i>open source<i}. Anda dapat menemukannya di GitHub kami di: http://github.com/Instrument/google-io-2013.

Kredit

Developer:

  • Thomas Reynolds
  • Brian Hefter
  • Hatcher Stefanie
  • Paul Farning

Desainer:

  • Dan Schechter
  • Cokelat Sage
  • Kyle Beck

Produser:

  • Amie Pascal
  • Andrea Budiman