Performanslı Paralaks

Paul Lewis
Robert Flack
Robert Flack

İster seviyor ister sevmeyin, paralaks efektleri kalıcı bir trend. Akıllıca kullanıldığında web uygulamasına derinlik ve incelik katabilir. Ancak sorun, paralaksın performanslı bir şekilde uygulanmasının zor olmasıdır. Bu makalede, hem yüksek performanslı hem de en az bunun kadar önemli olarak tarayıcı genelinde çalışan bir çözümden bahsedeceğiz.

Paralaks görseli.

Özet

  • Paralaks animasyon 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 etkisinin yayılmasını sağlamak için position: sticky kullanın.

Hazır çözümü kullanmak istiyorsanız UI Element Samples GitHub deposuna gidip paralel kaydırma yardımcı JS'yi indirin. GitHub deposunda paralel kaydırma özelliğinin canlı demosunu görebilirsiniz.

Problem paralaksörleri

Öncelikle, paralaks efekti elde etmenin yaygın iki yoluna ve özellikle de bu yöntemlerin amaçlarımız için neden uygun olmadığına göz atalım.

Kötü: Kaydırma etkinliklerini kullanma

Paralaksın temel koşulu, kaydırmayla eşleştirilmesidir. 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 yayınlanır ve kaydırma animasyonunun her karesinde yayınlanması garanti edilmez.

Bu önemli bilgi, öğeleri kaydırma etkinliklerine göre hareket ettiren JavaScript tabanlı bir çözümden neden kaçınmamız gerektiğini bize gösterir: JavaScript, paralaksın sayfanın kaydırma konumuyla uyumlu olmasını garanti etmez. Mobile Safari'nin eski sürümlerinde kaydırma etkinlikleri aslında kaydırma işleminin sonunda yayınlanıyordu. Bu da JavaScript tabanlı bir kaydırma efekti oluşturmayı imkansız kılıyordu. 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 yayınlanmaz. Bu da paralaks etkisinin kaybolacağı anlamına gelir.

Hatalı: 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, animasyonu önemli ölçüde bozacak kadar maliyetli olabilir.

Paralaks hareketi sunmak istiyorsak hızlandırılmış bir özellik olarak uygulanabilen (bugün dönüşümler ve opaklığa bağlı kalmak anlamına gelir) ve kaydırma etkinliklerine ihtiyaç duymayan bir şey istiyoruz.

3D CSS

Hem Scott Kellum hem de Keith Clark, paralaks hareketi elde etmek için CSS 3D'yi kullanma konusunda önemli çalışmalar yaptı. Kullandıkları teknik şu şekildedir:

  • overflow-y: scroll (ve muhtemelen overflow-x: hidden) ile kaydırılacak 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ın CSS'si şöyle görünür:

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

Bu durumda, aşağıdaki gibi bir HTML snippet'i varsayılır:

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

Perspektif için ölçeği ayarlama

Alt öğeyi geri itmek, perspektif değeriyle orantılı olarak daha küçük hale gelmesine neden olur. (Perspektif - mesafe) / perspektif denklemini kullanarak ne kadar ölçeklendirilmesi gerektiğini hesaplayabilirsiniz. Paralaks öğesinin paralaks yapmasını ancak oluşturduğumuz boyutta görünmesini istediğimizden, öğenin olduğu gibi bırakılması yerine bu şekilde ölçeklendirilmesi gerekir.

Yukarıdaki kodda perspektif 1 piksel, parallax-child'nin Z mesafesi ise -2 piksel'dir. Bu, öğenin 3 kat büyütülmesi gerektiği anlamına gelir. Bu değer, koda eklenen değerdir: scale(3).

translateZ değeri uygulanmamış içerikler için sıfır değerini kullanabilirsiniz. Bu, ölçeğin (perspektif - 0) / perspektif olduğu anlamına gelir. Bu değer 1'e eşit olduğundan ölçek yukarı veya aşağı ölçeklendirilmemiştir. Gerçekten çok kullanışlı.

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

Bu bilgiyi kısa süre içinde kullanacağımız için bunun neden işe yaradığını net bir şekilde anlamanız önemlidir. Kaydırma, aslında bir dönüştürme işlemidir. Bu nedenle hızlandırılabilir. Çoğunlukla katmanların GPU ile kaydırılmasını 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 aynı miktarda yukarı doğru dönüştürülür: 300px.

Ancak kaydırma öğesine bir perspektif değeri uygulamak bu süreci bozar ve kaydırma dönüştürme işleminin temelini oluşturan matrisleri değiştirir. Artık 300 piksellik bir kaydırma, seçtiğiniz perspective ve translateZ değerlerine bağlı olarak alt öğeleri yalnızca 150 piksel hareket ettirebilir. Bir öğenin translateZ değeri 0 ise 1:1 oranında (eskiden olduğu gibi) kaydırılır ancak perspektif orijininden Z yönünde itilen bir alt öğe farklı bir hızda kaydırılır. Net sonuç: paralaks hareketi. Ve çok önemli bir şekilde, bu işlem tarayıcıdaki dahili kaydırma mekanizmasının bir parçası olarak otomatik olarak gerçekleştirilir. Yani scroll etkinliklerini dinlemenize veya background-position değerini değiştirmenize gerek yoktur.

Bir sorun: Mobil Safari

Her efektlerle ilgili uyarılar vardır ve dönüşümler için önemli olanlardan biri, 3D efektlerin alt öğelerde korunmasıyla ilgilidir. Hiyerarşide, perspektif içeren öğe ile paralaks alt öğeleri arasında öğeler varsa 3D perspektif "düzleştirilir". Diğer bir deyişle, efekt 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 düzleştirir. Böylece paralaks etkisini kaybederiz. Çözüm, çoğu durumda oldukça basittir: Öğeye transform-style: preserve-3d eklediğinizde, ağacın daha üst kısmında uygulanan tüm 3D efektlerin (perspektif değerimiz gibi) yayılmasına neden olursunuz.

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

Ancak Mobil Safari söz konusu olduğunda işler biraz daha karmaşıktır. overflow-y: scroll öğesinin kapsayıcı öğesine uygulanması teknik olarak işe yarar ancak kaydırma öğesinin fırlatılabilmesini engeller. Çözüm, -webkit-overflow-scrolling: touch eklemektir ancak bu durumda perspective düzleşeceğinden paralaks oluşmaz.

Kademeli iyileştirme açısından bu durum muhtemelen çok büyük bir sorun değildir. Her durumda paralaks sağlayamasak da uygulamamız çalışmaya devam edecektir. Ancak bir çözüm bulmak faydalı olacaktır.

position: sticky kurtarın!

Aslında, öğelerin kaydırma sırasında görüntü alanının üst kısmına veya belirli bir üst öğeye "sabitlenmesine" olanak tanımak için position: sticky şeklinde bir yardım mevcuttur. Özelliklerin çoğu gibi oldukça kapsamlı, ancak içinde de küçük bir cevher var:

Bu, ilk bakışta çok fazla anlam ifade etmeyebilir Diğer bir deyişle, yapışkan öğenin taşınacağı mesafe (başka bir öğeye veya görüntü alanına bağlı görünmesi için), diğer tüm dönüştürme işlemleri uygulandıktan sonra değil, önce hesaplanır. Bu, önceki kaydırma örneğine çok benzer şekilde, ofset 300 piksel olarak hesaplandıysa bu 300 piksel ofset değerini yapışkan öğelere uygulanmadan önce değiştirmek için perspektifleri (veya başka bir dönüştürme işlemini) kullanmanın yeni bir fırsatı olduğu anlamına gelir.

Paralaks öğesine position: -webkit-sticky uygulayarak -webkit-overflow-scrolling: touch'ün düzleştirme etkisini etkili bir şekilde "ters çevirebiliriz". Bu sayede, paralaks öğesi, kaydırma kutusu içeren en yakın ataya (bu durumda .container) referans verir. 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 Mobil Safari'de paralaks efekti geri yüklendi.

Yapışkan yerleşimle ilgili uyarılar

Ancak burada bir fark vardır: position: sticky, paralaks mekanizmasını değiştirir. Yapışkan konumlandırma, öğeyi kaydırılabilir kapsayıcıya yapıştırmaya çalışır. Yapışkan olmayan sürümde ise bu işlem yapılmaz. Bu, yapışkan uçlara sahip paralaksın, yapışkan uçları olmayan paralaksın tersi olduğu anlamına gelir:

  • position: sticky ile öğe, z=0'a ne kadar yakınsa o kadar az hareket eder.
  • position: sticky olmadığında öğe, z=0'a ne kadar yakınsa o kadar 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. Farklılığı görmek için Chrome Canary (bu makalenin yazıldığı sırada 56 sürümü) veya Safari kullanmanız gerekir.

Paralaks perspektifi ekran görüntüsü

position: sticky'nin paralaks kaydırmayı nasıl etkilediğini gösteren Robert Flack tarafından hazırlanmış 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:

  • Sabit destek tutarlı değil. Chrome'da destek henüz uygulanmaya devam ediyor. Edge'de destek tamamen yok. Firefox'ta ise yapışkan özelliği perspektif dönüşümleriyle birlikte kullanıldığında boyama hataları yaşanıyor. Bu gibi durumlarda, yalnızca gerektiğinde position: sticky (-webkit- ön ekiyle sürüm) eklemek için küçük bir kod eklemek faydalı olabilir. Bu kod yalnızca Mobil Safari içindir.
  • Efekt Edge'de "sadece çalışmaz". Edge, kaydırma işlemini işletim sistemi düzeyinde gerçekleştirmeye çalışır. 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. Bu, Edge'i OS dışı bir kaydırma yöntemine geçirir 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çek uygulanmışsa perspective uygulandıktan sonra öğe 1x ölçekte olsa bile kaydırma çubukları ve benzeri öğeleri görebilirsiniz. Öğeleri sağ alt köşeden ölçeklendirerek (transform-origin: bottom right ile) bu sorunun üstesinden gelebilirsiniz. Bu yöntem, büyük boyutlu öğelerin kaydırılabilir alanın "negatif bölgesine" (genellikle sol üst) genişlemesine neden olduğu için işe yarar. Kaydırılabilir bölgeler, negatif bölgedeki içeriği hiçbir zaman görmenize veya kaydırarak görüntülemenize 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. İstenilen etkiyi elde etmek için biraz matematiksel kıvraklık ve az miktarda standart metin gerektirdiğinden, UI Öğesi Örnekleri GitHub depomuzda bulabileceğiniz küçük bir yardımcı kitaplık ve örnek hazırladık.

İyi eğlenceler. Deneyimlerinizi bizimle paylaşın.