CSS Ayrıntılı İnceleme - mükemmel kareler için özel kaydırma çubuğu için matrix3d()

Özel kaydırma çubukları son derece nadirdir ve bu durum büyük ölçüde kaydırma çubukları, web'de birçok kullanıcı tarafından sabit tutulamaz (Sana bakıyorum, tarih seçici). Kendi JavaScript'inizi oluşturmak için JavaScript kullanabilirsiniz, ancak bu pahalı ve ve takılabilir. Bu makalede, proje yaşam döngüsü boyunca standart olmayan CSS matrislerinden yararlanarak Kaydırma sırasında JavaScript, yalnızca bazı kurulum kodları vardır.

Özet

Küçük şeyler sizin için önemli değil mi? Tek yapmanız gereken Nyan kedi demosu kitaplığını edinmek ister misiniz? Demonun kodunu şurada bulabilirsiniz: GitHub deposu.

LAM;WRA (Uzun ve matematiksel; yine de okuyacak)

Bir süre önce bir paralaks kaydırıcısı geliştirdik ( ? Gerçekten iyi, zaman ayırmaya değer!). CSS 3D kullanarak öğeleri geri aktararak öğeler gerçek kaydırma hızımızdan daha yavaş hareket etmiştir.

Özet

Paralaks kaydırma çubuğunun çalışma şeklini özetleyerek başlayalım.

Animasyonda gösterildiği gibi, öğeleri iterek paralaks efektini 3D uzayda Z ekseni boyunca "geriye doğru". Dokümanda kaydırmak çevirisini görebilirsiniz. Örneğin, 100 piksel gibi aşağı kaydırdığımızda, öğesi, 100 piksel yukarıya çevrilir. Bu tüm öğeler için geçerlidir, hatta "daha eskideki" kitleler olabilir. Ancak web'den daha uzakta çünkü ekranda gözlemlenen hareketleri 100 pikselden az olur ve böylece paralaks efektini ortaya koyabilir.

Elbette bir öğeyi tekrar uzaya taşımak da onun daha küçük görünmesini sağlar. Bunu düzeltmek için öğeyi tekrar yukarı doğru ölçeklendiririz. Arkadaş Bitkiler projesinin ilk projemizin paralaks kaydırma çubuğu tüm detayları tekrarlamayacağım.

0. Adım: Ne yapmak istiyoruz?

Kaydırma çubukları. İşte bunu geliştireceğiz. Peki hiç düşündünüz mü merak ediyor musunuz? Kesinlikle yapmadım. Kaydırma çubukları, Şu anda mevcut içeriğin ne kadarının görünür durumda olduğu ve ne kadar ilerleme kaydedildiğini siz de düşünmüşsünüzdür. Ekranı aşağı kaydırdığınızda kaydırma çubuğu da projenin sonuna doğru ilerleme kaydettiğinizi gösterir. Tüm içerikler uygunsa görüntü alanında kaydırma çubuğu genellikle gizlidir. İçerik, görüntü alanının yüksekliğinin 2 katıysa kaydırma çubuğu, görüntü alanının yüksekliğinin 1⁄2'sini kaplar. 3 kat yüksekliğe sahip içerik görüntü alanı, kaydırma çubuğunu görüntü alanının 1⁄3'üne ölçeklendirir. Bu şekilde kalıbı görürsünüz. Kaydırmak yerine kaydırma çubuğunu tıklayıp sürükleyerek de yardımcı olur. Bu, göze çarpmayan bir kişi için şaşırtıcı derecede büyük bir buna benzer. Gelin, teker teker savaşalım.

1. Adım: Ters konulma

Tamam, CSS 3D kullanarak öğelerin kaydırma hızından daha yavaş hareket etmesini sağlayabiliriz ana hatlarıyla açıklandığı gibi dönüştürülür. Aynı zamanda yönünüzü bulmak mı istiyorsunuz? Görünüşe göre yapabiliriz. Kullanıcılardan farklı bir alan mükemmel, özel kaydırma çubuğu. Bunun nasıl işlediğini anlamak için bir konuyu ele almalıyız: CSS 3D ile ilgili temel bilgileri edineceğim.

Matematiksel açıdan herhangi bir perspektif projeksiyonunu elde etmek için büyük ihtimalle homojen koordinatlar kullanmanız gerekir. Bunların ne olduğunu ve neden işe yaradığını ayrıntılı olarak açıklamayacağım, ama Bunlar, w adlı ek bir dördüncü koordinatla birlikte 3D koordinatlar gibidir. Bu perspektif bozulması isteniyorsa koordinat değeri 1 olmalıdır. Biz b'nin ayrıntılarıyla ilgili olarak endişelenmenize gerek yoktur; çünkü 1 dışında bir değerdir. Dolayısıyla, tüm noktalar artık 4 boyutlu vektörlerden alınır [x, y, z, w=1] ve sonuç olarak matrislerde olması gerekir.

CSS'nin kullanarak bir dönüştürme özelliğinde kendi 4x4 matrislerinizi tanımlamaktır. matrix3d() işlevi. matrix3d, 16 bağımsız değişken alır (çünkü matris 4x4) biçimindedir). Bu fonksiyonu kullanarak çevirmeleri, çevirmeleri vb. manuel olarak belirtme w koordinatını karıştırabilir!

matrix3d() özelliğini kullanabilmek için 3D bir bağlama ihtiyacımız var. Sonuçta 3D bağlamda perspektif bozulması ve 3D içeriğe koordinatlar arasında olacaktır. 3D bağlam oluşturmak için, perspective ve içindeki bazı öğeler, yeni Studio'da dönüştürülecek. 3D alan oluşturdu. Örneğin, örnek:

CSS kodu kullanarak bir div'i bozan bir CSS kodu parçası
    perspektif özelliği için de geçerlidir.

Perspektif kapsayıcısının içindeki öğeler, CSS motoru tarafından işlenir şu şekilde:

  • Bir öğenin her bir köşesini (köşeyi) homojen koordinatlara dönüştürün [x,y,z,w], perspektif kapsayıcısına göre.
  • Öğenin tüm dönüşümlerini sağdan sola matris olarak uygulayın.
  • Perspektif öğesi kaydırılabiliyorsa bir kaydırma matrisi uygulayın.
  • Perspektif matrisini uygulayın.

Kaydırma matrisi, y ekseni boyunca uzanan bir çeviridir. Aşağıya kayarak 400 piksel olarak ayarlamak için tüm öğelerin 400 piksel x yukarıya taşınması gerekir. Perspektif matrisi noktaları kaybolma noktasına 3D'de daha da geri "çeken" matris ne kadar iyi olduğunu ortaya koyabilir. Bu da, öğeler bir araya getirildiğinde öğelerin daha küçük görünmesinin bu nedenle çeviri sırasında "daha yavaş hareket ediyor". Dolayısıyla, bir öğe geri itilirse 400 piksellik bir çeviri, öğenin ekranda yalnızca 300 piksel hareket ettirin.

Tüm ayrıntıları öğrenmek istiyorsanız CSS'ye ilişkin spesifikasyon ancak bu makalede örnek olarak, her bir dönüşüm işleminde daha fazla bilgi edineceksiniz.

Kutumuz, perspective için p değerine sahip bir perspektif kabının içinde ve kapsayıcının kaydırılabilir olduğunu ve kapsayıcının n piksel.

Perspektif matrisi çarpı kaydırma matrisi çarpı öğe dönüşüm matrisi
  dördüncü satırdaki eksi bir/p üstü dörde dörde dörde eşit özdeşlik matrisine eşittir
  üçüncü sütun çarpı dörde dört özdeşlik matrisi ile ikincide eksi n
  satır dördüncü sütun çarpı öğe dönüşüm matrisi.

İlk matris perspektif matrisi, ikinci matris ise kaydırmadır. matristir. Özetlemek gerekirse: Kaydırma matrisinin işi, bir öğeyi yukarıya taşımak aşağı kaydırılıyor. Bu nedenle eksi işareti vardır.

Ancak kaydırma çubuğumuz için tam tersini istiyoruz. Öğemizin aşağıya doğru aşağı kaydırabilirsiniz. İpuçlarını şu şekilde kullanabiliriz: Kutumuzun köşelerinin w koordinatını ters çevirme. w koordinatı -1, tüm çeviriler ters yönde gerçekleşir. Peki, bunu nasıl bu mu? CSS motoru, kutumuzun köşelerini üçgen şeklinde ve w'yı 1'e ayarlar. matrix3d() ile dikkatleri üzerinize çekin!

.box {
  transform:
    matrix3d(
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, -1
    );
}

Bu matrisin amacı w'yu eksiltmektir. CSS motoru, her köşeyi [x,y,z,1] biçiminde bir vektöre dönüştürürse matris [x,y,z,-1] biçimine dönüştürebilirsiniz.

Dördüncü satırda eksi bir bölü p bulunan dörte dörde özdeş özdeşlik matrisi
  üçüncü sütun çarpı dörde dört özdeşlik matrisi ile ikincide eksi n
  üçüncü satır dördüncü sütun çarpı dörde dört özdeşlik matrisi ile değer eksi bir
  dördüncü satır dördüncü sütun çarpı dört boyutlu vektör x, y, z, 1 eşittir dört
  dördüncü satırın üçüncü sütunundaki eksi bir, p üzerinden, dört özdeşlik matrisine göre
  ikinci satır dördüncü sütunda eksi n ve dördüncü satırdan bir eksi
  dördüncü sütun dört boyutlu x, y artı n, z, eksi z üzeri vektörüne eşittir
  p eksi 1.

Öğe dönüşümümüzün etkisini göstermek için bir ara adım listeledim matristir. Matris matematiğiyle ilgili olarak kendinizi rahat hissetmiyorsanız sorun değil. Eureka son satırda, n'nin kaydırma ofsetini y'mize ekleyerek koordinasyonunu kullanabilirsiniz. Öğe aşağı doğru çevrilir aşağı kaydırdığımızda da görünür.

Ancak bu matrisi örnek, öğe görüntülenmez. Çünkü CSS spesifikasyonu için w < olan tepe noktası 0 değeri, öğenin oluşturulmasını engeller. Z kuşağından koordinat şu anda 0'dır ve p değeri 1'dir; w ise -1 olur.

Neyse ki z'nin değerini seçebiliriz. w=1'in elde edilmesini sağlamak için kullanarak z = -2 olarak ayarlayın.

.box {
  transform:
    matrix3d(
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, -1
    )
    translateZ(-2px);
}

Bakın, kutusu geri döndü!

2. Adım: Harekete geçirin

Kutumuz oldu ve bu kutu hiç kutucuk olmasaydı da dönüşür. Perspektif kapsayıcısı şu anda kaydırılamadığı için yapamıyoruz. ancak öğemizin başka yöne gideceğini biliyoruz kaydırıldı. Kapsayıcıyı kaydıralım, değil mi? Yalnızca yer kaplayan ayırıcı öğe:

<div class="container">
    <div class="box"></div>
    <span class="spacer"></span>
</div>

<style>
/* … all the styles from the previous example … */
.container {
    overflow: scroll;
}
.spacer {
    display: block;
    height: 500px;
}
</style>

Şimdi de kutuyu kaydırın! Kırmızı kutu aşağı hareket eder.

3. Adım: Bir beden belirleyin

Sayfa aşağı kaydırıldığında aşağı hareket eden bir öğemiz vardır. İşte zor olan yardımcı olabilir. Şimdi kaydırma çubuğuna benzer bir stil belirlemeli etkileşimli hale getirmektir.

Kaydırma çubuğu genellikle bir "parmak" ve "parça"dan oluşur ancak parça her zaman görünür olması gerekir. Başparmak yüksekliği, başparmağının yüksekliği ile doğru orantılıdır. görünür olduğundan emin olun.

<script>
    const scroller = document.querySelector('.container');
    const thumb = document.querySelector('.box');
    const scrollerHeight = scroller.getBoundingClientRect().height;
    thumb.style.height = /* ??? */;
</script>

scrollerHeight, kaydırılabilir öğenin yüksekliğidir, scroller.scrollHeight, kaydırılabilir içeriğin toplam yüksekliğidir. scrollerHeight/scroller.scrollHeight, içerikle ilgili olup görünür. Başparmağın kapladığı dikey alanın oranı Görünür içeriğin oranı:

baş parmak nokta stili nokta yüksekliği, ScrollerHeight üzerinde kaydırma yüksekliğine eşittir
  yalnızca baş parmak nokta stili nokta yüksekliğinde kaydırma çubuğu üzerinde nokta kaydırma yüksekliği
  şuna eşittir: kaydırma çubuğu yüksekliği çarpı kaydırma yüksekliği, kaydırma çubuğu nokta kaydırma üzerinde
  yükseklik.
<script>
    // …
    thumb.style.height =
    scrollerHeight * scrollerHeight / scroller.scrollHeight + 'px';
    // Accommodate for native scrollbars
    thumb.style.right =
    (scroller.clientWidth - scroller.getBoundingClientRect().width) + 'px';
</script>

Başparmak boyutu iyi görünüyorsa ama çok hızlı ilerliyor. Burada tekniğimizi paralaks kaydırıcısı. Öğeyi daha geriye taşırsak öğe, daha yavaş kaydırır. Bu boyutu büyüterek düzeltebiliriz. Ama ne kadar çaba göstermeli geri alabilir miyiz? Haydi tahmin edelim, matematik problemi çözelim. Bu son defa, sözü.

En önemli bilgi de şu: Parmağınızın alt kenarını tamamen kaydırıldığında kaydırılabilir öğenin alt kenarıyla hizalanacak aşağı tüketim. Başka bir deyişle: Sayfayı kaydırdıysak Resmimiz scroller.scrollHeight - scroller.height piksel olsun scroller.height - thumb.height tarafından çevrildi. Kaydırma çubuğunun her pikseli için bir pikselin bir kısmını hareket ettirmesini istiyorsanız:

Faktör eşittir: kaydırma çubuğu nokta yüksekliği eksi baş parmak nokta yüksekliği kaydırma çubuğu üzerinde
  nokta kaydırma yüksekliği eksi kaydırma çubuğu nokta yüksekliği.

Bu, bizim ölçeklendirme faktörümüz. Şimdi ölçeklendirme faktörünü paralaks kaydırmada yaptığımız gibi, z ekseninde çeviri makalesine göz atın. Kaynak: Özelliklerdeki ilgili bölüme dikkat edin: Ölçeklendirme faktörü p/(p − z) değerine eşittir. Bu denklemi, z'den 'e doğru giderebiliriz. başparmağımızı z ekseni boyunca ne kadar çevirmemiz gerektiğini ölçebiliriz. Ama devam et w koordinatı kurcalamalarımız nedeniyle, bir dili z boyunca ek -2px. Ayrıca, bir öğenin dönüşümlerinin uygulandığını anlamına gelir. Özel matrisinizden önceki tüm çeviriler, Ancak özel matristen sonraki tüm çeviriler tersine çevrilir. Haydi bunu kodla!

<script>
    // ... code from above...
    const factor =
    (scrollerHeight - thumbHeight)/(scroller.scrollHeight - scrollerHeight);
    thumb.style.transform = `
    matrix3d(
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, -1
    )
    scale(${1/factor})
    translateZ(${1 - 1/factor}px)
    translateZ(-2px)
    `;
</script>

Bir kaydırma çubuğunu kullanabilirsiniz. Bu yalnızca bir DOM öğesi. İstediğimiz gibi şekillendirebiliriz. Projeye ilişkin açısından en önemli şey başparmağınızın bu kişilere yanıt vermesidir. ve sürükleyebilirsiniz. Çünkü birçok kullanıcı kaydırma çubuğuyla bu şekilde etkileşimde bulunmaya alışkın. Bu blog yayınını daha da uzatmamak adına, bu konudaki detaylıca değineceğiz. Şu bölüme göz atın: kitaplık kodu ayrıntılara göz atın.

Peki ya iOS?

Ah, eski arkadaşım iOS Safari. Paralaks kaydırmada olduğu gibi bahsedeceğim. Bir öğe üzerinde kaydırdığımız için -webkit-overflow-scrolling: touch, ancak bu, 3D düzleştirmeye ve tüm kaydırma efekti çalışmayı durdurur. Bu sorunu paralaks kaydırıcıda çözdük iOS Safari'yi tespit edip geçici çözüm olarak position: sticky'ı kullanarak ve Burada da tam olarak aynı şeyi yapacağız. Şu bölüme göz atın: paralaks oluşturma makalesi dokunun.

Tarayıcının kaydırma çubuğuna ne olacak?

Bazı sistemlerde kalıcı, yerel bir kaydırma çubuğuyla uğraşmak zorundayız. Önceden kaydırma çubuğu gizlenemez ( standart olmayan sözde seçici) kullanabilirsiniz. Dolayısıyla, bunu gizlemek için (matematiksiz) birtakım bilgisayar korsanlarına başvurmamız gerekiyor. İçerikleri kaydırma öğesini overflow-x: hidden içeren bir kapsayıcıda açın ve kaydırma öğesi, kapsayıcıdan daha geniştir. Tarayıcının yerel kaydırma çubuğu artık görüntü dışında.

Fin

Hepsini bir araya getirdiğimizde, artık muhteşem bir özel tasarım oluşturabiliriz. kaydırma çubuğunu kullanın (örneğin, Nyan kedi demosu.

Nyan kedisini görmüyorsanız, bulduğumuz ve dosyaladığımız bir hata (Nyan kedisinin görünmesi için başparmağını tıklayın). Chrome, gereksiz çalışmalardan kaçınmada gerçekten çok iyidir boyama veya animasyon gibi bir şeyler hazırlayabilirsiniz. Kötü haber şu ki matris kurnazlıkları, Chrome'un Nyan kedi GIF'inin aslında ekran dışı olduğunu düşünmesine neden oluyor. Bu sorunun kısa süre içinde düzeltileceğini umuyoruz.

İşte oldu. Çok emek harcamıştım. Tamamını okuduğunuz için sizi tebrik ederim şey. Bu biraz işe yarayacağını bilmenin bir yolu yoktur ve muhtemelen çabaya değmez Ancak özelleştirilmiş kaydırma çubuğunun bu deneyimin önemli bir parçası olduğu durumlar hariç. Ama mümkün olduğunu bilmek güzel, değil mi? Bir şeyi yapmanın zor olduğu düşünüldüğünde, özel kaydırma çubuğu, CSS tarafında yapılacak işler olduğunu gösterir. Endişelenmeyin! Gelecekte, Houdini AnimationWorklet, bunun gibi mükemmel kaydırma bağlantılı efektleri çok daha kolay hale getiriyor.