Performanslı Paralaks

Paul Lewis
Robert Flack
Robert Flack

İster sevin, ister nefret edin, paralaks hep hayatımızda. Akıllıca kullanıldığında bir web uygulamasına derinlik ve incelik katabilir. Ancak sorun, paralaksı etkili bir şekilde uygulamanın zor olabileceğidir. Bu makalede, hem yüksek performans gösteren hem de tarayıcılar arası çalışan bir çözümü ele alacağız.

Paralaks resmi.

Özet:

  • Paralaks animasyonları oluşturmak için kaydırma etkinliklerini veya background-position öğesini kullanmayın.
  • Daha doğru bir paralaks efekti oluşturmak için CSS 3D dönüştürmelerini kullanın.
  • Mobil Safari'de paralaks efektinin yayıldığından emin olmak için position: sticky kullanın.

Geçici çözümü kullanmak istiyorsanız UI Element Samples GitHub deposuna gidin ve Parallax yardımcı JS'sini alın. GitHub deposunda paralaks kaydırma çubuğunun canlı demosunu görebilirsiniz.

Problem paralaksörleri

İlk olarak, paralaks etkisi elde etmenin yaygın olarak kullanılan iki yoluna, özellikle de amaçlarımız için neden uygun olmadıklarına bakalım.

Kötü: Kaydırma etkinlikleri kullanılıyor

Paralaks yapmanın temel koşulu, kaydırmayla bağlantılı olmasıdır. Sayfanın kaydırma konumundaki her değişiklik için paralaks öğesinin konumu güncellenmelidir. Bu basit gibi görünse de, modern tarayıcıların önemli bir mekanizması, eşzamansız olarak çalışma yeteneğidir. Bu, bizim örneğimizde kaydırma etkinlikleri için de geçerlidir. Çoğu tarayıcıda kaydırma etkinlikleri "en iyi çaba" olarak sunulur ve kaydırma animasyonunun her karesinde sağlanacağı garanti edilmez.

Bu önemli bilgi, öğeleri kaydırma etkinliklerine göre hareket eden JavaScript tabanlı bir çözümden neden kaçınmamız gerektiğini açıklar: JavaScript, paralaksın sayfanın kaydırma konumuna uygun olacağını garanti etmez. Mobile Safari'nin eski sürümlerinde, kaydırma etkinlikleri aslında kaydırmanın sonunda gerçekleşiyordu. Bu da JavaScript tabanlı bir kaydırma efektinin oluşturulmasını imkansız hale getiriyordu. Daha yeni sürümler, animasyon sırasında kaydırma etkinlikleri gerçekleştirir, ancak Chrome'a benzer şekilde, "en iyi çaba" esasına göre hareket eder. Ana iş parçacığı başka bir işle meşgulse kaydırma etkinlikleri hemen teslim edilmez. Bu da paralaks efektinin kaybedileceği anlamına gelir.

Kötü: background-position güncelleniyor

Kaçınmak istediğimiz bir başka durum da her kareyi boyamaktır. Birçok çözüm, paralaks görünümünü sağlamak için background-position değerini değiştirmeye çalışır. Bu da tarayıcının sayfayı kaydırıldığında sayfanın etkilenen bölümlerini yeniden boyamasına neden olur ve bu durum, animasyonu önemli ölçüde sekteye uğratacak kadar maliyetli olabilir.

Paralaks hareketi vaadini yerine getirmek istiyorsak hızlandırılmış özellik olarak uygulanabilecek (bugün dönüşüm ve opaklığa bağlı kalma anlamına gelir) ve kaydırma etkinliklerine bağlı olmayan bir özellik istiyoruz.

3D CSS

Hem Scott Kellum hem de Keith Clark, paralaks hareketi elde etmek için CSS 3D kullanma alanında önemli çalışmalar gerçekleştirdi. Kullandıkları teknik ise şu şekilde:

  • overflow-y: scroll (ve muhtemelen overflow-x: hidden) ile kaydırmak için bir kapsayıcı öğe oluşturun.
  • Aynı öğeye bir perspective değeri ve top left veya 0 0 olarak ayarlanmış bir perspective-origin uygulayın.
  • Bu öğenin alt öğelerine Z ile çeviri uygulanır ve ekrandaki boyutları etkilenmeden paralaks hareketi sağlayacak şekilde ölçeklendirilir.

Bu yaklaşım için CSS aşağıdaki gibidir:

.container {
  width: 100%;
  height: 100%;
  overflow-x: hidden;
  overflow-y: scroll;
  perspective: 1px;
  perspective-origin: 0 0;
}

.parallax-child {
  transform-origin: 0 0;
  transform: translateZ(-2px) scale(3);
}

Aşağıdaki gibi bir HTML snippet'i varsayılır:

<div class="container">
    <div class="parallax-child"></div>
</div>

Perspektif için ölçek ayarlanıyor

Alt öğeyi geri itmek, perspektif değeriyle orantılı olarak daha küçük hale gelmesine neden olur. Şu denklemle ne kadar ölçeklenmesi gerektiğini hesaplayabilirsiniz: (perspektif - mesafe) / perspektif. Büyük olasılıkla paralaks unsurunun paralaks almasını istediğimiz, ancak yazıldığı boyutta göründüğü için olduğu gibi bırakılması yerine bu şekilde ölçeklenmesi gerekir.

Yukarıdaki kodda, perspektif 1 piksel ve parallax-child öğesinin Z mesafesi -2 piksel'dir. Bu, öğenin 3x oranında artırılması gerektiği anlamına gelir. Koda eklenen değeri görebilirsiniz: scale(3).

translateZ değeri uygulanmamış içerikler için sıfır değerini kullanabilirsiniz. Bu da ölçeğin (perspektif - 0) / perspektif (perspektif) olduğu anlamına gelir. Bu da 1 değerini verir. Diğer bir deyişle, ölçeğin artırılıp azaltılmadığı anlamına gelir. Çok kullanışlı, gerçekten.

Bu yaklaşımın işleyiş şekli

Bu yöntemin neden işe yarayacağını net bir şekilde bilmek önemlidir, çünkü bu bilgiyi kısa süre içinde kullanacağız. Kaydırma etkili bir şekilde bir dönüşümdür, bu yüzden hızlandırılabilir. Çoğunlukla katmanların GPU ile yerlerinin değiştirilmesini içerir. Perspektif kavramı olmayan tipik bir kaydırma işleminde, kaydırma öğesi ile alt öğeleri karşılaştırılırken kaydırma bire bir şekilde gerçekleşir. Bir öğeyi 300px kadar aşağı kaydırırsanız alt öğeleri de aynı miktarda yukarı dönüştürülür: 300px.

Bununla birlikte, kaydırma öğesine bir perspektif değeri uygulamak bu süreçte karışıklığa yol açar; kaydırma dönüşümünün temelini oluşturan matrisleri değiştirir. 300 piksellik bir kaydırma, seçtiğiniz perspective ve translateZ değerlerine bağlı olarak artık alt öğeleri yalnızca 150 piksel hareket ettirebilir. Bir öğenin translateZ değeri 0 ise öğe 1:1'de (eski adıyla) kaydırılır. Ancak Z'de belirtilenden uzağa itilen bir çocuk, perspektif kaynağından uzağa kaydırılır. Net sonuç: paralaks hareket. En önemlisi de bu, otomatik olarak tarayıcının dahili kaydırma makinesinin bir parçası olarak işlenir. Böylece scroll etkinliklerini dinleme veya background-position değişikliği gerekmez.

Mermer sinek: Mobile Safari

Her efektlerle ilgili uyarılar vardır ve dönüşümler için önemli olanlardan biri, 3D efektlerin alt öğelerde korunmasıyla ilgilidir. Perspektifi olan öğe ile paralaks yapan alt öğeleri arasındaki hiyerarşide öğeler varsa 3D perspektif "düzeltilmiş" olur, yani etkinin kaybolur.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>

Yukarıdaki HTML'de .parallax-container yenidir ve perspective değerini etkili bir şekilde birleştirir ve paralaks etkisini kaybederiz. Çoğu durumda çözüm oldukça basittir: Öğeye transform-style: preserve-3d eklerseniz ağacın daha yukarılarına uygulanan 3D efektlerini (perspektif değerimiz gibi) yaymasını sağlarsınız.

.parallax-container {
  transform-style: preserve-3d;
}

Ancak Mobile Safari söz konusu olduğunda işler biraz daha karmaşıktır. Container öğesine overflow-y: scroll uygulamak teknik olarak çalışır ancak kaydırma öğesini hızlıca kaydırma maliyetine bağlıdır. Çözüm -webkit-overflow-scrolling: touch eklemektir ancak perspective öğesi de düzeltilir ve paralaks işlemi uygulanmaz.

Progresif geliştirmeler açısından bakıldığında bu, büyük olasılıkla çok büyük bir sorun değildir. Her durumda paralaks uygulayamıyorsak uygulamamız çalışmaya devam edecektir. Ancak bir çözüm bulmak faydalı olacaktır.

position: sticky kurtarın!

Aslında, öğelerin görüntü alanının üst kısmına veya kaydırma sırasında belirli bir üst öğeye "sapmasını" sağlayan position: sticky biçiminde bazı yardımlar vardır. Özelliklerin çoğu gibi oldukça kapsamlı, ancak içinde de küçük bir cevher var:

Bu, ilk bakışta çok fazla bir anlam ifade etmeyebilir Diğer bir deyişle, yapışkan öğeyi hareket ettirecek mesafe (başka bir öğeye veya görüntü alanına bağlı görünmesi için) sonra değil, başka dönüştürme işlemleri uygulanmadan önce hesaplanır. Bu, önceki kaydırma örneğinde olduğu gibi, ofset 300 pikselde hesaplandıysa bu 300 piksellik ofset değerini yapışkan öğelere uygulanmadan önce manipüle etmek için perspektifleri (veya başka bir dönüşümü) kullanmak için yeni bir fırsat ortaya çıkar.

Paralaks oluşturma öğesine position: -webkit-sticky uygulayarak -webkit-overflow-scrolling: touch düzleştirme etkisini etkili bir şekilde "ters çevirebiliriz". Bu, paralaks öğesinin bir kaydırma kutusuyla en yakın üst öğeye (bu örnekte .container) başvurmasını sağlar. Ardından, öncekine benzer şekilde .parallax-container, perspective değerini uygular. Bu değer, hesaplanan kaydırma ofsetini değiştirir ve paralaks efekti oluşturur.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>
.container {
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}

.parallax-container {
  perspective: 1px;
}

.parallax-child {
  position: -webkit-sticky;
  top: 0px;
  transform: translate(-2px) scale(3);
}

Bu sayede Mobile Safari için paralaks efekti geri yüklenir ve her şey için çok iyi bir haber olur!

Yapışkan konumlandırma uyarıları

Ancak burada bir fark vardır: position: sticky paralaks mekaniğini değiştirir. Yapışkan konumlandırma, öğeyi kayan kapsayıcıya yapıştırmaya çalışırken yapışkan olmayan sürümlerde bunu yapmaz. Yani, yapışkanlı paralaks, içermeyen paralaksın tersidir:

  • position: sticky kullanıldığında öğe, z=0 değerine ne kadar yakın olursa daha az hareket eder.
  • position: sticky olmadığında, öğe z=0'a ne kadar yakın olursa o kadar çok hareket eder.

Bütün bunlar biraz soyut görünüyorsa, Robert Flack'in yaptığı bu demoya göz atın. Bu demoda öğelerin yapışkan konumlandırma ile ve sabit konumlama olmadan nasıl farklı davrandığı gösterilmektedir. Farkı görmek için Chrome Canary (yazma sırasındaki sürüm 56) veya Safari'ye ihtiyacınız vardır.

Paralaks perspektifi ekran görüntüsü

position: sticky'nin paralaks kaydırmayı nasıl etkilediğini gösteren, Rober Flack tarafından sunulan bir demo.

Çeşitli hatalar ve geçici çözümler

Yine de her şeyde olduğu gibi, yine de düzeltilmesi gereken yumrular ve çıkıntılar vardır:

  • Sürekli destek tutarsız. Chrome'da destek uygulanmaya devam ediyor, Edge'de hiç destek yok ve Firefox'ta yapışkan yapışkan perspektif dönüşümleriyle birleştirildiğinde boyama hataları var. Bu tür durumlarda, sadece gerektiğinde position: sticky (-webkit- önekli sürüm) eklemek, yani sadece Mobile Safari için küçük bir kod eklemekte fayda vardır.
  • Efekt, Edge'de "sadece çalışmıyor". Edge, kaydırma işlemini işletim sistemi düzeyinde yapmaya çalışıyor. Bu genellikle iyi bir şeydir ancak bu durumda, kaydırma sırasında perspektif değişikliklerini algılamasını engeller. Bu sorunu düzeltmek için sabit konum öğesi ekleyebilirsiniz. Bunun nedeni, Edge'nin işletim sistemi olmayan bir kaydırma yöntemine geçmesini sağlar ve perspektif değişikliklerini hesaba katmasını sağlar.
  • "Sayfanın içeriği çok arttı!" Birçok tarayıcı, sayfa içeriğinin ne kadar büyük olduğuna karar verirken ölçeği hesaba katar, ancak maalesef Chrome ve Safari bakış açısını hesaba katmamaktadır. Dolayısıyla, bir öğeye 3x ölçeği uygulanmışsa kaydırma çubukları ve benzerleri, perspective uygulandıktan sonra öğe 1x değerinde olsa bile görebilirsiniz. Öğeleri sağ alt köşeden (transform-origin: bottom right ile) ölçeklendirerek bu sorunu çözmek mümkündür. Çünkü büyük boyutlu öğelerin, kaydırılabilir alanın "negatif bölgesinde" (genellikle sol üstte) büyümesine neden olur. Kaydırılabilir bölgeler, negatif bölgedeki içeriği görmenize veya içeriklere gitmenize hiçbir zaman izin vermez.

Sonuç

Paralaks, dikkatli bir şekilde kullanıldığında eğlenceli bir efekttir. Gördüğünüz gibi, bu özelliği yüksek performans gösteren, kaydırmaya bağlı ve tarayıcılar arası bir şekilde uygulamak mümkündür. Bu yöntemde, istenilen etkiyi elde etmek için biraz matematiksel hareket ve küçük bir standart metin yer almaktadır. Bu yüzden, UI Element Samples GitHub depomuzda bulabileceğiniz küçük bir yardımcı kitaplık ve örneği hazırladık.

Biraz oynayıp konu hakkında bize bilgi verin.