Pengantar
Untuk membangun minat developer di situs Google I/O 2013 sebelum pendaftaran konferensi dibuka, kami mengembangkan serangkaian eksperimen dan game yang mengutamakan perangkat seluler yang berfokus pada interaksi sentuh, audio generatif, dan kesenangan penemuan. Terinspirasi oleh potensi kode dan kekuatan bermain, 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 bergoyang dan tidak sering terlihat dalam interaksi HTML5. Memilih opsi agar terasa menyenangkan dan responsif memerlukan sedikit waktu.
Contoh Kode Fisika Bouncy
Untuk mencapai efek ini, kami menggunakan simulasi fisika sederhana pada serangkaian titik yang mewakili tepi dari dua bentuk tersebut. Saat salah satu bentuk diketuk, semua titik akan dipercepat keluar dari lokasi ketukan. Mereka meregangkan dan menjauh sebelum ditarik kembali.
Saat pembuatan instance, setiap titik mendapatkan jumlah akselerasi acak dan "kekenyalan" rebound sehingga tidak dianimasikan 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, lingkaran akan dipercepat ke luar 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 akan diperlambat pada setiap frame dan perlahan kembali ke kesetimbangan 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
Berikut adalah mode rumah I/O yang dapat Anda gunakan. Kami juga telah mengekspos banyak opsi tambahan dalam penerapan ini. Jika mengaktifkan "tampilkan titik", Anda akan melihat setiap titik yang menjadi sasaran simulasi dan gaya fisika.
Penggantian skin
Setelah puas dengan gerakan mode rumah, kami ingin menggunakan efek yang sama untuk dua mode retro: Eightbit dan Ascii.
Untuk melakukan penggantian tema ini, kita menggunakan kanvas yang sama dari mode layar utama dan menggunakan data piksel untuk menghasilkan kedua efek tersebut. Pendekatan ini mengingatkan pada shader fragmen OpenGL, tempat setiap piksel tampilan diperiksa dan dimanipulasi. Mari kita bahas lebih lanjut.
Contoh Kode "Shader" Kanvas
Piksel pada Kanvas 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 piksel dan 16 entri dalam array imageData-nya.
Kanvas kita adalah layar penuh, jadi jika kita berpura-pura layarnya berukuran 1024x768 (seperti di iPad), array akan memiliki 3.145.728 entri. Karena ini adalah animasi, seluruh array ini diperbarui 60 kali per detik. Mesin JavaScript modern dapat menangani loop dan bertindak pada data sebanyak ini dengan cukup cepat untuk menjaga kecepatan frame tetap konsisten. (Tips: jangan mencoba mencatat data tersebut ke konsol developer, karena akan memperlambat crawling browser atau membuatnya error sepenuhnya.)
Berikut cara mode Eightbit membaca kanvas mode layar utama dan memperbesar piksel agar memiliki efek yang lebih kaku:
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 akan menghapus overlay Eightbit dan melihat animasi aslinya di bawah. Opsi "kill screen" akan menampilkan efek aneh yang kami temukan dengan mengambil sampel piksel sumber yang salah. Kami akhirnya menggunakannya sebagai easter egg "responsif" saat mode Eightbit diubah ukurannya menjadi rasio aspek yang tidak mungkin. Kecelakaan yang menyenangkan!
Komposisi Kanvas
Anda dapat melakukan hal yang luar biasa dengan menggabungkan beberapa langkah render dan mask. Kami membuat metaball 2D yang mengharuskan setiap bola memiliki gradien radialnya sendiri dan gradien tersebut digabungkan saat bola tumpang-tindih. (Anda dapat melihatnya dalam demo di bawah.)
Untuk melakukannya, kita menggunakan dua kanvas terpisah. Kanvas pertama menghitung dan menggambar bentuk metaball. Kanvas kedua menggambar gradien radial di setiap posisi bola. Kemudian, bentuk akan menyamarkan gradien dan kita merender output akhir.
Contoh Kode Komposisi
Berikut 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 masking dan menggambar:
// 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 harus kami gunakan dan teknologi yang kami terapkan (seperti Canvas, SVG, Animasi CSS, Animasi JS, Audio Web, dll.) membuat project ini sangat menyenangkan untuk dikembangkan.
Ada banyak hal lain yang dapat dijelajahi selain yang Anda lihat di sini. Terus ketuk logo I/O dan urutan yang benar akan membuka lebih banyak eksperimen mini, game, visual trippy, dan mungkin bahkan beberapa makanan sarapan. Sebaiknya coba di smartphone atau tablet untuk mendapatkan pengalaman terbaik.
Berikut adalah kombinasi untuk memulai: O-I-I-I-I-I-I-I. Coba sekarang: google.com/io
Open Source
Kami telah menjadikan lisensi Apache 2.0 kode kami sebagai open source. Anda dapat menemukannya di GitHub kami di: http://github.com/Instrument/google-io-2013.
Kredit
Pengembang:
- Thomas Reynolds
- Brian Hefter
- Stefanie Hatcher
- Paul Farning
Desainer:
- Dan Schechter
- Sage Brown
- Kyle Beck
Produsen:
- Amie Pascal
- Andrea Nelson