Paralaks'

Paul Lewis

Giriş

Paralaks siteleri son zamanlarda çok rağbet gören siteler olmuştur, bunlara bir göz atın:

Bunlar hakkında bilginiz yoksa, sayfayı aşağı kaydırdığınızda sayfanın görsel yapısının değiştiği sitelerdir. Normalde sayfa içindeki öğeler ölçeklenir, döner veya sayfadaki kaydırma konumuna göre orantılı olarak hareket eder.

Demo paralaks sayfası
Demo sayfamız paralaks efektiyle tamamlandı

Paralaks yapan siteleri sevip sevmemek de önemli olsa da, bu sitelerin performansın kara deliği olduğu konusunda kendinden emin bir şekilde ifade edebilirsiniz. Bunun nedeni tarayıcıların genellikle, kaydırma yaptığınızda ekranın üst veya alt kısmında yeni içeriğin görünmesi (kaydırma yönünüze bağlı olarak) için optimize edilmiş olması ve genel anlamda, tarayıcıların bir kaydırma sırasında görsel olarak çok az değişiklik yapıldığında en iyi şekilde çalışmasıdır. Paralaks sitelerinde çoğu zaman büyük görsel öğeler değişip tarayıcının tüm sayfayı yeniden boyamasına neden olduğu için nadiren böyle bir durum yaşanır.

Paralaks içeren bir siteyi şu şekilde genelleştirmek mantıklıdır:

  • Yukarı ve aşağı doğru kaydırdığınızda konumlarını, dönüşlerini ve ölçeklerini değiştiren arka plan öğeleri.
  • Tipik bir yukarıdan aşağıya doğru kayan metin veya daha küçük resimler gibi sayfa içeriği.

Daha önce kaydırma performansından ve uygulamanızın yanıt verme hızını iyileştirmek için uygulayabileceğiniz yöntemlerden bahsetmiştik. Bu makale de bu temel üzerine inşa edilmiştir. Bu nedenle, henüz yapmadıysanız bu makaleyi okumanız yararlı olabilir.

Sorulması gereken soru, paralaks kaydırmalı bir site inşa ediyorsanız pahalı boyalara takılıp kalmadığınız mı, yoksa performansı en üst düzeye çıkarmak için uygulayabileceğiniz alternatif yaklaşımlar mı olduğudur. Seçeneklerimize göz atalım.

1. Seçenek: DOM öğelerini ve mutlak konumları kullanma

Çoğu kullanıcı varsayılan olarak bu yaklaşımı benimser. Sayfa içinde birçok öğe vardır ve bir kaydırma etkinliği tetiklendiğinde, bu öğelerin dönüştürülmesi için bir dizi görsel güncelleme yapılır.

DevTools Zaman Çizelgesi'ni kare modunda başlatır ve etrafta gezinirseniz tam ekran boyama işlemlerinin pahalı olduğunu görürsünüz. Çok fazla kaydırma yaparsanız tek bir kare içinde, her biri düzen çalışmasını tetikleyecek birkaç kaydırma etkinliği görebilirsiniz.

Ardışık kaydırma etkinlikleri olmayan Chrome Geliştirici Araçları.
Tek bir karede büyük boyutlu boyalar ve etkinlik tetiklemeli birden çok düzen gösteren Geliştirici Araçları.

Unutmamanız gereken önemli bir nokta, 60 fps'ye (60 Hz normal monitör yenileme hızına karşılık gelir) ulaşmak için her şeyi halletme süresinin 16 ms'nin biraz üzerinde olmasıdır. Bu ilk sürümde görsel güncellemelerimizi her kaydırma etkinliği aldığımızda gerçekleştiriyoruz, ancak requestAnimationFrame ile daha yalın, daha kötü animasyonlar ve kaydırma performansı konularında daha önce bahsettiğimiz gibi, bu durum tarayıcının güncelleme programıyla örtüşmez. Bu nedenle, kareleri atlar veya her birinde çok fazla işlem yaparız. Bu, sitenizde kolayca olumsuz ve doğal olmayan bir izlenime neden olarak kullanıcıların hayal kırıklığına uğramasına ve kedi yavrularının hayal kırıklığına uğramasına yol açabilir.

Güncelleme kodunu, kaydırma etkinliğinden requestAnimationFrame geri çağırmasına taşıyalım ve kaydırma etkinliğinin geri çağırmasındaki kaydırma değerini yakalayalım.

Kaydırma testini tekrarlarsanız, büyük bir fark olmasa da küçük bir iyileşme görebilirsiniz. Bunun nedeni, kaydırma ile tetiklediğimiz düzen işleminin o kadar da pahalı olmaması, ancak diğer kullanım durumlarında gerçekten mümkün olabilmesidir. Artık her karede en azından bir düzen işlemi gerçekleştiriyoruz.

Ardışık kaydırma etkinlikleri olan Chrome Geliştirici Araçları.
Tek bir karede büyük boyutlu boyalar ve etkinlik tetiklemeli birden çok düzen gösteren Geliştirici Araçları.

Artık kare başına yüz veya yüz kaydırma etkinliğini işleyebiliyoruz ancak en önemlisi, requestAnimationFrame geri çağırması her çalıştırıldığında ve görsel güncellemelerimizi gerçekleştirdiğinde yalnızca en son kullanılacak değeri depoluyoruz. Bunun nedeni, her kaydırma etkinliği aldığınızda görsel güncellemeleri zorunlu kılmayı denemekten, tarayıcının bunları yapmak için size uygun bir pencere vermesini istemeye başlamış olmanızdır. Tatlı değil misin?

Bu yaklaşımdaki ana sorun (requestAnimationFrame olsun veya olmasın), temelde tüm sayfa için tek bir katmanımız olması ve bu görsel öğeleri hareket ettirerek büyük (ve pahalı) yeniden boyamalar yapılması gerekmesidir. Tipik olarak, boyama işlemi engelleme işlemidir (ancak bu değişir), tarayıcı başka bir iş yapamaz ve çoğu zaman çerçevemizin 16 ms'lik bütçesini aşarız ve her şey kötü durumda kalır.

2. Seçenek: DOM öğelerini ve 3D dönüşümleri kullanma

Mutlak konumlar yerine başka bir yaklaşım olarak öğelere 3D dönüşümleri uygulayabiliriz. Bu durumda, 3D dönüşümlerinin uygulandığı öğelere öğe başına yeni bir katman verildiğini ve WebKit tarayıcılarında da genellikle donanım birleştiriciye geçişe neden olduğunu görürüz. 1. Seçenek'te ise, bir şey değiştiğinde ve boyama ve birleştirme işlemleri CPU tarafından gerçekleştirildiğinde yeniden boyanması gereken sayfa için büyük bir katmanımız vardı.

Bu, bu seçenekle her şeyin farklı olduğu anlamına gelir: 3D dönüşümü uyguladığımız herhangi bir öğe için potansiyel olarak bir katmanımız olur. Bu noktadan itibaren yaptığımız tek şey öğelerde daha fazla dönüşüm yapmaksa katmanı yeniden boyamamız gerekmez. GPU ise öğeleri hareket ettirerek son sayfayı birlikte bir araya getirme işini halledebilir.

Çoğu kişi sadece -webkit-transform: translateZ(0); saldırısını kullanarak sihirli performans iyileştirmeleri görür. Bu yöntem bugün işe yararken bazı sorunlar ortaya çıktı:

  1. Tarayıcılar arası uyumlu değildir.
  2. Dönüştürülen her öğe için yeni bir katman oluşturarak tarayıcının elini zorlar. Çok sayıda katman, başka performans darboğazlarına neden olabilir. Bu nedenle, kullanırken dikkatli olun.
  3. Bu özellik, bazı WebKit bağlantı noktaları için devre dışı bırakılmıştır (alttan dördüncü madde).

3D çeviri rotasında dikkatli hareket ederseniz, bu, sorununuza geçici bir çözümdür! İdeal olarak, 3D'de olduğu gibi 2D dönüşümlerden benzer oluşturma özellikleri görürüz. Tarayıcılar olağanüstü bir hızla gelişiyor. Umarız bunu daha önce göreceğiz.

Son olarak, mümkün olan her yerde boyalar yapmamayı hedeflemeli ve var olan öğeleri sayfanın etrafında taşımalısınız. Örneğin, paralaks sitelerde tipik bir yaklaşım olan, sabit yükseklikte div'ler kullanmak ve efektin elde edilmesi için arka plan konumlarını değiştirmektir. Ne yazık ki bu, öğenin her geçişte yeniden boyanması gerektiği anlamına gelir. Bu da performans açısından size maliyet çıkarabilir. Bunun yerine, mümkünse öğeyi oluşturmanız (gerekirse overflow: hidden ile bir div öğesinin içine sarmalamanız) ve çevirmeniz yeterlidir.

3. Seçenek: Sabit konumlu tuval veya WebGL kullanın

Dikkate alacağımız son seçenek, sayfanın arka tarafında, dönüştürülen görüntülerimizi çizeceğimiz sabit konumlu bir kanvas kullanmaktır. Bu ilk bakışta en etkili çözüm gibi görünmeyebilir, ancak bu yaklaşımın bazı avantajları vardır:

  • Tek bir öğe (tuval) bulunduğundan artık birleştirme işlemi için eskisi kadar çok çalışılması gerekmiyor.
  • Şu anda donanım hızlandırmalı tek bir bit eşlemi yönetiyoruz.
  • Canvas2D API, gerçekleştirmek istediğimiz dönüşüm türleri için son derece uygun. Yani geliştirme ve bakım işlemleri daha kolay yönetilebilir.

Bir tuval öğesi kullanmak bize yeni bir katman sağlar, ancak bu yalnızca tek bir katmandır. 2. Seçenek'te ise aslında bize 3D dönüştürme uygulanmış her öğe için yeni bir katman verilmişti. Dolayısıyla, tüm bu katmanları bir araya getiren artan bir iş yüküne sahibiz. Bu aynı zamanda, dönüşümlerin tarayıcılar arası farklı uygulamaları ışığında bugün en uyumlu çözümdür.


/**
 * Updates and draws in the underlying visual elements to the canvas.
 */
function updateElements () {

  var relativeY = lastScrollY / h;

  // Fill the canvas up
  context.fillStyle = "#1e2124";
  context.fillRect(0, 0, canvas.width, canvas.height);

  // Draw the background
  context.drawImage(bg, 0, pos(0, -3600, relativeY, 0));

  // Draw each of the blobs in turn
  context.drawImage(blob1, 484, pos(254, -4400, relativeY, 0));
  context.drawImage(blob2, 84, pos(954, -5400, relativeY, 0));
  context.drawImage(blob3, 584, pos(1054, -3900, relativeY, 0));
  context.drawImage(blob4, 44, pos(1400, -6900, relativeY, 0));
  context.drawImage(blob5, -40, pos(1730, -5900, relativeY, 0));
  context.drawImage(blob6, 325, pos(2860, -7900, relativeY, 0));
  context.drawImage(blob7, 725, pos(2550, -4900, relativeY, 0));
  context.drawImage(blob8, 570, pos(2300, -3700, relativeY, 0));
  context.drawImage(blob9, 640, pos(3700, -9000, relativeY, 0));

  // Allow another rAF call to be scheduled
  ticking = false;
}

/**
 * Calculates a relative disposition given the page's scroll
 * range normalized from 0 to 1
 * @param {number} base The starting value.
 * @param {number} range The amount of pixels it can move.
 * @param {number} relY The normalized scroll value.
 * @param {number} offset A base normalized value from which to start the scroll behavior.
 * @returns {number} The updated position value.
 */
function pos(base, range, relY, offset) {
  return base + limit(0, 1, relY - offset) * range;
}

/**
 * Clamps a number to a range.
 * @param {number} min The minimum value.
 * @param {number} max The maximum value.
 * @param {number} value The value to limit.
 * @returns {number} The clamped value.
 */
function limit(min, max, value) {
  return Math.max(min, Math.min(max, value));
}

Bu yaklaşım, büyük resimler (veya bir tuvale kolayca yazılabilen diğer öğeler) ile uğraştığınızda işe yarar ve büyük metin bloklarıyla çalışmak da kesinlikle daha zor olabilir, ancak sitenize bağlı olarak en uygun çözüm de olabilir. Kanvastaki metinle uğraşmanız gerekiyorsa fillText API yöntemini kullanmanız gerekir. Ancak bu, erişilebilirlik maliyetine neden olur (metni bir bit eşlem halinde pikselleştirirsiniz!) ve artık satır kaydırma ve bir dizi başka sorunla uğraşmanız gerekir. Bundan kaçınabiliyorsanız kesinlikle yapmalısınız ve yukarıdaki dönüşüm yaklaşımı kullanılarak daha iyi hizmet alabilirsiniz.

Bunu mümkün olduğunca açıkladığımıza göre, paralaks çalışmasının bir tuval öğesi içinde yapılması gerektiğini düşünmek için hiçbir neden yok. Tarayıcı destekliyorsa WebGL'yi kullanabiliriz. Burada önemli olan, WebGL'nin tüm API'ler arasında grafik kartına en doğrudan yönlendirme imkanı olmasıdır. Bu nedenle, özellikle sitenin efektleri karmaşıksa 60 fps'ye ulaşmak için en olası adayınızdır.

Hemen tepkiniz WebGL'nin aşırı yüklü olması veya destek açısından her yerde kullanılamaması olabilir. Ancak Three.js gibi bir dosya kullanıyorsanız her zaman bir tuval öğesi kullanmaya devam edebilirsiniz. Böylece kodunuz tutarlı ve samimi bir şekilde soyutlanır. Tek yapmamız gereken Modernizr kullanarak uygun API desteğini kontrol etmektir:

// check for WebGL support, otherwise switch to canvas
if (Modernizr.webgl) {
  renderer = new THREE.WebGLRenderer();
} else if (Modernizr.canvas) {
  renderer = new THREE.CanvasRenderer();
}

Bu yaklaşımla ilgili son bir fikir olarak, sayfaya ekstra öğeler eklemekten hoşlanmıyorsanız, hem Firefox hem de WebKit tabanlı tarayıcılarda bir tuvali arka plan öğesi olarak her zaman kullanabilirsiniz. Bu her zaman karşılaşılan bir sorun olmadığı için her zamanki gibi dikkatli davranmalısınız.

Seçim size kalmış

Geliştiricilerin diğer seçenekler yerine tamamen konumlandırılmış öğeleri varsayılan olarak kullanmasının asıl nedeni, desteğin her yerde mevcut olması olabilir. Hedeflenen eski tarayıcılar büyük olasılıkla son derece kötü bir oluşturma deneyimi sağlayacağından, bu bir dereceye kadar hayali bir şeydir. Mutlaka konumlandırılmış öğeler kullanan günümüzün modern tarayıcılarında bile iyi bir performans elde etmek mümkün olmayabilir!

Dönüşümler, özellikle de 3D türleri, DOM öğeleriyle doğrudan çalışabilmeniz ve sağlam bir kare hızı elde edebilmenizi sağlar. Burada başarının anahtarı, mümkün olan her yerde boyamaktan kaçınmak ve öğeleri hareket ettirmeye çalışmaktır. WebKit tarayıcılarının katman oluşturma şeklinin diğer tarayıcı motorlarıyla ilişkili olması gerekmediğini unutmayın. Bu nedenle, bu çözüme başvurmadan önce bunu test ettiğinizden emin olun.

Yalnızca en üst düzey tarayıcıları hedefliyorsanız ve siteyi tuval kullanarak oluşturabiliyorsanız, sizin için en iyi seçenek bu olabilir. Three.js kullanırsanız ihtiyaç duyduğunuz desteğe bağlı olarak oluşturucular arasında çok kolay bir şekilde geçiş yapabilmeniz gerekir.

Sonuç

Paralaks sitelerle ilgili olarak, kesinlikle konumlandırılmış öğelerden sabit konumlu tuval kullanmaya kadar birkaç yaklaşımı değerlendirdik. Gerçekleştireceğiniz uygulama, elbette ne elde etmeye çalıştığınıza ve üzerinde çalıştığınız belirli tasarıma bağlıdır. Ancak seçeneklerinizin olduğunu bilmek her zaman faydalıdır.

Her zaman olduğu gibi, tahmin etmeyin, test edin yaklaşımını hangi yöntemle denerseniz çalışın.