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, kita akan membahas tantangan, masalah, dan solusi yang kita temui saat membuat bagian front-end HTML5 lainnya.

Tiga versi situs yang sama

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

Seluruh project ini didasarkan pada gaya yang sangat “sinematik”, dengan desain yang ingin kami pertahankan pengalamannya dalam bingkai tetap berorientasi lanskap untuk mempertahankan keajaiban dari film. Karena sebagian besar project terdiri dari “game” mini interaktif, sebaiknya jangan biarkan game tersebut melebihi bingkai.

Kita dapat mengambil halaman landing sebagai contoh cara kita menyesuaikan desain untuk berbagai ukuran.

Elang baru saja menurunkan kita di halaman landing.
Elang baru saja menurunkan kita di halaman landing.

Situs ini memiliki tiga mode berbeda: desktop, tablet, dan seluler. Bukan 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 yang lebih buruk daripada ponsel, menentukan kumpulan aturan yang paling tepat bukanlah hal 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 yang lebih tinggi). Setiap mode yang berbeda sebenarnya dapat merender semua resolusi, karena tata letaknya didasarkan pada kueri media atau pemosisian relatif/persentase dengan JavaScript.

Karena desain dalam hal ini tidak didasarkan pada petak atau aturan dan cukup unik di antara berbagai bagian, hal ini benar-benar bergantung pada elemen dan skenario tertentu terkait titik henti sementara atau gaya yang akan digunakan. Sering kali kita telah menyiapkan tata letak yang sempurna dengan sass-mixin dan media-query yang bagus, lalu kita perlu menambahkan efek berdasarkan posisi mouse atau objek dinamis, dan akhirnya menulis ulang semuanya dalam JavaScript.

Kita juga menambahkan class dengan mode saat ini di tag head sehingga kita dapat menggunakan info tersebut dalam gaya, seperti dalam contoh ini (dalam 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 menikmati situs dalam area pandang yang lebih besar jika memungkinkan. Di perangkat seluler, kami memutuskan untuk mengizinkan mode lanskap dan potret hingga pengalaman interaktif, tempat kami meminta Anda untuk mengubah perangkat ke mode lanskap. Argumen yang menentang hal ini adalah bahwa situs tidak begitu imersif dalam mode potret seperti dalam mode lanskap; tetapi situs ini diskalakan dengan cukup baik sehingga kami mempertahankannya.

Perlu diperhatikan bahwa tata letak tidak boleh dicampuradukkan dengan deteksi fitur seperti jenis input, orientasi perangkat, sensor, dll. Fitur tersebut dapat ada di semua mode ini dan harus mencakup semua. Mendukung mouse dan sentuh secara bersamaan adalah salah satu contohnya. Kompensasi retina untuk kualitas, tetapi yang terpenting adalah performa, terkadang kualitas yang lebih rendah lebih baik. Misalnya, kanvas adalah setengah resolusi dalam pengalaman WebGL pada layar retina, yang harus 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 pada perangkat sungguhan secara berkala. Salah satu alasannya adalah karena situs beradaptasi dengan layar penuh. Halaman dengan scroll vertikal menyembunyikan UI browser saat men-scroll dalam sebagian besar kasus (Safari di iOS7 saat ini mengalami masalah dengan hal ini), tetapi kami harus menyesuaikan semuanya terlepas dari hal tersebut. 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 konsumsi memori dan performa

Menangani status

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

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

Karena pengguna dapat menekan tombol kembali browser atau menavigasi melalui menu kapan saja, semua yang dibuat harus dihapus pada suatu waktu. Waktu tunggu dan animasi harus dihentikan dan dihapus, atau akan menyebabkan perilaku, error, dan kebocoran memori yang tidak diinginkan. Hal ini tidak selalu mudah, terutama saat tenggat waktu semakin dekat dan Anda perlu memasukkan semuanya secepat mungkin.

Menampilkan lokasi

Untuk menampilkan latar belakang dan karakter Middle-earth yang indah, kami membuat sistem modular komponen gambar dan teks yang dapat Anda tarik atau geser secara horizontal. Kita belum mengaktifkan scrollbar di sini karena kita ingin memiliki kecepatan yang berbeda pada rentang yang berbeda, seperti dalam urutan gambar saat Anda menghentikan gerakan ke samping hingga klip diputar.

Aula Thranduil
Linimasa Hall of Thranduil

Linimasa

Saat pengembangan dimulai, kami tidak mengetahui konten modul untuk setiap lokasi. Yang kami tahu adalah kami menginginkan cara template untuk menampilkan berbagai jenis media dan informasi dalam linimasa horizontal yang akan memberi kami kebebasan untuk memiliki enam presentasi lokasi yang berbeda tanpa harus membuat ulang semuanya enam kali. Untuk mengelola hal ini, kami membuat pengontrol linimasa yang menangani penggeseran modulnya berdasarkan setelan dan perilaku modul.

Modul dan komponen perilaku

Modul yang berbeda yang kami tambahkan dukungannya adalah urutan gambar, gambar diam, tampilan paralaks, tampilan pergeseran fokus, dan teks.

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

Layar pergeseran fokus adalah varian bucket paralaks, dengan tambahan bahwa kita menggunakan dua gambar untuk setiap lapisan yang memudar dan menghilang untuk menyimulasikan perubahan fokus. Kami mencoba menggunakan filter blur, tetapi masih terlalu mahal, jadi kami akan menunggu shader CSS untuk ini.

Konten dalam modul teks dapat ditarik dengan plugin TweenMax Draggable. Anda juga dapat menggunakan roda scroll atau menggeser dua jari untuk men-scroll secara vertikal. Perhatikan throw-props-plugin yang menambahkan fisika gaya ayun saat Anda menggeser dan melepaskan.

Modul juga dapat memiliki perilaku yang berbeda yang ditambahkan sebagai kumpulan komponen. Semuanya memiliki pemilih dan setelan targetnya sendiri. Terjemahkan untuk memindahkan elemen, skalakan untuk memperbesar, hotspot untuk overlay info, metrik debug untuk pengujian secara visual, overlay judul awal, lapisan flare, dan lainnya. Ini akan ditambahkan ke DOM atau mengontrol elemen targetnya di dalam modul.

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

Urutan gambar

Modul yang paling menantang dari aspek performa dan ukuran download adalah urutan gambar. Ada banyak artikel yang dapat dibaca tentang topik ini. Di perangkat seluler dan tablet, kami menggantinya dengan gambar diam. Ada terlalu banyak data yang harus didekode dan disimpan dalam memori jika kita menginginkan kualitas yang baik di perangkat seluler. Kami mencoba beberapa solusi alternatif; menggunakan gambar latar dan spritesheet terlebih dahulu, tetapi hal ini menyebabkan masalah memori dan jeda saat GPU perlu beralih antar-spritesheet. Kemudian, kami mencoba menukar elemen img, tetapi juga terlalu lambat. Menggambar frame dari spritesheet ke kanvas adalah yang paling berperforma, jadi kami mulai mengoptimalkannya. Untuk menghemat waktu komputasi setiap frame, data gambar yang akan ditulis ke kanvas dipraproses melalui kanvas sementara dan disimpan dengan putImageData() ke array, didekode, dan siap digunakan. Spritesheet asli kemudian dapat dihapus, dan kita hanya menyimpan jumlah minimum data yang diperlukan dalam memori. Mungkin sebenarnya lebih sedikit untuk menyimpan gambar yang tidak didekode, tetapi kita mendapatkan performa yang lebih baik saat menghapus urutan dengan cara ini. Frame-nya cukup kecil, hanya 640x400, tetapi hanya akan terlihat selama proses geser. Saat Anda berhenti, gambar beresolusi tinggi akan dimuat dan dengan cepat 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++;
  }
}

Sheet sprite 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 di linimasa, representasi tersembunyi linimasa, yang ditampilkan di luar layar, akan melacak 'playhead' dan lebar linimasa. Hal ini dapat dilakukan hanya dengan kode, tetapi akan lebih baik dengan representasi visual saat mengembangkan dan men-debug. Saat berjalan secara nyata, dimensi hanya diperbarui saat ukuran diubah untuk menetapkan dimensi. Beberapa modul mengisi area pandang dan beberapa memiliki rasionya sendiri, sehingga agak sulit untuk menskalakan dan memosisikan semuanya dalam semua resolusi sehingga semuanya terlihat dan tidak terlalu dipangkas. Setiap modul memiliki dua indikator progres, satu untuk posisi yang terlihat di layar dan satu untuk durasi modul itu sendiri. Saat membuat gerakan paralaks, sering kali sulit untuk menghitung posisi awal dan akhir objek agar disinkronkan dengan posisi yang diharapkan saat terlihat. Sebaiknya ketahui dengan tepat kapan modul memasuki tampilan, memutar linimasa internalnya, dan kapan animasinya keluar dari tampilan lagi.

Setiap modul memiliki lapisan hitam halus di bagian atas yang menyesuaikan opasitas sehingga sepenuhnya transparan saat berada di posisi tengah. Hal ini membantu Anda berfokus pada satu modul dalam satu waktu, sehingga meningkatkan pengalaman.

Performa halaman

Beralih dari prototipe yang berfungsi ke versi rilis bebas jank berarti beralih dari menebak-nebak 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 tentu saja merupakan salah satu alat yang paling penting untuk mendapatkan animasi yang lancar. Namun, Anda juga harus mencari kolom berwarna-warni dan persegi panjang merah di Chrome DevTools. Ada banyak artikel bagus tentang topik tersebut, dan Anda harus membacanya semuanya. Reward untuk menghapus frame yang dilewati bersifat instan, tetapi begitu juga rasa frustrasi saat frame tersebut kembali. Dan mereka akan melakukannya. Ini adalah proses berkelanjutan yang memerlukan iterasi.

Saya suka menggunakan TweenMax dari Greensock untuk properti tweening, transformasi, dan CSS. Pikirkan dalam penampung, visualisasikan struktur Anda saat menambahkan lapisan baru. Perhatikan bahwa transformasi yang ada dapat ditimpa oleh transformasi baru. translateZ(0) yang memaksa akselerasi hardware di class CSS Anda akan diganti dengan matriks 2D jika Anda hanya melakukan tween nilai 2D. Untuk mempertahankan lapisan dalam mode akselerasi dalam kasus tersebut, gunakan properti “force3D:true” di tween untuk membuat matriks 3D, bukan matriks 2D. Hal ini mudah dilupakan saat Anda menggabungkan tween CSS dan JavaScript untuk menetapkan gaya.

Jangan memaksakan akselerasi hardware jika tidak diperlukan. Memori GPU dapat cepat terisi dan menyebabkan hasil yang tidak diinginkan saat Anda ingin mempercepat banyak penampung dengan hardware, 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 perlu kami tingkatkan keterampilannya. Saat beralih di antara berbagai pengalaman WebGL, banyak objek, materi, tekstur, dan geometri yang dibuat. Jika tidak siap untuk pembersihan sampah memori saat Anda keluar dan menghapus bagian tersebut, bagian tersebut mungkin akan menyebabkan perangkat error setelah beberapa saat saat memori habis.

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

Untuk menemukan kebocoran, alur kerjanya cukup sederhana di DevTools, yaitu merekam linimasa dan mengambil snapshot heap. Akan lebih mudah jika ada objek tertentu, seperti geometri 3D atau library tertentu, yang dapat Anda filter. Dalam contoh di atas, ternyata tampilan 3D masih ada dan juga array yang menyimpan geometri tidak dihapus. Jika Anda kesulitan menemukan lokasi objek, ada fitur bagus yang memungkinkan Anda melihatnya, yang disebut jalur retensi. Cukup klik objek yang ingin Anda periksa di snapshot heap dan Anda akan mendapatkan informasinya di panel di bawah. Menggunakan struktur yang baik dengan objek yang lebih kecil akan membantu saat menemukan referensi Anda.

Layar direferensikan di EffectComposer.
Scene direferensikan di EffectComposer.

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

Menunda pembacaan dimensi 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. Terkadang hal ini bisa menjadi masalah besar, tetapi ada alasannya. Jadi, cobalah untuk mempelajari cara kerjanya di balik layar dan Anda akan mendapatkan banyak manfaat.

Layar penuh

Jika tersedia, Anda memiliki opsi untuk menempatkan situs dalam mode layar penuh di menu melalui Fullscreen API. Namun, di perangkat, ada juga keputusan browser untuk menempatkannya ke layar penuh. Safari di iOS sebelumnya memiliki hack untuk memungkinkan Anda mengontrolnya, tetapi hack tersebut tidak tersedia lagi sehingga Anda harus menyiapkan desain agar berfungsi tanpanya saat membuat halaman yang tidak dapat di-scroll. Kami mungkin akan memberikan update tentang hal ini dalam update mendatang, karena hal ini telah merusak banyak aplikasi web.

Aset

Petunjuk animasi untuk eksperimen.
Petunjuk animasi untuk eksperimen.

Di seluruh situs, kami memiliki banyak jenis aset, kami menggunakan gambar (PNG dan JPEG), SVG (inline dan latar belakang), spritesheet (PNG), font ikon kustom, dan animasi Adobe Edge. Kami menggunakan PNG untuk aset dan animasi (spritesheet) jika elemen tidak dapat berbasis vektor. Jika tidak, kami akan mencoba menggunakan SVG sebanyak mungkin.

Format vektor berarti tidak ada penurunan kualitas, meskipun kita menskalakannya. 1 file untuk semua perangkat.

  • Ukuran file kecil.
  • Kita dapat menganimasikan setiap bagian secara terpisah (cocok untuk animasi lanjutan). Sebagai contoh, kita menyembunyikan "subtitel" logo Hobbit (kehancuran Smaug) saat logo tersebut diskalakan.
  • SVG dapat disematkan sebagai tag HTML SVG atau digunakan sebagai background-image 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 kecil seperti ikon yang hanya perlu kita ubah warnanya (mengarahkan kursor, aktif, dll.). Ikon juga sangat mudah digunakan kembali, Anda hanya perlu menetapkan properti "content" CSS dari elemen.

Animasi

Dalam beberapa kasus, menganimasikan elemen SVG dengan kode dapat sangat memakan waktu, terutama jika 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 hal ini membantu tim, tetapi ada beberapa kekurangan, terutama dalam mengintegrasikan animasi Edge dalam proses pemuatan aset karena dilengkapi dengan loader dan logika penerapannya sendiri.

Saya masih merasa bahwa kita masih harus melakukan banyak hal sebelum memiliki alur kerja yang sempurna untuk menangani aset dan animasi buatan tangan di web. Kami tidak sabar untuk melihat bagaimana alat seperti Edge akan berkembang. Jangan ragu untuk menambahkan saran tentang alat dan alur kerja animasi lainnya di komentar.

Kesimpulan

Sekarang, setelah semua bagian project dirilis dan kita melihat hasil akhirnya, saya harus mengatakan bahwa kita cukup terkesan dengan status browser seluler modern. Saat memulai project ini, kami memiliki ekspektasi yang jauh lebih rendah tentang seberapa lancar, terintegrasi, dan berperforma baik fitur ini. 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 pada jenis project ini, dari menebak menjadi mengetahui.