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, paralaksi performanslı bir şekilde uygulamanı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.

Paralaks sorunu

Ö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 bir işlem gibi görünse de modern tarayıcıların önemli bir mekanizması, eşzamansız olarak çalışabilmeleridir. Bu durum, özel olarak bizim durumumuzda kaydırma etkinlikleri için 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. Mobil 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 gönderir ancak Chrome'a benzer şekilde "en iyi çaba" esasına göre çalışır. 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

Her karede boya kullanmaktan da kaçınmak isteriz. Birçok çözüm, paralaks görünümü sağlamak için background-position değerini değiştirmeye çalışır. Bu, tarayıcının sayfanın etkilenen kısımlarını kaydırırken yeniden boyamasına neden olur ve bu da animasyonun önemli ölçüde sarsılmasına neden olacak 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.
  • Söz konusu öğenin alt öğelerine Z ekseninde bir kaydırma uygulayın ve ekrandaki boyutlarını etkilemeden paralaks hareketi sağlamak için bunları tekrar ölçeklendirin.

Bu yaklaşımın CSS'si şu şekildedir:

.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ğerine orantılı olarak daha küçük olmasına 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 ölçeklendirilmesi 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ırmada, kaydırma öğesi ile alt öğeleri karşılaştırıldığında kaydırma 1:1 oranında 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 efektin bazı sınırlamaları vardır. Dönüşümler için önemli olan sınırlamalardan biri, alt öğelerde 3D efektlerin korunmasıdır. 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 efekti kaybolur. Çö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. Paralaksı her durumda kullanamamamız uygulamamızın çalışmasını etkilemez ancak bir geçici çözüm bulmak iyi olur.

position: sticky imdadınıza yetişiyor!

Aslında, öğelerin kaydırma sırasında görüntü alanının üst kısmına veya belirli bir üst öğeye "yapışmasına" olanak tanımak için position: sticky şeklinde bir yardım mevcuttur. Çoğu teknik özellik gibi bu da oldukça hacimli ancak içinde faydalı bir bilgi yer alıyor:

Bu, ilk bakışta çok önemli bir şey gibi görünmeyebilir ancak bu cümledeki önemli nokta, bir öğenin yapışkanlığının tam olarak nasıl hesaplandığıyla ilgilidir: "Ofset, kaydırma kutusu içeren en yakın ataya göre hesaplanır". 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, hesaplanan kaydırma ofsetini değiştiren ve paralaks efekti oluşturan bir perspective değeri uygular.

<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üm ise bunu yapmaz. 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.

Bu bilgiler biraz soyut geliyorsa Robert Flack'ın bu demosuna göz atın. Bu demoda, öğelerin yapışkan yerleşim özelliğiyle ve bu özellik olmadan nasıl farklı davrandığı gösterilmektedir. Farklılığı görmek için Chrome Canary'a (bu makalenin yazıldığı sırada 56 sürümü) 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 Robert Flack tarafından hazırlanmış bir demo.

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

Ancak her şeyde olduğu gibi, düzeltilmesi gereken bazı sorunlar da var:

  • Sabit destek tutarsız. 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 fazla büyüdü." Birçok tarayıcı, sayfa içeriğinin ne kadar büyük olacağına karar verirken ölçeği dikkate alır. Ancak maalesef Chrome ve Safari perspektifi dikkate almaz. 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 performanslı, kaydırmayla eşleştirilmiş ve tarayıcılar arası olacak şekilde uygulamak mümkündür. İstenilen etkiyi elde etmek için biraz matematiksel manevra 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.