Front-end Middle-earth

Panduan pengembangan multiperangkat

Dalam artikel pertama kami tentang pengembangan Eksperimen Chrome A Journey Through Middle-earth, kami berfokus pada pengembangan WebGL untuk perangkat seluler. Dalam artikel ini kami membahas tantangan, masalah, dan solusi yang kami temui saat membuat bagian depan HTML5 lainnya.

Tiga versi dari situs yang sama

Mari kita mulai dengan membahas sedikit tentang mengadaptasikan eksperimen ini agar berfungsi di komputer desktop dan perangkat seluler dari perspektif ukuran layar dan kemampuan perangkat.

Seluruh project didasarkan pada gaya yang sangat "sinematik", di mana kami secara desain ingin mempertahankan pengalaman dalam frame tetap berorientasi lanskap untuk mempertahankan keajaiban film. Karena sebagian besar proyek ini terdiri dari "game" mini interaktif, tidak masuk akal untuk membiarkannya meluap dari {i>frame<i} juga.

Kita dapat mengambil halaman landing sebagai contoh bagaimana kita mengadaptasi desain untuk ukuran yang berbeda.

Elang baru saja menjatuhkan kami di halaman landing.
Elang baru saja menjatuhkan kita di halaman landing.

Situs ini memiliki tiga mode yang berbeda: desktop, tablet, dan seluler. Tidak hanya untuk menangani tata letak, tetapi karena kita perlu menangani aset yang dimuat runtime dan menambahkan berbagai pengoptimalan performa. Dengan perangkat yang memiliki resolusi lebih tinggi daripada komputer desktop dan laptop, tetapi memiliki performa lebih buruk daripada ponsel, menetapkan serangkaian aturan terbaik bukanlah tugas yang mudah.

Kami menggunakan data agen pengguna untuk mendeteksi perangkat seluler dan pengujian ukuran area pandang untuk menargetkan tablet di antara perangkat tersebut (645 piksel dan lebih tinggi). Setiap mode yang berbeda sebenarnya dapat merender semua resolusi, karena tata letak didasarkan pada kueri media atau pemosisian relatif/persentase dengan JavaScript.

Karena desain dalam hal ini tidak didasarkan pada {i>grid<i} atau aturan dan cukup unik di antara bagian yang berbeda, desain itu benar-benar bergantung pada elemen dan skenario tertentu mengenai titik henti sementara atau gaya yang akan digunakan. Hal ini terjadi lebih dari sekali ketika kita telah menyiapkan tata letak yang sempurna dengan sass-mixin dan kueri media yang bagus, kemudian kita perlu menambahkan efek berdasarkan posisi mouse atau objek dinamis, dan akhirnya menulis ulang semuanya dalam JavaScript.

Kita juga menambahkan kelas dengan mode saat ini di tag head sehingga kita dapat menggunakan info tersebut dalam gaya kita, seperti dalam contoh ini (di SCSS):

.loc-hobbit-logo {

  // Default values here.

  .desktop & {
     // Applies only in desktop mode.
  }

 .tablet &, .mobile & {
   
   // Different asset for mobile and tablets perhaps.

   @media screen and (max-height: 760px), (max-width: 760px) {
     // Breakpoint-specific styles.
   }

   @media screen and (max-height: 570px), (max-width: 400px) {
     // Breakpoint-specific styles.
   }
 }
}

Kami mendukung semua ukuran hingga sekitar 360x320, yang cukup menantang saat membuat pengalaman web yang imersif. Di desktop, kami memiliki ukuran minimum sebelum menampilkan scrollbar karena kami ingin Anda melihat situs dalam area pandang yang lebih besar jika memungkinkan. Di perangkat seluler, kami memutuskan untuk mengizinkan mode lanskap dan potret hingga ke pengalaman interaktif, tempat kami meminta Anda mengubah perangkat ke lanskap. Argumen terhadap hal ini adalah bahwa situs ini tidak begitu imersif dalam mode potret atau lanskap; tetapi situs ini diskalakan dengan cukup baik sehingga kami mempertahankannya.

Penting untuk diperhatikan bahwa tata letak tidak boleh tercampur dengan deteksi fitur seperti jenis input, orientasi perangkat, sensor, dll. Fitur-fitur itu bisa ada di semua mode ini dan harus mencakup semua mode. Mendukung mouse dan sentuhan pada saat yang sama adalah salah satu contohnya. Kompensasi retina untuk kualitas, tetapi yang terpenting adalah performa yang berbeda, terkadang kualitas yang lebih rendah lebih baik. Sebagai contoh, kanvas adalah setengah dari resolusi dalam pengalaman WebGL pada layar retina, yang seharusnya merender empat kali jumlah piksel

Kami sering menggunakan alat emulator di DevTools selama pengembangan, terutama di Chrome Canary yang memiliki fitur baru yang ditingkatkan dan banyak preset. Ini adalah cara yang baik untuk memvalidasi desain dengan cepat. Kami masih perlu melakukan pengujian di perangkat sungguhan secara rutin. Salah satu alasannya adalah karena situs beradaptasi dengan layar penuh. Halaman dengan scroll vertikal menyembunyikan UI browser saat men-scroll pada sebagian besar kasus (Safari di iOS7 saat ini bermasalah dengan ini), tetapi kami harus menyesuaikan semuanya secara terpisah dari itu. Kami juga menggunakan preset di emulator dan mengubah setelan ukuran layar untuk menyimulasikan hilangnya ruang yang tersedia. Pengujian pada perangkat sungguhan juga penting untuk memantau penggunaan dan performa memori

Menangani status

Setelah halaman landing, kita akan tiba di peta Middle-earth. Apakah Anda melihat URL berubah? Situs ini adalah aplikasi web satu halaman yang menggunakan History API untuk menangani perutean.

Setiap bagian situs adalah objeknya sendiri yang mewarisi boilerplate fungsionalitas seperti elemen DOM, transisi, pemuatan aset, pembuangan, dll. Saat Anda menjelajahi berbagai bagian dalam situs, bagian-bagiannya dimulai, elemen ditambahkan ke dan dihapus dari DOM, dan aset untuk bagian saat ini akan dimuat.

Karena pengguna bisa menekan tombol kembali di browser atau menavigasi melalui menu setiap saat, semua yang dibuat harus dibuang pada suatu saat. Waktu tunggu dan animasi harus dihentikan dan dihapus atau akan menyebabkan perilaku yang tidak diinginkan, error, dan kebocoran memori. Ini tidak selalu menjadi tugas yang mudah, terutama ketika tenggat waktu sudah dekat dan Anda harus menyelesaikan semuanya secepat mungkin.

Menampilkan lokasi

Untuk memamerkan pengaturan yang indah dan karakter Middle-earth, kami membangun sistem modular yang terdiri dari komponen gambar dan teks yang dapat kamu tarik atau geser secara horizontal. Kami belum mengaktifkan scrollbar di sini karena kami ingin memiliki kecepatan yang berbeda pada rentang yang berbeda, seperti dalam urutan gambar dengan menghentikan gerakan secara miring hingga klip selesai diputar.

Balai Thranduil
Linimasa Thranduil's Hall

Garis waktu

Saat pengembangan dimulai, kami tidak mengetahui konten modul untuk setiap lokasi. Yang kami ketahui adalah bahwa kami ingin menggunakan template untuk menampilkan berbagai jenis media dan informasi dalam rentang waktu horizontal, yang akan memberi kami kebebasan untuk melakukan enam presentasi lokasi berbeda tanpa harus membangun ulang semuanya enam kali. Untuk mengelolanya, kami membuat pengontrol linimasa yang menangani penggeseran modulnya berdasarkan setelan dan perilaku modul.

Modul dan komponen perilaku

Berbagai modul yang kami tambahkan dukungannya adalah urutan gambar, gambar diam, adegan paralaks, scene perubahan fokus, dan teks.

Modul scene paralaks memiliki latar belakang buram dengan jumlah lapisan kustom yang memantau progres area pandang untuk posisi yang tepat.

Scene pergeseran fokus adalah varian dari bucket paralaks, dengan tambahan bahwa kita menggunakan dua gambar untuk setiap lapisan yang memperjelas dan memudar untuk menyimulasikan perubahan fokus. Kami telah mencoba menggunakan filter blur ini, tetapi biayanya tetap mahal, jadi kami akan menunggu shader CSS untuk hal ini.

Konten dalam modul teks diaktifkan untuk tarik dengan plugin TweenMax Draggable. Anda juga dapat menggunakan roda gulir atau geser dengan dua jari untuk men-scroll secara vertikal. Perhatikan throw-props-plugin yang menambahkan fisika gaya fling saat Anda menggeser dan melepaskannya.

Modul ini juga dapat memiliki perilaku berbeda yang ditambahkan sebagai satu set komponen. Semuanya memiliki target pemilih dan setelannya sendiri. Terjemahan untuk memindahkan elemen, skala untuk zoom, hotspot untuk overlay info, metrik debug untuk pengujian secara visual, overlay judul awal, lapisan flare, dan banyak lagi. Ini akan ditambahkan ke DOM atau mengontrol elemen targetnya di dalam modul.

Dengan penerapan ini, kita dapat membuat lokasi yang berbeda hanya dengan file konfigurasi yang menentukan aset apa yang akan dimuat dan menyiapkan berbagai jenis modul dan komponen.

Urutan gambar

Hal yang paling menantang dalam modul dilihat dari aspek performa dan ukuran download adalah urutan gambar. Ada banyak hal yang dapat dibaca tentang topik ini. Pada ponsel dan tablet, kita menggantinya dengan gambar diam. Terlalu banyak data yang harus didekode dan disimpan dalam memori jika kita menginginkan kualitas seluler yang bagus. Kami mencoba beberapa solusi alternatif; menggunakan gambar latar dan spritesheet terlebih dahulu, tetapi hal ini menyebabkan masalah memori dan lag saat GPU perlu beralih antar spritesheet. Kemudian kami mencoba menukar elemen img, tetapi proses itu juga terlalu lambat. Menggambar bingkai dari spritesheet ke kanvas adalah cara yang paling efektif, jadi kami mulai mengoptimalkannya. Untuk menghemat waktu komputasi setiap frame, data gambar yang akan ditulis ke kanvas sudah diproses sebelumnya melalui kanvas sementara dan disimpan dengan putImageData() ke array, didekode, dan siap digunakan. Spritesheet asli kemudian dapat dibersihkan sampah memorinya, dan kita hanya menyimpan jumlah minimum data yang diperlukan di dalam memori. Mungkin penyimpanan gambar yang tidak dikodekan lebih sedikit, tetapi kita mendapatkan performa yang lebih baik saat menggosok urutan dengan cara ini. Bingkainya cukup kecil, hanya 640x400, tetapi itu hanya akan terlihat selama penggosok. Saat Anda berhenti, gambar resolusi tinggi akan dimuat dan perlahan memudar.

var canvas = document.createElement('canvas');
canvas.width = imageWidth;
canvas.height = imageHeight;

var ctx = canvas.getContext('2d');
ctx.drawImage(sheet, 0, 0);

var tilesX = imageWidth / tileWidth;
var tilesY = imageHeight / tileHeight;

var canvasPaste = canvas.cloneNode(false);
canvasPaste.width = tileWidth;
canvasPaste.height = tileHeight;

var i, j, canvasPasteTemp, imgData, 
var currentIndex = 0;
var startIndex = index * 16;
for (i = 0; i < tilesY; i++) {
  for (j = 0; j < tilesX; j++) {
    // Store the image data of each tile in the array.
    canvasPasteTemp = canvasPaste.cloneNode(false);
    imgData = ctx.getImageData(j * tileWidth, i * tileHeight, tileWidth, tileHeight);
    canvasPasteTemp.getContext('2d').putImageData(imgData, 0, 0);

    list[ startIndex + currentIndex ] = imgData;

    currentIndex++;
  }
}

Sprite sheet dibuat dengan Imagemagick. Berikut adalah contoh sederhana di GitHub yang menunjukkan cara membuat spritesheet dari semua gambar di dalam folder.

Menganimasikan modul

Untuk menempatkan modul pada garis waktu, representasi tersembunyi dari garis waktu, ditampilkan di luar layar, melacak pada 'playhead' dan lebar garis waktu. Hal ini dapat dilakukan hanya dengan kode, tetapi berfungsi dengan baik dengan representasi visual saat mengembangkan dan melakukan proses debug. Saat dijalankan secara nyata, ini hanya diperbarui tentang ubah ukuran untuk menetapkan dimensi. Beberapa modul mengisi area pandang dan beberapa memiliki rasionya sendiri, jadi sedikit sulit untuk menskalakan dan memosisikan semuanya dalam semua resolusi sehingga semuanya terlihat dan tidak terlalu banyak dipangkas. Setiap modul memiliki dua indikator progres, satu untuk posisi yang terlihat di layar dan satu lagi untuk durasi modul itu sendiri. Saat membuat gerakan paralaks, sering kali sulit untuk menghitung posisi awal dan akhir objek untuk disinkronkan dengan posisi yang diharapkan saat objek terlihat. Ada baiknya untuk mengetahui secara persis kapan modul memasuki tampilan, memutar linimasa internalnya, dan kapan beralih kembali keluar dari tampilan.

Setiap modul memiliki lapisan hitam halus di bagian atasnya yang menyesuaikan opasitasnya sehingga menjadi sepenuhnya transparan saat berada di posisi tengah. Ini membantu Anda untuk fokus pada satu modul pada satu waktu, yang akan meningkatkan pengalaman.

Performa halaman

Berpindah dari prototipe yang berfungsi ke versi rilis bebas jank berarti beralih dari menebak ke mengetahui apa yang terjadi di browser. Di sinilah Chrome DevTools menjadi sahabat Anda.

Kami telah menghabiskan cukup banyak waktu untuk mengoptimalkan situs. Memaksa akselerasi hardware adalah salah satu alat terpenting untuk mendapatkan animasi yang halus. Namun, Anda juga memburu kolom warna-warni dan persegi panjang merah di Chrome DevTools. Ada banyak artikel bagus tentang topik ini, dan Anda harus membaca semuanya. Reward untuk menghapus frame yang dilewati bersifat instan, tetapi begitu juga rasa frustrasi saat mereka kembali lagi. Dan mereka akan melakukannya. Ini adalah proses berkelanjutan yang membutuhkan iterasi.

Saya suka menggunakan TweenMax dari Greensock untuk properti tweening, transformasi, dan CSS. Berpikirlah dalam container, visualisasikan struktur Anda saat menambahkan lapisan baru. Perlu diingat bahwa transformasi yang ada dapat ditimpa oleh transformasi baru. TranslateZ(0) yang memaksa akselerasi perangkat keras di kelas CSS Anda diganti dengan matriks 2D jika Anda hanya mengubah nilai 2D tween. Untuk menjaga lapisan dalam mode akselerasi dalam kasus tersebut, gunakan properti "force3D:true" di tween untuk membuat matriks 3D, bukan matriks 2D. Hal ini sering terlupakan ketika Anda menggabungkan CSS dan JavaScript untuk menetapkan gaya.

Jangan memaksakan akselerasi hardware di tempat yang tidak diperlukan. Memori GPU dapat terisi penuh dengan cepat dan menyebabkan hasil yang tidak diinginkan jika Anda ingin meningkatkan akselerasi hardware pada banyak container, terutama di iOS yang memiliki lebih banyak batasan memori. Untuk memuat aset yang lebih kecil dan menskalakannya dengan CSS serta menonaktifkan beberapa efek dalam mode seluler, kami melakukan peningkatan besar.

Kebocoran memori adalah bidang lain yang kami butuhkan untuk meningkatkan keterampilan kami. Saat bernavigasi di antara berbagai pengalaman WebGL, akan banyak objek, material, tekstur, dan geometri yang dibuat. Jika belum siap untuk pembersihan sampah memori saat Anda keluar dan menghapus bagian, bagian tersebut mungkin akan menyebabkan perangkat error setelah beberapa saat jika memori habis.

Keluar dari bagian dengan fungsi membuang yang gagal.
Keluar dari bagian dengan fungsi membuang yang gagal.
Jauh lebih baik!
Jauh lebih baik!

Untuk menemukan kebocoran, alur kerja ini cukup lurus di DevTools, merekam linimasa dan merekam snapshot heap. Akan lebih mudah jika ada objek tertentu, seperti geometri 3D atau library tertentu, yang dapat Anda filter. Pada contoh di atas, ternyata adegan 3D masih ada dan juga array yang menyimpan geometri tidak dihapus. Jika Anda kesulitan menemukan lokasi keberadaan objek, ada fitur bagus yang memungkinkan Anda melihat hal ini yang disebut jalur penahanan. Cukup klik objek yang ingin Anda periksa di cuplikan heap dan Anda akan mendapatkan informasinya di panel di bawah ini. Menggunakan struktur yang baik dengan objek yang lebih kecil akan membantu saat menemukan referensi.

Adegan ini direferensikan di EffectComposer.
Adegan ini direferensikan di EffectComposer.

Secara umum, ada baiknya berpikir dua kali sebelum Anda memanipulasi DOM. Ketika Anda melakukannya, pikirkan tentang efisiensi. Jangan memanipulasi DOM di dalam game loop jika Anda dapat membantunya. Simpan referensi dalam variabel untuk digunakan kembali. Jika Anda perlu menelusuri elemen, gunakan rute terpendek dengan menyimpan referensi ke container strategis dan menelusuri di dalam elemen ancestor terdekat.

Menunda dimensi pembacaan elemen yang baru ditambahkan atau saat menghapus/menambahkan class jika Anda mengalami bug tata letak. Atau pastikan Tata Letak dipicu. Terkadang batch browser berubah menjadi gaya, dan tidak akan diperbarui setelah pemicu tata letak berikutnya. Hal ini terkadang dapat menjadi masalah besar, tetapi ada alasannya. Jadi, cobalah untuk mempelajari cara kerjanya di balik layar, dan Anda akan mendapatkan banyak manfaat.

Layar penuh

Bila tersedia, Anda memiliki opsi untuk menempatkan situs dalam mode layar penuh di menu melalui API Layar Penuh. Tetapi pada perangkat ada juga keputusan {i>browser<i} untuk memasukkannya ke dalam layar penuh. Safari di iOS sebelumnya memiliki peretasan untuk memungkinkan Anda mengontrolnya, tetapi hal itu tidak tersedia lagi sehingga Anda harus mempersiapkan desain agar berfungsi tanpanya saat membuat halaman yang tidak dapat di-scroll. Kami mungkin bisa mengharapkan pembaruan tentang hal ini dalam pembaruan mendatang, karena ini telah merusak banyak aplikasi web.

Aset

Petunjuk animasi untuk eksperimen.
Petunjuk dengan animasi untuk eksperimen.

Di sepanjang situs, kami memiliki banyak jenis aset yang berbeda, kami menggunakan gambar (PNG dan JPEG), SVG (inline dan latar belakang), spritesheet (PNG), font ikon khusus, dan animasi Adobe Edge. Kita menggunakan PNG untuk aset dan animasi (spritesheet) di mana elemennya tidak dapat berbasis vektor, jika tidak, kita akan mencoba menggunakan SVG sebanyak mungkin.

Format vektor berarti tidak ada kehilangan kualitas, bahkan jika kita menskalakannya. 1 file untuk semua perangkat.

  • Ukuran file yang kecil.
  • Kita bisa menganimasikan setiap bagian secara terpisah (cocok untuk animasi lanjutan). Sebagai contoh, kita menyembunyikan "subtitle" logo Hobbit (penghancuran Smaug) saat diperkecil.
  • Ini dapat disematkan sebagai tag HTML SVG atau digunakan sebagai gambar latar tanpa pemuatan tambahan (dimuat bersamaan dengan halaman html).

Jenis huruf ikon memiliki keunggulan yang sama dengan SVG dalam hal skalabilitas dan digunakan sebagai pengganti SVG untuk elemen-elemen kecil seperti ikon yang hanya perlu kita ubah warnanya (hover, active, dll.). Ikon ini juga sangat mudah digunakan kembali, Anda hanya perlu menyetel properti "content" CSS untuk elemen.

Animasi

Dalam beberapa kasus, menganimasikan elemen SVG dengan kode bisa sangat memakan waktu, terutama bila animasi perlu banyak diubah selama proses desain. Untuk meningkatkan alur kerja antara desainer dan developer, kami menggunakan Adobe Edge untuk beberapa animasi (petunjuk sebelum game). Alur kerja animasi sangat mirip dengan Flash dan membantu tim, tetapi ada beberapa kelemahan, terutama dengan mengintegrasikan animasi Edge dalam proses pemuatan aset kami karena dilengkapi dengan loader dan logika implementasinya sendiri.

Kami masih merasa masih banyak yang harus dilakukan sebelum kami memiliki alur kerja yang sempurna untuk menangani aset dan animasi buatan tangan di web. Kami menantikan perkembangan alat seperti Edge. Jangan ragu untuk menambahkan saran tentang alat animasi dan alur kerja lainnya di komentar.

Kesimpulan

Sekarang ketika semua bagian dari proyek ini dirilis dan kita melihat hasil akhir, saya harus mengatakan bahwa kita cukup terkesan dengan keadaan browser seluler modern. Saat memulai proyek ini, kami memiliki ekspektasi yang jauh lebih rendah tentang bagaimana kami dapat membuatnya tanpa hambatan, terintegrasi, dan berperforma tinggi. Ini adalah pengalaman belajar yang luar biasa bagi kami dan semua waktu yang dihabiskan untuk melakukan iterasi dan pengujian (banyak) telah meningkatkan pemahaman kami tentang cara kerja browser modern. Dan itulah yang diperlukan jika kita ingin mempersingkat waktu produksi untuk jenis proyek ini, dari sekadar menebak sampai mengetahui.