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

Birden çok cihaz geliştirmenin adım adım açıklamalı kılavuzu

Daniel Isaksson
Daniel Isaksson
Einar Öberg
Einar Öberg

Chrome Denemesinin geliştirilmesiyle ilgili ilk makalemizde Orta Dünya'da Bir Yolculuk mobil cihazlar için WebGL'yi geliştirmeye odaklandık. Bu makalede, HTML5 kullanıcı arabiriminin geri kalanını oluştururken karşılaştığımız zorlukları, sorunları ve çözümleri ele alacağız.

Aynı sitenin üç sürümü

Bu denemeyi, ekran boyutu ve cihaz kapasitesi açısından hem masaüstü bilgisayarlarda hem de mobil cihazlarda çalışacak şekilde uyarlamakla başlayalım.

Tüm proje, oldukça “sinematik” bir tarzda inşa ediliyor. Filmdeki sihri korumak için tasarım açısından deneyimi yatay yönde sabit bir çerçeve içinde tutmak istiyorduk. Projenin büyük bir kısmı etkileşimli mini “oyunlardan” oluştuğu için bunların da karede taşmasına izin vermek anlamlı olmaz.

Açılış sayfasını, tasarımı farklı boyutlara nasıl uyarladığımıza örnek olarak verebiliriz.

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

Sitenin üç farklı modu vardır: masaüstü, tablet ve cep telefonu. Sadece düzeni yönetmekle kalmaz, aynı zamanda çalışma zamanı yüklü öğeleri işlememiz ve çeşitli performans optimizasyonları eklememiz gerekir. Masaüstü ve dizüstü bilgisayarlardan daha yüksek çözünürlüğe ve telefonlardan daha kötü performansa sahip cihazlar göz önünde bulundurulduğunda, nihai kural kümesini tanımlamak kolay bir iş değildir.

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

Bu durumda tasarımlar ızgaralara veya kurallara dayalı olmadığından ve farklı bölümler arasında oldukça benzersiz olduğundan, hangi ayrılma noktalarının veya stillerin kullanılacağı konusunda belirli öğeye ve senaryoya bağlıdır. Bu, birkaç kez oldu. Güzel sass-mixin'ler ve medya sorguları içeren mükemmel düzeni oluşturduktan sonra, fare konumuna veya dinamik nesnelere dayalı bir efekt eklememiz gerekti ve sonuçta her şeyi JavaScript'te yeniden yazmak zorunda kaldık.

Ayrıca, geçerli modun bulunduğu bir sınıfı head etiketine ekleriz. Böylece, bu bilgiyi şu örnekte olduğu gibi stillerimizde kullanabiliriz (SCSS'de):

.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 boyutuna kadar tüm boyutları destekliyoruz. Bu özellik, sürükleyici bir web deneyimi oluştururken oldukça zorlandı. Siteyi mümkünse daha büyük bir görüntü alanında deneyimlemenizi istediğimizden, kaydırma çubuklarını göstermeden önce masaüstünde minimum bir boyut belirleriz. Mobil cihazlarda hem yatay hem de dikey moda izin vermeye karar verdik ve cihazı yatay hale getirmenizi istediğimiz etkileşimli deneyimlere kadar varabileceksiniz. Bununla ilgili düşüncem, sitenin yatay yönde olduğu kadar sürükleyici olmamasıydı ancak site oldukça iyi ölçeklendiğinden biz de bu durumu koruduk.

Düzenin giriş türü, cihaz yönü, sensörler gibi özellik algılamayla karıştırılmaması gerektiğini unutmayın. Bu özellikler tüm bu modlarda mevcut olabilir ve tüm özellikleri kapsamalıdır. Fare ve dokunma işlevinin aynı anda desteklenmesi örnek olarak verilebilir. Kalite için retina telafisi, ancak çoğu performans başka bir durumdur ve bazen daha düşük kalite daha iyidir. Örneğin, tuval, retina ekranlardaki WebGL deneyimlerindeki çözünürlüğün yarısı kadardır, aksi takdirde piksel sayısının dört katı

Geliştirme sırasında Geliştirici Araçları'ndaki emülatör aracını sık kullandık. Özellikle yeni, iyileştirilmiş özelliklere ve birçok hazır ayara sahip Chrome Canary'de bu aracı kullandık. Tasarımı hızlıca doğrulamak için kullanabileceğiniz iyi bir yöntemdir. Hâlâ gerçek cihazlarda düzenli olarak test yapmamız gerekiyordu. Bunun bir nedeni, sitenin tam ekrana uyum sağlamasıydı. Dikey kaydırma içeren sayfalar çoğu durumda kaydırma sırasında tarayıcı kullanıcı arayüzünü gizler (şu anda iOS7'de Safari'de bu konuda sorunlar vardır) ancak bundan bağımsız olarak her şeyi sığdırmak zorunda kaldık. Ayrıca, emülatörde bir hazır ayar kullandık ve kullanılabilir alan kaybını simüle etmek için ekran boyutu ayarını değiştirdik. Gerçek cihazlarda test etmek, bellek tüketimini ve performansı izlemek için de önemlidir

Eyalet yönetimi

Açılış sayfasından sonra Orta Dünya haritasına ulaşırız. URL'nin değiştiğini fark ettiniz mi? Site, yönlendirme işlemini gerçekleştirmek için Geçmiş API'sini kullanan tek sayfalık bir uygulamadır.

Sitenin her bölümü, DOM öğeleri, geçişler, öğelerin yüklenmesi, imha edilmesi gibi işlevlerin bir birleşimini devralan kendi nesnesidir. Sitenin farklı bölümlerini keşfettiğinizde, bölümler başlatılır, DOM'ye öğeler eklenir ve DOM'den kaldırılır ve mevcut bölümün öğeleri yüklenir.

Kullanıcı istediği zaman tarayıcının geri düğmesine basabileceğinden veya menüde menü aracılığıyla gidebileceği için oluşturulan her şeyin bir noktada atılması gerekir. Zaman aşımlarının ve animasyonların durdurulup silinmesi gerekir. Aksi takdirde, istenmeyen davranışlara, hatalara ve bellek sızıntılarına neden olurlar. Bu her zaman kolay bir iş değildir, özellikle de teslim tarihleri yaklaşırken ve işleri olabildiğince hızlı bir şekilde halletmeniz gerekiyorsa.

Konumları gösterme

Orta Dünya'nın güzel ayarlarını ve karakterlerini göstermek için, yatay olarak sürükleyip kaydırabileceğiniz modüler bir görüntü ve metin bileşeni sistemi oluşturduk. Farklı aralıklarda farklı hızlara sahip olmak istediğimizden (örneğin, klip oynatılana kadar hareketi yana doğru durdurduğunuz resim dizilerinde) farklı hızlara sahip olmak istediğimiz için burada bir kaydırma çubuğunu etkinleştirmedik.

Thranduil'in Malikanesi
Thranduil's Hall zaman çizelgesi

Zaman çizelgesi

Geliştirme aşaması başladığında her şube için modüllerin içeriklerini bilmiyorduk. Farklı medya ve bilgi türlerinin yatay bir zaman çizelgesinde gösterilmesi için her şeyi altı kez yeniden inşa etmek zorunda kalmadan altı farklı yerde sunum yapma özgürlüğüne sahip olmamızı sağlayacak bir şablon temelli yöntemi istiyorduk. Bu amaçla, modüllerin kaydırılmasını ayarlara ve modüllerin davranışlarına bağlı olarak yöneten bir zaman çizelgesi denetleyicisi oluşturduk.

Modüller ve davranış bileşenleri

Destek eklediğimiz farklı modüller arasında resim dizisi, sabit resim, paralaks sahne, odak kaydırma sahnesi ve metin bulunuyor.

Paralaks sahne modülünün, tam konumlar için görüntü alanının ilerlemesini izleyen özel sayıda katmana sahip opak bir arka plan vardır.

Odak kaydırma sahnesi paralaks paketinin bir çeşididir. Buna ek olarak, her bir katman için kararma ve sönen iki görüntü kullanırız. Böylece odak değişimi simüle edilir. Bulanıklaştırma filtresini kullanmayı denedik ama hâlâ pahalı olduğu için bunun için CSS gölgelendiricilerini bekleyeceğiz.

Metin modülündeki içerik, TweenMax eklentisi Draggable ile sürükleme ile etkinleştirilir. Dikey olarak kaydırma yapmak için kaydırma tekerleğini de kullanabilir veya iki parmakla hızlıca kaydırabilirsiniz. Ekranı kaydırıp bıraktığınızda hızlı fırlama tarzı fiziği ekleyen throw-props-plugin'e dikkat edin.

Ayrıca modüllerin farklı davranışları da olabilir ve bunlar da bir dizi bileşen olarak eklenir. Hepsinin kendi hedef seçicileri ve ayarları vardır. Bir öğeyi taşımak için çevirin, yakınlaştırmak için ölçekleyin, bilgi yer paylaşımı için hotspot'lar, görsel test için hata ayıklama metrikleri, başlangıç başlığı yer paylaşımı, parlama katmanı ve daha fazlası. Bunlar DOM'ye eklenir veya modülün içindeki hedef öğelerini kontrol eder.

Böylece, hangi öğelerin yükleneceğini ve farklı türlerdeki modül ve bileşenlerin kurulacağını tanımlayan bir yapılandırma dosyasıyla farklı konumlar oluşturabiliriz.

Görüntü dizileri

Performans ve indirme boyutu açısından modüllerin en zorlayıcısı, resim dizisidir. Bu konu hakkında okunacak çok şey var. Mobil cihazlarda ve tabletlerde bunu hareketsiz bir resimle değiştiririz. Mobil cihazlarda yüksek kalitede bir veri olmasını istiyorsak kodu çözülecek ve bellekte depolayamayacağımız çok fazla veri vardır. Çeşitli alternatif çözümler denedik; önce bir arka plan resmi ve bir model sayfası kullandık, ancak bu durum bellek sorunlarına ve GPU'nun model sayfaları arasında geçiş yapması gerektiğinde gecikme yaşanmasına neden oluyordu. Daha sonra img öğelerini değiştirmeyi denedik ama aynı zamanda çok yavaştı. Bir model sayfasından kanvasa bir çerçeve çizmek en iyi performansı sağladı ve biz de bunu optimize etmeye başladık. Her karede hesaplama süresinden tasarruf etmek için tuvale yazılacak görüntü verileri geçici bir tuval aracılığıyla önceden işlenir ve putImageData() ile bir diziye kaydedilir, kodu çözülür ve kullanıma hazır olur. Orijinal model sayfası daha sonra çöp olarak toplanabilir ve yalnızca gereken minimum miktarda veriyi bellekte depolarız. Çözülmemiş resimleri depolamak belki daha az bir şeydir, ancak diziyi bu şekilde ileri geri oynatırken daha iyi performans elde ederiz. Kareler oldukça küçüktür, yalnızca 640x400 boyutundadır, ancak bunlar sadece ileri geri oynatma sırasında görünür. Durduğunuzda, yüksek çözünürlüklü bir resim yüklenir ve çabucak görüntüye karar verir.

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. GitHub'da bir klasördeki tüm resimlerden sprite sayfa oluşturmayı gösteren basit bir örneği burada bulabilirsiniz.

Modüllere animasyon ekleme

Modülleri zaman çizelgesine yerleştirmek için zaman çizelgesinin ekran dışında gösterilen gizli bir gösterimi, "video yer imleci"ni ve zaman çizelgesinin genişliğini takip ediyor. Bunu sadece kodla da yapabilirsiniz ancak geliştirme ve hata ayıklama esnasında görsel temsille de çok iyi sonuçlar elde edebilirsiniz. Gerçek zamanlı olarak çalışırken, yalnızca boyutları ayarlamak için yeniden boyutlandırmada güncellenir. Bazı modüller görüntü alanını doldurur, bazılarının ise kendi oranı vardır. Bu nedenle, her şeyi görünür ve çok fazla kırpılmayacak şekilde tüm çözünürlüklerde ölçeklendirip konumlandırmak biraz zordu. Her modülün iki ilerleme göstergesi vardır. Bunlardan biri ekrandaki konum için, diğeri ise modülün kendisi içindir. Paralaks hareketi yaparken nesnelerin görünümdeki beklenen konumla senkronize edilmesi için başlangıç ve bitiş konumlarını hesaplamak genellikle zordur. Bir modülün tam olarak ne zaman görünüme girdiğini, dahili zaman çizelgesini oynattığını ve ne zaman animasyonun tekrar görüntü dışında olacağını bilmekte fayda vardır.

Her modülün üst kısmında, orta konumdayken tamamen şeffaf olması için opaklığını ayarlayan ince siyah bir katman bulunur. Bu sayede aynı anda tek bir modüle odaklanabilir ve daha iyi bir deneyim sunabilirsiniz.

Sayfa performansı

Çalışan bir prototipten jank içermeyen bir sürüme geçmek, bir tahmin yürütme aşamasından tarayıcıda neler olduğunu bilmek demektir. Chrome Geliştirici Araçları bu noktada en iyi arkadaşınızdır.

Siteyi optimize etmek için epey zaman harcadık. Donanım hızlandırmayı zorlamak, elbette animasyonların akıcı olması için en önemli araçlardan biridir. Diğer yandan, Chrome Geliştirici Araçları'nda renkli sütunları ve kırmızı dikdörtgenleri de avlıyoruz. Konularla ilgili birçok faydalı makale bulunmaktadır. Bu makalelerin tümünü okumalısınız. Atlanan kareleri kaldırmanın ödülü anında verebildiği gibi, tekrar geri döndüklerinde yaşadığı hayal kırıklığını da yaşayabilirsiniz. Ve onlar da. Bu, yineleme gerektiren ve devam eden bir süreçtir.

Mülkler, dönüşümler ve CSS'yi birbirinden ayırmak için Greensock'tan TweenMax'i kullanmak istiyorum. Kapsayıcıların içinde düşünün ve yeni katmanlar ekledikçe yapınızı görselleştirin. Yeni dönüşümlerin mevcut dönüşümlerin üzerine yazılabileceğini unutmayın. Yalnızca 2D değerleri arasında geçiş yaparsanız CSS sınıfınızdaki zorunlu donanım hızlandırmasının yerine 2D matrisi alır. Böyle durumlarda katmanı hızlandırma modunda tutmak için 2D matrisi yerine bir 3D matrisi oluşturmak üzere iki değerde "force3D:true" özelliğini kullanın. Stilleri ayarlamak için CSS ve JavaScript ara değerlerini birleştirdiğinizde kolayca unutulabilirsiniz.

Gerekli olmayan yerlerde donanım hızlandırmaya zorlamayın. GPU belleği, çok sayıda kapsayıcıyı (özellikle de belleğin daha fazla kısıtlamanın olduğu iOS'te) donanım olarak hızlandırmak istediğinizde, hızlı bir şekilde doldurulabilir ve istenmeyen sonuçlara neden olabilir. Daha küçük öğeleri 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 alanlardan biriydi. Farklı WebGL'de gezinirken çok sayıda nesne, materyal, doku ve geometri oluşturulur. Bu cihazlar çöp toplama işlemi için hazır değilse, ayrılıp bölümü kaldırdığınızda muhtemelen bellek tükendiğinde cihazın bir süre sonra kilitlenmesine neden olurlar.

Hatalı temizleme işlevine sahip bir bölümden çıkılıyor.
Silme işlevi başarısız olan bir bölümden çıkma.
Çok daha iyi.
Çok daha iyi bir deneyim!

Sızıntıyı bulmak için DevTools'daki iş akışı oldukça basitti. Zaman çizelgesini kaydedip yığın anlık görüntülerini yakaladık. 3D geometri veya belirli bir kitaplık gibi spesifik nesnelere filtre uygulayabiliyorsanız bunu yapmak daha kolaydır. Yukarıdaki örnekte, 3D sahnenin hâlâ etrafta olduğu ve ayrıca, depolanan geometrinin silinmediği bir dizi olduğu ortaya çıkmıştır. Nesnenin bulunduğu yeri bulmakta zorlanıyorsanız yolları tutma adlı bu özelliği görmenizi sağlayan güzel bir özellik vardır. Yığın anlık görüntüsünde incelemek istediğiniz nesneyi tıklamanız yeterlidir. Bilgi, aşağıdaki panelde gösterilir. Daha küçük nesneler içeren iyi bir yapı kullanmak, referanslarınızı bulmaya yardımcı olur.

Sahne EffectComposer&#39;da referans gösteriliyordu.
Sahneye EffectComposer'da referans verildi.

Genel olarak, DOM'yi manipüle etmeden önce iki kez düşünmek yararlıdır. Bunu yaparken verimlilik üzerine düşünün. Yardımcı olabilecekseniz oyun döngüsünün içinde DOM'a müdahale etmeyin. Referansları değişkenlerde yeniden kullanmak üzere depolayın. Bir öğe aramanız gerekiyorsa stratejik kapsayıcılara referansları depolayarak ve en yakın üst öğe içinde arama yaparak en kısa rotayı kullanın.

Düzen hatalarıyla karşılaşıyorsanız yeni eklenen öğelerin boyutlarını veya sınıfları kaldırırken/eklerken okumayı erteleyin. İsterseniz Düzen'in tetiklendiğinden de emin olun. Bazen tarayıcı stillerde toplu değişiklikler yapar ve bir sonraki düzen tetikleyicisinden sonra güncellenmez. Bu bazen gerçekten büyük bir sorun olabiliyor, ancak bunun bir sebebi var. Bu yüzden arka planda nasıl işlediğini öğrenmeye çalışın, çok kazanacaksınız.

Fullscreen

Mümkün olduğunda Tam Ekran API'sı aracılığıyla menüde siteyi tam ekran moduna alma seçeneğiniz vardır. Ancak cihazlarda tam ekrana yerleştirme kararı da var. iOS'ta Safari'de daha önce bunu kontrol etmenizi sağlayan bir saldırı yapılmıştı ancak bu özellik artık kullanılamıyor. Bu nedenle, kaydırılmayan bir sayfa oluştururken tasarımınızı bu güvenlik açığı olmadan çalışacak şekilde hazırlamanız gerekiyor. Bu uygulama çok sayıda web uygulamasını bozduğundan, gelecekteki güncellemelerde muhtemelen bu konuyla ilgili güncellemeler almayı bekleyebiliriz.

Öğeler

Denemeler için animasyonlu talimatlar.
Denemeler için animasyonlu talimatlar.

Sitede çok sayıda farklı öğe türümüz bulunmaktadır. Resimler (PNG ve JPEG), SVG (satır içi ve arka plan), sprite sayfa (PNG), özel simge yazı tipleri ve Adobe Edge animasyonları kullanılır. Öğenin vektör tabanlı olamayacağı öğeler ve animasyonlar (model sayfaları) için PNG'ler kullanırız. Aksi takdirde, mümkün olduğunca SVG kullanmaya çalışırız.

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

  • Dosya boyutu küçük.
  • Her bölümü ayrı olarak canlandırabiliriz (gelişmiş animasyonlar için mükemmeldir). Örneğin, Hobbit logosunun (Smaug'un çorak arazisi) ölçeği küçültüldüğünde"alt başlığını" gizleriz.
  • SVG HTML etiketi olarak yerleştirilebilir veya fazladan yükleme olmaksızın arka plan resmi olarak kullanılabilir (html sayfasıyla aynı anda yüklenir).

Ölçeklenebilirlik konusunda SVG ile aynı avantajlara sahip olan simge yazı tipleri, yalnızca rengi değiştirebilmemizi gerektiren simge gibi küçük öğeler için SVG yerine kullanılır. Simgelerin yeniden kullanımı da çok kolaydır. Tek yapmanız gereken bir öğenin CSS "content" özelliğini ayarlamaktır.

Animasyonlar

Bazı durumlarda, SVG öğelerine kodla animasyon eklemek, özellikle de tasarım sürecinde animasyonun çok fazla değiştirilmesi gerektiğinde çok zaman alabilir. Tasarımcılar ve 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 gerçekten yakın ve bu durum ekibe yardımcı oldu, ancak kendi yükleyicileri ve uygulama mantığıyla birlikte geldiğinden özellikle Edge animasyonlarını öğe yükleme işlemimize entegre etmenin bazı dezavantajları var.

Hâlâ web'de öğeleri ve el yapımı animasyonları ele almak için mükemmel bir iş akışına sahip olmamız için epey yol almamız gerektiğini düşünüyorum. Edge gibi araçların nasıl gelişeceğini görmek için sabırsızlanıyoruz. Yorumlar bölümünde diğer animasyon araçları ve iş akışlarıyla ilgili önerilerinizi ekleyebilirsiniz.

Sonuç

Şimdi, projenin tüm bölümleri yayınlandığında 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 bunu ne kadar sorunsuz, entegre ve etkili bir şekilde başarabileceğimize dair beklentilerimiz çok daha düşüktü. Bizim için harika bir öğrenme deneyimi oldu ve yineleme ve test için harcanan süre (büyük ölçüde) modern tarayıcıların nasıl çalıştığını daha iyi anlamamızı sağladı. Bu tür projelerde üretim süresini kısaltmak istiyorsak tahminde bulunma aşamasından bilgiye ulaşmayı başarmak bizim işimiz olacak.