Performanslı Paralaks

Paul Lewis
Robert Flack
Robert Flack

İster sevin, ister nefret edin, paralaks oyununa ara veriyorum. Makul bir şekilde kullanıldığında, web uygulamasına derinlik ve incelik katabilir. Ancak sorun, paralaksmayı etkili bir şekilde uygulamanın zor olabilmesidir. Bu makalede hem yüksek performanslı hem de en az farklı tarayıcılarda çalışan bir çözümü ele alacağız.

Paralaks görseli.

Özet

  • Paralaks animasyonlar oluşturmak için kaydırma etkinlikleri veya background-position kullanmayın.
  • Daha doğru bir paralaks efekti oluşturmak için CSS 3D dönüştürmelerini kullanın.
  • Mobile Safari'de paralaks efektinin yayılmasını sağlamak için position: sticky kullanın.

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

Sorunlu paralakslar

İlk olarak, paralaks etkisini sağlamak için sık kullanılan iki yola , özellikle de bunların amaçlarımıza uygun olmadıklarına göz atalım.

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

Paralakslamanın temel koşulu, kaydırmayla birleştirilmelidir; sayfanın kaydırma konumundaki her değişiklik için paralaks oluşturan öğenin konumu güncellenmelidir. Kulağa basit gelse de, modern tarayıcıların önemli bir mekanizması eşzamansız olarak çalışma yeteneğidir. Bu, özel örneğimizde kaydırma etkinlikleri için geçerlidir. Çoğu tarayıcıda kaydırma etkinlikleri "en iyi çaba" yöntemiyle sunulur ve kaydırma animasyonunun her karesinde gerçekleşecekleri garanti edilmez.

Bu önemli bilgi bize öğeleri kaydırma etkinliklerine göre taşıyan JavaScript tabanlı bir çözümden neden kaçınmamız gerektiğini söyler: JavaScript, paralaks işleminin sayfanın kaydırma konumuna göre uygulanacağını garanti etmez. Mobile Safari'nin eski sürümlerinde kaydırma etkinlikleri aslında kaydırmanın sonunda sunuluyordu. Bu da JavaScript tabanlı bir kaydırma efektinin yapılmasını imkansız hale getiriyordu. Daha yeni sürümler, kaydırma etkinliklerini animasyon sırasında yayınlar, ancak Chrome'a benzer şekilde, "en iyi çaba" temelinde yapılır. Ana iş parçacığı başka bir işle meşgulse kaydırma etkinlikleri hemen teslim edilmez, yani paralaks efekti kaybolur.

Kötü: background-position güncelleniyor

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

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

3D CSS

Hem Scott Kellum hem de Keith Clark, paralaks hareketi sağlamak için CSS 3D'yi kullanma alanında önemli çalışmalar yürütmüştür ve kullandıkları teknik şu şekildedir:

  • overflow-y: scroll (ve muhtemelen overflow-x: hidden) ile kaydırmak için kapsayıcı bir öğe ayarlayın.
  • Aynı öğeye bir perspective değeri ve top left veya 0 0 olarak ayarlanmış bir perspective-origin uygulayın.
  • Bu öğenin alt öğelerine Z çevirisi uygulayın ve ekrandaki boyutlarını etkilemeden paralaks hareketi sağlamak için bunları ölçeklendirin.

Bu yaklaşım için CSS şö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 örnekte aşağıdaki gibi bir HTML snippet'i varsayılmaktadır:

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

Perspektif için ölçek ayarlanıyor

Alt öğeyi geri itmek, öğenin perspektif değeriyle orantılı olarak küçülmesine neden olur. Şu denklemle resmin ne kadar büyütülmesi gerektiğini hesaplayabilirsiniz: (perspektif - mesafe) / perspektif. Büyük olasılıkla paralaks yapan öğenin paralaks yapmasını istediğimizden, ancak oluşturduğumuz boyutta görünmelerini istediğimiz için olduğu gibi bırakmak yerine, ölçeğinin bu şekilde artırılması gerekir.

Yukarıdaki kodda perspektif 1px ve parallax-child'nin Z mesafesi 1px'dir. Diğer bir deyişle, öğenin 3x büyütülmesi gerekir. Burada, koda eklenen değeri görebilirsiniz: scale(3).

translateZ değeri uygulanmamış herhangi bir içeriği sıfır değerini kullanarak değiştirebilirsiniz. Bu, ölçeğin (perspektif - 0) / perspektif olduğu anlamına gelir. Yani, net olarak 1 değerini alır, yani yukarı veya aşağı ölçeklenmemiştir. Oldukça kullanışlı.

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

Bu bilgiden kısa süre içinde yararlanacağımız için bunun neden işe yaradığını açıklığa kavuşturmak önemlidir. Kaydırma etkili bir şekilde bir dönüşüm olduğu için hızlandırılabilir. Çoğunlukla, katmanların GPU ile birlikte kaydırılması içerir. Perspektif algısı olmayan tipik bir kaydırmada kaydırma, kaydırma öğesi ile alt öğeleri karşılaştırılırken 1:1 şeklinde gerçekleşir. Bir öğeyi 300px aşağı kaydırırsanız alt öğeleri aynı miktarda dönüştürülür: 300px.

Bununla birlikte, kaydırma öğesine perspektif değeri uygulamak bu süreci karıştırır; kaydırma dönüşümünün 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 kaydırılır (eskiden olduğu gibi) ancak Z yönünde perspektiften uzaklaşan bir çocuk farklı bir hızda kaydırılır. Net sonuç: paralaks hareketi. En önemlisi de bu, tarayıcının dahili kaydırma makinesinin bir parçası olarak otomatik şekilde işlenir. Diğer bir ifadeyle, scroll etkinliklerini dinlemek veya background-position öğelerini değiştirmek gerekmez.

Merhemdeki sinek: Mobile Safari

Her efekt için dikkat edilmesi gereken noktalar vardır. Dönüşümler için önemli bir nokta da alt öğeler üzerindeki 3D efektlerin korunmasıdır. Hiyerarşide perspektifli öğe ile paralaks oluşturan alt öğeleri arasında öğeler varsa 3D perspektif "düzleştirilmiş olur", yani 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üzeltir ve paralaks efektini kaybederiz. Çoğu durumda çözüm oldukça basittir: Öğeye transform-style: preserve-3d eklersiniz. Böylece, ağacın daha yukarı kısımlarına uygulanmış 3D efektleri (perspektif değerimiz gibi) yayabilirsiniz.

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

Ancak Mobile Safari'de işler biraz daha karmaşıktır. Kapsayıcı öğesine overflow-y: scroll uygulamak teknik olarak işe yarar ancak kaydırma öğesini hızlı bir şekilde kaydırabilme pahasına. Çözüm, -webkit-overflow-scrolling: touch eklemektir, ancak aynı zamanda perspective düzelir ve herhangi bir paralaks oluşmaz.

Kademeli geliştirme açısından bakıldığında, muhtemelen bu çok da önemli bir sorun değildir. Her durumda paralaks uygulayamazsak uygulamamız çalışmaya devam eder, ancak bir geçici çözüm bulmak iyi olur.

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

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ını" sağlayan position: sticky biçiminde bazı yardımlar vardır. Bu özelliklerin çoğu gibi spesifikasyon oldukça uzundur, ancak içinde faydalı küçük bir cevher bulunur:

Bu ilk bakışta çok önemli bir anlama gelmeyebilir ancak bu cümledeki önemli bir nokta, bir öğenin yapışkanlılığının tam olarak nasıl hesaplandığıyla ilgilidir: "ofset, kaydırma kutusuyla en yakın üst öğe temel alınarak hesaplanır". Diğer bir deyişle, yapışkan öğeyi hareket ettirmek için gereken mesafe (başka bir öğeye veya görüntü alanına ekli görünmesi için) başka dönüştürme işlemlerinden sonra değil, başka bir dönüştürme uygulanmadan önce hesaplanır. Bu, önceki kaydırma örneğinde olduğu gibi, ofset 300 piksel olarak hesaplandıysa yapışkan öğelere uygulanmadan önce 300 piksel ofset değerini değiştirmek için perspektifleri (veya başka herhangi bir dönüşümü) kullanmak için yeni bir fırsat olduğu anlamına gelir.

Paralaks öğesine position: -webkit-sticky uygulayarak -webkit-overflow-scrolling: touch öğesinin birleştirme etkisini etkili bir şekilde "geri alabiliriz". Bu, paralaks oluşturan öğenin bir kaydırma kutusuyla en yakın üst öğeye (bu örnekte .container) başvuruda bulunmasını sağlar. Ardından .parallax-container, öncekine benzer şekilde bir perspective değeri uygular. Bu değer, hesaplanan kaydırma uzaklığını 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, Mobile Safari için paralaks efektini geri yükler. Bu, her yönüyle mükemmel bir haberdir!

Sabit konumlandırma uyarıları

Burada bir fark vardır: position: sticky paralaks mekanizmasını değiştirir. Yapışkan konumlandırma, öğeyi kaydırma kapsayıcısına yapışmaya çalışır, yapışkan olmayan sürümde ise yapışmaz. Yani yapışkanlı paralaks, aşağıdakileri içermeyen paralaksın tersi olur:

  • position: sticky olduğunda, öğe ne kadar daha az hareket ederse z=0'a ne kadar yakındır.
  • position: sticky olmadığı durumlarda, öğe z=0'a ne kadar yakın olursa harekete de o kadar yakın olur.

Tüm bunlar biraz soyut görünüyorsa Robert Flack'in yaptığı demoya göz atın. Bu demoda, yapışkan konumlandırma ile ve yapışkan konumlandırma olmadan öğelerin nasıl farklı davranışlar sergilediğini görebilirsiniz. Farkı görmek için Chrome Canary (yazma sırasındaki sürüm 56'dır) veya Safari'ye ihtiyacınız vardır.

Paralaks perspektifi ekran görüntüsü

position: sticky ürününün paralaks kaydırmasını nasıl etkilediğini gösteren Roman Flack tarafından hazırlanmış bir demo.

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

Ancak her şeyde olduğu gibi yine de yumuşatılması gereken yumrular ve çıkıntılar vardır:

  • Sabit destek tutarsız. Chrome'da destek hâlâ uygulanmaktadır, Edge için hiç destek yoktur ve Firefox'un yapışkanlı yaklaşım perspektif dönüşümleriyle birleştirildiğinde boyama hataları olmaktadır. Bu gibi durumlarda, yalnızca gerektiğinde (yalnızca Mobile Safari'ye yönelik) position: sticky (önlü -webkit- sürüm) eklemek için küçük bir kod eklemek faydalı olacaktır.
  • Bu efekt Edge'de "sadece çalışmaz". Edge, kaydırma işlemini işletim sistemi düzeyinde yapmaya çalışır. Bu genellikle iyi bir şeydir ancak bu durumda, kaydırma sırasında perspektif değişikliklerini algılamasını engeller. Edge'yi OS dışı bir kaydırma yöntemine geçirerek perspektif değişikliklerini hesaba kattığı için bu sorunu düzeltmek için sabit konumlu bir öğe ekleyebilirsiniz.
  • "Sayfanın içeriği çok büyüdü!" Birçok tarayıcı, sayfa içeriğinin ne kadar büyük olduğuna karar verirken ölçeği dikkate alır, ancak maalesef Chrome ve Safari bakış açısını dikkate almıyor. Dolayısıyla, örneğin bir öğeye 3x ölçek uygulanmışsa öğe, perspective uygulandıktan sonra 1x değerinde olsa bile kaydırma çubukları ve benzerlerini görebilirsiniz. Bu sorunu, sağ alt köşedeki (transform-origin: bottom right ile) öğeleri ölçeklendirerek çözebilirsiniz. Bu yöntem, aşırı boyutlu öğelerin kaydırılabilir alanın "negatif bölgesinde" (genellikle sol üst tarafta) büyümesine neden olur. Kaydırılabilir bölgeler, negatif bölgedeki içeriği görmenize veya sayfayı kaydırmanıza izin vermez.

Sonuç

Paralakslar dikkatli bir şekilde kullanıldığında eğlenceli bir efekttir. Gördüğünüz gibi, yüksek performanslı, kaydırmalı ve tarayıcılar arası bir şekilde uygulamak mümkündür. İstenen etkiyi elde etmek için biraz matematiksel hareket ve az miktarda ortak metin gerektirdiğinden, UI Element Samples GitHub depomuzda bulabileceğiniz küçük bir yardımcı kitaplık ve örnek hazırladık.

Biraz eğlenin ve yolculuğunuzu bize bildirin.