Orta Dünya'nın Önyüzü

Çoklu cihaz geliştirmeyle ilgili bir adım adım açıklamalı kılavuz

Daniel Isaksson
Daniel Isaksson
Einar Öberg
Einar Öberg

Orta Dünya'da Bir Yolculuk adlı Chrome deneysel çalışmasının geliştirilmesiyle ilgili ilk makalemizde mobil cihazlar için WebGL geliştirmeye odaklandık. Bu makalede, HTML5 ön uçunun geri kalanını oluştururken karşılaştığımız zorluklar, sorunlar ve çözümler ele alınmaktadır.

Aynı sitenin üç sürümü

Bu denemeyi ekran boyutu ve cihaz özellikleri açısından hem masaüstü bilgisayarlarda hem de mobil cihazlarda çalışacak şekilde uyarlama hakkında biraz konuşarak başlayalım.

Projenin tamamı, filmin büyüsünü korumak için tasarım açısından deneyimi yatay sabit bir çerçevede tutmak istediğimiz çok "sinematik" bir stile dayanıyor. Projenin büyük bir kısmı etkileşimli mini "oyunlardan" oluştuğu için bunların çerçeveyi aşmasına izin vermek de mantıklı olmaz.

Tasarımımızı farklı boyutlara nasıl uyarladığımıza örnek olarak açılış sayfasını alabiliriz.

Kartal bizi açılış sayfasına bıraktı.
Kartal bizi açılış sayfasına bıraktı.

Sitenin üç farklı modu vardır: masaüstü, tablet ve mobil. Yalnızca düzeni işlemek için değil, çalışma zamanında yüklenen öğeleri işlemek ve çeşitli performans optimizasyonları eklemek için de bunu yapıyoruz. Masaüstü bilgisayarlardan ve dizüstü bilgisayarlardan daha yüksek çözünürlüğe sahip ancak telefonlardan daha kötü performans gösteren cihazlar söz konusu olduğunda, nihai kurallar dizisini tanımlamak kolay bir iş değildir.

Mobil cihazları algılamak için kullanıcı aracısı verilerini ve bu cihazlar arasından tabletleri (645 piksel ve üzeri) hedeflemek için bir görüntü alanı boyutu testi kullanıyoruz. Düzenleme, medya sorgularına veya JavaScript ile göreceli/yüzdelik konumlamaya dayalı olduğundan her farklı mod aslında tüm çözünürlükleri oluşturabilir.

Bu durumdaki tasarımlar ızgaralara veya kurallara dayalı olmadığı ve farklı bölümler arasında oldukça benzersiz olduğu için hangi kesme noktalarının veya stillerin kullanılacağı, söz konusu öğeye ve senaryoya bağlıdır. Bir kez değil, birkaç kez mükemmel bir düzeni güzel sass-mixin'ler ve medya sorgularıyla oluşturduk, ardından fare konumuna veya dinamik nesnelere dayalı bir efekt eklememiz gerekti ve sonunda her şeyi JavaScript'te yeniden yazmak zorunda kaldık.

Ayrıca, bu bilgileri stillerimizde kullanabilmek için head etiketine mevcut modu içeren bir sınıf da ekleriz. Bu örnekte (SCSS'de) gösterildiği gibi:

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

Yaklaşık 360x320'e kadar tüm boyutları destekliyoruz. Bu, etkileyici bir web deneyimi oluştururken oldukça zorlayıcı bir durumdur. Mümkünse siteyi daha büyük bir görüntü alanında deneyimlemenizi istediğimiz için masaüstünde kaydırma çubuklarını göstermeden önce minimum bir boyut belirleriz. Mobil cihazlarda, cihazı yatay moduna getirmenizi istediğimiz etkileşimli deneyimlere kadar hem yatay hem de dikey moda izin vermeye karar verdik. Buna karşı çıkanlar, dikey modun yatay mod kadar etkileyici olmadığını savunuyordu. Ancak site oldukça iyi ölçeklendiği için bu modu kullanmaya devam ettik.

Düzenin giriş türü, cihaz yönü, sensörler gibi özellik algılama ile karıştırılmaması gerektiğini unutmayın. Bu özellikler bu modların hepsinde bulunabilir ve tümünü kapsamalıdır. Fare ve dokunma işlemlerinin aynı anda desteklenmesi buna örnek gösterilebilir. Kalite için retina telafisi, ancak en önemlisi performans farklıdır. Bazen daha düşük kalite daha iyidir. Örneğin, retina ekranlardaki WebGL deneyimlerinde tuval, çözünürlüğün yarısı kadardır. Aksi takdirde dört kat daha fazla piksel oluşturması gerekir.

Geliştirme sırasında, özellikle yeni ve iyileştirilmiş özelliklere ve birçok hazır ayarlara sahip Chrome Canary'da DevTools'daki emülatör aracını sık sık kullandık. Bu, tasarımı hızlı bir şekilde doğrulamanın iyi bir yoludur. Yine de gerçek cihazlarda düzenli olarak test yapmamız gerekiyordu. Bunun nedenlerinden biri, sitenin tam ekrana uyum sağlamasıdır. Dikey kaydırma içeren sayfalar, çoğu durumda kaydırırken tarayıcı kullanıcı arayüzünü gizler (iOS7'deki Safari'de şu anda bu konuda sorun var). Ancak her şeyi bundan bağımsız olarak yerleştirmemiz gerekiyordu. Ayrıca, emülatörde bir hazır ayar kullandık ve ekran boyutu ayarını, kullanılabilir alan kaybını simüle edecek şekilde değiştirdik. Gerçek cihazlarda test yapmak, bellek tüketimini ve performansı izlemek için de önemlidir.

Durumu işleme

Açılış sayfasından sonra Orta Dünya haritasına yönlendiriliriz. URL'nin değiştiğini fark ettiniz mi? Site, yönlendirmeyi işlemek için History API'yi kullanan tek sayfalık bir uygulamadır.

Sitenin her bölümü, DOM öğeleri, geçişler, öğelerin yüklenmesi, öğelerin kaldırılması gibi işlevlerin şablonunu devralan kendi nesnesi olur. Sitenin farklı bölümlerini keşfederken bölümler başlatılır, öğeler DOM'a eklenir ve DOM'dan kaldırılır ve geçerli bölümün öğeleri yüklenir.

Kullanıcı, tarayıcının geri düğmesine basabilir veya menüden istediği zaman gezinebilir. Bu nedenle, oluşturulan her şeyin bir noktada kaldırılması gerekir. Zaman aşımlarının ve animasyonların durdurulması ve kaldırılması gerekir. Aksi takdirde istenmeyen davranışlara, hatalara ve bellek sızıntılarına neden olurlar. Bu, özellikle son tarihler yaklaştığında ve her şeyi mümkün olduğunca hızlı bir şekilde eklemeniz gerektiğinde her zaman kolay bir iş değildir.

Konumları gösterme

Orta Dünya'nın güzel ortamlarını ve karakterlerini göstermek için yatay olarak sürükleyebileceğiniz veya kaydırayabileceğiniz resim ve metin bileşenlerinden oluşan modüler bir sistem oluşturduk. Klibin oynatılması tamamlanana kadar hareketi yana doğru durdurduğunuz resim serilerinde olduğu gibi, farklı aralıklarda farklı hızlara sahip olmak istediğimiz için burada kaydırma çubuğunu etkinleştirmedik.

Thranduil'un Salonu
Thranduil'in Salonu zaman çizelgesi

Zaman çizelgesi

Geliştirme başladığında her konuma ait modüllerin içeriğini bilmiyorduk. Farklı medya ve bilgi türlerini yatay bir zaman çizelgesinde göstermenin şablonlu bir yolunu istediğimizi biliyorduk. Bu sayede her şeyi altı kez yeniden oluşturmak zorunda kalmadan altı farklı konum sunumu yapma özgürlüğüne sahip olacaktık. Bunu yönetmek için, modüllerin kaydırılmasını ayarlara ve modüllerin davranışlarına göre yöneten bir zaman çizelgesi denetleyicisi oluşturduk.

Modüller ve davranış bileşenleri

Destek eklediğimiz farklı modüller resim dizisi, hareketsiz resim, paralaks sahnesi, odak kaydırma sahnesi ve metindir.

Paralaks sahne modülü, tam konumlar için görüntü alanı ilerlemesini dinleyen özel sayıda katmana sahip opak bir arka plana sahiptir.

Odak kaydırma sahnesi, paralaks grubuna ait bir varyanttır. Bununla birlikte, her katman için odak değişikliğini simüle etmek üzere yavaşça görünen ve kaybolan iki resim kullanırız. Bulanıklaştırma filtresini kullanmaya çalıştık ancak hâlâ çok pahalı olduğu için bu konuda CSS gölgelendiricileri bekleyeceğiz.

Metin modülündeki içerik, TweenMax eklentisi Draggable ile sürüklenmeye uygundur. Dikey kaydırma için kaydırma tekerleğini veya iki parmağınızla kaydırma özelliğini de kullanabilirsiniz. Kaydırıp bıraktığınızda fırlatma tarzı fizik ekleyen throw-props-plugin eklentisini not edin.

Modüller, bileşen grubu olarak eklenen farklı davranışlara da sahip olabilir. Bunların hepsinin kendi hedef seçicileri ve ayarları vardır. Bir öğeyi taşımak için çevirme, yakınlaştırmak için ölçeklendirme, bilgi yer paylaşımı için sıcak noktalar, görsel olarak test etmek için hata ayıklama metrikleri, başlangıç başlığı yer paylaşımı, parlama katmanı ve daha fazlası. Bunlar DOM'a eklenir veya modül içindeki hedef öğelerini kontrol eder.

Bu şekilde, hangi öğelerin yükleneceğini ve farklı türde modüllerin ve bileşenlerin nasıl ayarlanacağını tanımlayan tek bir yapılandırma dosyasıyla farklı konumlar oluşturabiliriz.

Resim dizileri

Performans ve indirme boyutu açısından en zorlu modül resim dizisidir. Bu konu hakkında çok sayıda makale var. Mobil cihazlarda ve tabletlerde bu resim hareketsiz bir resimle değiştirilir. Mobil cihazlarda iyi bir kalite elde etmek için kodunun çözülmesi ve bellekte depolanması gereken çok fazla veri var. Öncelikle bir arka plan resmi ve spritesheet kullanarak birden fazla alternatif çözüm denedik ancak bu, GPU'nun spritesheet'ler arasında geçiş yapması gerektiğinde bellek sorunlarına ve gecikmeye neden oldu. Ardından img öğelerini değiştirmeyi denedik ancak bu da çok yavaştı. Sprite e-tablosundan tuvale kare çizme işlemi en yüksek performansı sağladığı için bu işlemi optimize etmeye başladık. Her kare için hesaplama süresinden tasarruf etmek amacıyla, tuvale yazılacak resim verileri geçici bir tuval aracılığıyla önceden işlenir ve putImageData() işleviyle bir diziye kaydedilir, kodu çözülür ve kullanıma hazır hale getirilir. Orijinal sprite e-tablosu daha sonra çöp toplanabilir ve bellekte yalnızca gereken minimum veri miktarı depolanır. Kodlanmamış resimleri depolamak aslında daha az yer kaplar ancak sekansı bu şekilde kaydırırken daha iyi performans elde ederiz. Çerçeveler oldukça küçüktür (yalnızca 640x400), ancak bunlar yalnızca kaydırma sırasında görünür. Durduğunuzda yüksek çözünürlüklü bir resim yüklenir ve hızla gösterilir.

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

Model sayfaları Imagemagick ile oluşturulur. Bir klasördeki tüm resimlerin sprite e-tablosunun nasıl oluşturulacağını gösteren basit bir GitHub örneği aşağıda verilmiştir.

Modüllere animasyon ekleme

Modülleri zaman çizelgesine yerleştirmek için zaman çizelgesinin ekran dışında gösterilen gizli bir temsili, "oynatma başlığını" ve zaman çizelgesinin genişliğini izler. Bu işlem yalnızca kodla yapılabilir ancak geliştirme ve hata ayıklama sırasında görsel bir temsil kullanmak iyi bir fikirdir. Gerçekte çalıştırıldığında, boyutlar ayarlanacak şekilde yeniden boyutlandırıldığında güncellenir. Bazı modüller görüntü alanını doldurur, bazılarının ise kendi oranları vardır. Bu nedenle, her şeyin görünür olması ve çok fazla kırpılmaması için tüm çözünürlüklerde her şeyi ölçeklendirmek ve konumlandırmak biraz zordu. Her modülün iki ilerleme göstergesi vardır: biri ekrandaki görünür konum için, diğeri ise modülün süresi içindir. Paralaks hareketi yaparken, nesnelerin başlangıç ve bitiş konumlarını, görüntüdeyken beklenen konumla senkronize edecek şekilde hesaplamak genellikle zordur. Bir modülün tam olarak ne zaman görüntüye girdiğini, dahili zaman çizelgesini ne zaman oynattığını ve ne zaman animasyonla tekrar görüntüden çıktığını bilmek iyidir.

Her modülün üst kısmında, şeffaflığını ayarlayarak orta konumdayken tamamen şeffaf olmasını sağlayan ince bir siyah katman bulunur. Bu sayede, aynı anda tek bir modüle odaklanabilirsiniz. Bu da deneyimi iyileştirir.

Sayfa performansı

Çalışan bir prototipten takılmayan bir sürüm sürümüne geçmek, tarayıcıda neler olduğunu tahmin etmekten bilmeye geçmek anlamına gelir. Bu noktada Chrome Geliştirici Araçları en iyi yardımcınızdır.

Siteyi optimize etmek için oldukça fazla zaman harcadık. Donanım hızlandırmayı zorlamak, elbette sorunsuz animasyonlar elde etmenin en önemli araçlarından biridir. Chrome Geliştirici Araçları'nda renkli sütunlar ve kırmızı dikdörtgenler de arayın. Bu konularla ilgili birçok iyi makale var ve bunların hepsini okumanız gerekiyor. Atlanan kareleri kaldırmanın ödülü anında gelir ancak tekrar ortaya çıktıklarında yaşanan hayal kırıklığı da anında olur. Ve bunu yapacaklar. Bu, iterasyon gerektiren devam eden bir süreçtir.

Özellikleri, dönüşümleri ve CSS'yi ara geçişlerle animasyonlu hale getirmek için Greensock'un TweenMax aracını kullanmayı tercih ediyorum. Kapsayıcılarla ilgili düşünün, yeni katmanlar eklerken yapınızı görselleştirin. Mevcut dönüştürme işlemlerinin yeni dönüştürme işlemleriyle üzerine yazılabileceğini unutmayın. Yalnızca 2D değerlerle tween yaparsanız CSS sınıfınızda donanım hızlandırmasını zorlayan translateZ(0) işlevi 2D matrisle değiştirilir. Bu durumlarda katmanı hızlandırma modunda tutmak için tween'de "force3D:true" mülkünü kullanarak 2D matris yerine 3D matris oluşturun. Stil belirlemek için CSS ve JavaScript tween'lerini birleştirdiğinizde bunları unutmanız kolaydır.

Gerekmediği durumlarda donanım hızlandırmayı zorlamayın. GPU belleği, özellikle belleğin daha fazla kısıtlamaya sahip olduğu iOS'te, birçok kapsayıcıyı donanım hızlandırmak istediğinizde hızlıca dolabilir ve istenmeyen sonuçlara neden olabilir. Daha küçük öğeler yüklemek, bunları CSS ile ölçeklendirmek ve mobil modda bazı efektleri devre dışı bırakmak büyük iyileştirmeler sağladı.

Bellek sızıntıları, becerilerimizi geliştirmemiz gereken bir diğer alandı. Farklı WebGL deneyimleri arasında gezinirken çok sayıda nesne, malzeme, doku ve geometri oluşturulur. Bu öğeler, siz başka bir bölüme gittiğinizde ve bölümü kaldırdığınızda çöp toplama işlemine hazır değilse bellek dolduktan bir süre sonra cihazın kilitlenmesine neden olabilir.

Başarısız bir dispose işlevi içeren bir bölümden çıkma.
Dispose işlevi başarısız olan bir bölümden çıkma.
Çok daha iyi.
Çok daha iyi.

Sızıntıyı bulmak için DevTools'ta zaman çizelgesini kaydetme ve yığın anlık görüntüleri yakalama gibi oldukça basit bir iş akışı uyguladık. Filtreleyebileceğiniz belirli nesneler (ör. 3D geometri veya belirli bir kitaplık) varsa işlem daha kolaydır. Yukarıdaki örnekte, 3D sahnenin hâlâ mevcut olduğu ve geometriyi depolayan bir dizinin de temizlenmediği ortaya çıktı. Nesnenin bulunduğu yeri bulmakta zorlanıyorsanız bunu görüntülemenizi sağlayan saklama yolları adlı kullanışlı bir özellik vardır. Yığın anlık görüntüsünde incelemek istediğiniz nesneyi tıklamanız yeterlidir. Bilgiler, aşağıdaki panelde gösterilir. Daha küçük nesneler içeren iyi bir yapı kullanmak, referanslarınızı bulmanıza yardımcı olur.

Sahne, EffectComposer&#39;da referans olarak kullanıldı.
Sahneye EffectComposer'da referans verildi.

Genel olarak, DOM'u değiştirmeden önce iki kez düşünmek iyi bir fikirdir. Bu durumda verimliliği göz önünde bulundurun. Mümkünse oyun döngüsü içinde DOM'u değiştirmeyin. Referansları yeniden kullanmak için değişkenlerde saklayın. Bir öğeyi aramanız gerekiyorsa stratejik kapsayıcılara referanslar depolayarak ve en yakın ata öğenin içinde arama yaparak en kısa rotayı kullanın.

Yeni eklenen öğelerin boyutlarını okumayı erteleyebilir veya sayfa düzeni hatalarıyla karşılaşırsanız sınıfları kaldırırken/eklerken bu ayarı kullanabilirsiniz. Alternatif olarak Düzenin tetiklendiğinden emin olun. Bazen tarayıcı, stillerde toplu değişiklik yapar ve bir sonraki düzen tetikleyicisinden sonra güncellenmez. Bu durum bazen gerçekten büyük bir sorun olabilir ancak bir nedeni vardır. Bu nedenle, perde arkasında nasıl çalıştığını öğrenmeye çalışın. Böylece çok şey kazanabilirsiniz.

Tam ekran

Mümkün olduğunda, Tam Ekran API'si aracılığıyla menüden siteyi tam ekran moduna alma seçeneğiniz vardır. Ancak cihazlarda, tarayıcı da videoyu tam ekrana koyma kararı verebilir. iOS'teki Safari'de daha önce bunu kontrol etmenizi sağlayan bir hile vardı ancak bu artık kullanılamıyor. Bu nedenle, kaydırma içermeyen bir sayfa oluştururken tasarımınızı bu hileye gerek kalmadan çalışacak şekilde hazırlamanız gerekir. Bu durum birçok web uygulamasının çalışmamasına neden olduğundan, gelecekteki güncellemelerde bu konuyla ilgili gelişmeleri görebiliriz.

Öğeler

Denemelerle ilgili animasyonlu talimatlar.
Denemeler için animasyonlu talimatlar.

Sitede çok çeşitli öğe türleri kullanıyoruz. Resimler (PNG ve JPEG), SVG (satır içi ve arka plan), sprite sayfaları (PNG), özel simge yazı tipleri ve Adobe Edge animasyonları bunlardan bazıları. Öğenin vektör tabanlı olamayacağı varlıklar ve animasyonlar (sprite sayfaları) için PNG'leri kullanırız. Aksi takdirde mümkün olduğunca SVG'leri kullanmaya çalışırız.

Vektör biçimi, ölçeklendirsek bile kalite kaybı yaşanmayacağı anlamına gelir. Tüm cihazlar için 1 dosya.

  • Küçük dosya boyutu.
  • Her bir bölümü ayrı ayrı canlandırabiliriz (ileri seviye animasyonlar için mükemmeldir). Örneğin, Hobbit logosunun "altyazısını" (Smaug'un yıkımı) ölçek küçültüldüğünde gizliyoruz.
  • SVG HTML etiketi olarak yerleştirilebilir veya ek yükleme olmadan arka plan resmi olarak kullanılabilir (html sayfasıyla aynı anda yüklenir).

Simge yazı tipleri, ölçeklenebilirlik açısından SVG ile aynı avantajlara sahiptir ve yalnızca rengini (fareyle üzerine gelme, etkin vb.) değiştirmemiz gereken simgeler gibi küçük öğeler için SVG yerine kullanılır. Simgelerin yeniden kullanımı da çok kolaydır. Bunun için bir öğenin CSS "content" özelliğini ayarlamanız yeterlidir.

Animasyonlar

SVG öğelerini kodla animasyon haline getirmek, özellikle de animasyon tasarım sürecinde çok fazla değiştirilmesi gerektiğinde çok zaman alıcı olabilir. Tasarımcılar ile geliştiriciler arasındaki iş akışını iyileştirmek için bazı animasyonlarda (oyunlardan önceki talimatlar) Adobe Edge'i kullanırız. Animasyon iş akışı Flash'a çok yakın olduğu için ekibe yardımcı oldu ancak özellikle Edge animasyonları kendi yükleyicileri ve uygulama mantığıyla birlikte geldiği için öğe yükleme sürecimize entegre etme konusunda birkaç dezavantaj var.

Web'de öğeleri ve el yapımı animasyonları işlemek için mükemmel bir iş akışına sahip olmamız için daha çok yol kat etmemiz gerektiğini düşünüyorum. Edge gibi araçların nasıl gelişeceğini görmek için sabırsızlanıyoruz. Diğer animasyon araçları ve iş akışlarıyla ilgili önerilerinizi yorumlara ekleyebilirsiniz.

Sonuç

Projenin tüm parçaları kullanıma sunulduğunda ve nihai sonuca baktığımızda, modern mobil tarayıcıların durumundan oldukça etkilendiğimizi söylemeliyim. Bu projeye başladığımızda, ne kadar sorunsuz, entegre ve performanslı bir deneyim sunabileceğimiz konusunda çok daha düşük beklentilerimiz vardı. Bu, bizim için harika bir öğrenme deneyimi oldu. Ayrıca, iterasyon ve test için harcadığımız çok fazla zaman, modern tarayıcıların işleyiş şekli hakkındaki bilgimizi artırdı. Bu tür projelerin üretim süresini kısaltmak ve tahminden bilgiye geçmek için gereken budur.