Pengantar
Untuk membangun minat developer di situs Google I/O 2013 sebelum pendaftaran konferensi dibuka, kami mengembangkan serangkaian eksperimen dan game yang berfokus pada interaksi sentuh, audio generatif, dan kesenangan penemuan. Pengalaman interaktif yang terinspirasi oleh potensi kode dan kekuatan permainan ini dimulai dengan suara "I" dan "O" yang sederhana 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 kedua bentuk. Saat salah satu bentuk diketuk, semua titik akan dipercepat keluar dari lokasi ketukan. Mereka meregangkan tubuh 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 diperlambat pada setiap frame dan perlahan-lahan 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
Berikut adalah mode beranda I/O yang dapat Anda mainkan. 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 tampilan
Setelah puas dengan gerakan mode rumah, kami ingin menggunakan efek yang sama untuk dua mode retro: Eightbit dan Ascii.
Untuk melakukan penggantian tema ini, kami 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 jauh.
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 rumah 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, kami menghapus overlay Eightbit dan melihat animasi asli di bawahnya. Opsi "kill screen" akan menampilkan efek aneh yang kami temukan dengan mengambil sampel piksel sumber yang salah. Kami akhirnya menggunakannya sebagai fitur tersembunyi yang "responsif" saat mode Eightbit diubah ukurannya menjadi rasio aspek yang tidak mungkin. Selamat kecelakaan!
Komposisi Kanvas
Sungguh menakjubkan apa yang bisa 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 ketika bola-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 Pengomposisian Kode
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 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 harus kami gunakan dan teknologi yang kami terapkan (seperti Canvas, SVG, Animasi CSS, Animasi JS, Web Audio, 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 ini kombinasi untuk memulai: O-I-I-I-I-I-I. Coba sekarang: google.com/io
Open Source
Kami telah menjadikan kode kami sebagai open source dengan lisensi Apache 2.0. Anda dapat menemukannya di GitHub kami di: http://github.com/Instrument/google-io-2013.
Kredit
Pengembang:
- Thomas Reynolds
- Brian Hefter
- Topi Stefanie
- Paul Farning
Desainer:
- Dan Schechter
- Cokelat Sage
- Kyle Beck
Produsen:
- Amie Pascal
- Andrea Nelson