Kaydırma Sırasında Paralaks Efekti Oluşturma

Kaydırma sırasında paralaks efekti, kullanıcı bir sayfayı aşağı kaydırdıkça farklı içerik katmanlarının farklı hızlarda hareket ettiği görsel bir tekniktir. Bu, ön plandaki öğelerin arka plandaki öğelerden daha hızlı hareket ettiği bir derinlik illüzyonu oluşturarak ilgi çekici ve etkileyici bir gezinme deneyimi sunar. Bu efekt, animasyonları bir kapsayıcının kaydırma konumuna bağlamanıza olanak tanıyan CSS kaydırmaya dayalı animasyonlar kullanılarak en iyi şekilde elde edilir.

Nasıl uygulanır?

Temel bir paralaks efekti oluşturmak için aşağıdaki adımları uygulayın:

  1. Sarmalayıcı öğe oluşturun: Bu öğe, paralaks efektinin tüm katmanlarını bir araya getirir. Kaydırılabilir öğe olmadığından taşması kırpılmalıdır. Ayrıca, paralaks efektinin katmanlarından birinin yüksekliğiyle eşleşen bir height verin.

    <div class="wrapper">
      …
    </div>
    
    .wrapper {
      overflow: clip;
      height: 100vh; /* Height of one of the layers of the parallax */
    }
    
  2. Katmanları tanımlayın: Sarmalayıcının içine, farklı hızlarda hareket edecek katmanları tek tek ekleyin.

    <div class="wrapper">
      <div class="layer">LAYER 0</div>
      <div class="layer">LAYER 1</div>
      <div class="layer">LAYER 2</div>
      …
    </div>
    
  3. Çeviri animasyonu ekleme: Katmanların transform özelliğini değiştiren bir CSS animasyonu tanımlayın. Paralaks efekti için genellikle katmanları dikey olarak hareket ettirmek üzere translateY kullanırsınız.

    @keyframes parallax {
      from {
        transform: translateY(700px);
      }
    }
    
  4. view-timeline ayarını yapın: Animasyonu kaydırma konumuna bağlamak için sarmalayıcı öğede bir view-timeline oluşturun ve ardından bunu katmanlara uygulayın.

    .wrapper {
      view-timeline: --wrapper;
    }
    
    .layer {
      animation: parallax linear both;
      animation-timeline: --wrapper;
    }
    
  5. Animasyonları kademelendirme: Katmanların farklı hızlarda hareket etmesini sağlamak için iki ana yaklaşımdan birini kullanabilirsiniz: anahtar karelerde kademelendirme veya animation-range kademelendirme.

    Bu yaklaşımların her ikisinde de sabit kodlu değerler veya sibling-index()/sibling-count() uygulaması kullanılabilir. Sabit kodlu değerler en kolay yöntemdir ve yalnızca sınırlı sayıda katman olduğunda kullanışlıdır. sibling-index()/sibling-count() uygulaması, çok sayıda katmanınız olduğunda kullanışlıdır.

    • Animasyon karelerini kademelendirme:

      Sabit kodlu değerleri kullanarak her katman için özel bir özellik tanımlayabilir ve paralaks ofsetini manuel olarak kontrol edebilirsiniz.

      .layer:nth-child(1) { --offset: 100px; }
      .layer:nth-child(2) { --offset: 200px; }
      .layer:nth-child(3) { --offset: 300px; }
      
      @keyframes parallax {
        from {
          transform: translateY(var(--offset));
        }
      }
      

      sibling-index() kullanarak, kademeli etkiyi otomatik olarak hesaplamak için sibling-index() işlevinin kardeşleri arasında bir alt öğenin dizinini döndürmesini sağlayın.

      @keyframes parallax {
        from {
          transform: translateY(calc(100px * sibling-index()));
        }
      }
      
    • animation-range kademeli olarak kullanıma sunuluyor:

      Sabit kodlu değerleri kullanarak her katmanda animation-range sınırlarını ayrı ayrı tanımlayabilirsiniz.

      .layer:nth-child(1) { animation-range: entry 25% exit 50%; }
      .layer:nth-child(2) { animation-range: entry 25% exit 75%; }
      .layer:nth-child(3) { animation-range: entry 25% exit 100%; }
      

      sibling-index() ve sibling-count() kullanarak aralığı toplam katman sayısına (sibling-count()) göre matematiksel olarak hesaplayabilirsiniz.

      .layer {
        animation-range: entry 25% exit calc(100% / sibling-count() * sibling-index());
      }
      

Örnek kod

@keyframes parallax {
  from {
    transform: translateY(calc(100px * sibling-index()));
  }
}

.wrapper {
  view-timeline: --wrapper;
}

.layer {
  animation: parallax linear both;
  animation-timeline: --wrapper;
}

@media (prefers-reduced-motion: reduce) {
  .layer {
    animation: none;
  }
}

Alternatif olarak, benzer bir etki elde etmek için animation-range özelliğini kullanabilirsiniz:

@keyframes parallax {
  from {
    transform: translateY(700px);
  }
}

.wrapper {
  view-timeline: --wrapper;
}

.layer {
  animation: parallax linear both;
  animation-timeline: --wrapper;
  animation-range: entry 25% exit calc(100% / sibling-count() * sibling-index());
}

@media (prefers-reduced-motion: reduce) {
  .layer {
    animation: none;
  }
}

En İyi Uygulamalar

Kaydırmaya dayalı animasyonları kullanırken sorunsuz ve erişilebilir bir deneyim sunmak için birkaç en iyi uygulamayı takip etmek önemlidir:

  • ÖZELLİK algılamayı ekleyin: Tüm tarayıcılar kaydırmaya dayalı animasyonları desteklemez. Destek durumunu kontrol etmek ve desteklemeyen tarayıcılar için yedek sağlamak üzere @supports ((animation-timeline: view()) and (animation-range: entry)) kullanın.
    • Yalnızca kısmi desteği olan tarayıcıları filtrelemek için (animation-range: entry) kontrolü buraya DAHİL EDİLMELİDİR.
    • Yedek strateji için scroll-timeline-polyfill paketini KULLANMAYIN. Bu paketin özellikleri tamamlanmamıştır ve bilinen birçok sorunu vardır.
    • Animasyonun yalnızca dekoratif olduğu düşünülüyorsa progresif geliştirmeyi tercih edin ve yedek SAĞLAMAYIN.
  • DO respect user preferences: Some users prefer to have less motion on the web. Bu kullanıcılar için animasyonlarınızı devre dışı bırakmak veya azaltmak üzere prefers-reduced-motion medya sorgusunu kullanın.
  • Yalnızca performanslı CSS özelliklerini canlandırmaya ÇALIŞIN: En akıcı animasyonlar için tarayıcının birleştirici iş parçacığı tarafından işlenebilen özellikleri (ör. transform ve opacity) canlandırın. width veya height gibi diğer özellikleri canlandırmak performans sorunlarına yol açabilir.
  • DO Doğru bildirim sırasını kullanın: animation kısayol özelliğini kullanırken, kısayolun zaman çizelgesini sıfırlamasını önlemek için animation-timeline ve animation-range özelliklerini sonra bildirin.

animation-range ayarını yapma konusunda:

  • DO Tüm katmanlara aynı başlangıç ofsetini verin (ör. entry 25%).
  • ÖNERİLİR: Tüm katmanlara, ofsetleri dağıtmak için sibling-count() ve sibling-index() kullanan farklı bir bitiş ofseti verin (ör. exit calc(100% / sibling-count() * sibling-index())).

Tarayıcı desteği ve yedek stratejiler

Bu nedenle, genellikle bir yedek strateji gerekir.

Kaydırmaya dayalı animasyonları desteklemeyen tarayıcılarda görsel efektleri yeniden oluşturmak için yedek kullanabilirsiniz. Yedekler genellikle bir kaydırma dinleyicisi (ScrollTimeline efektleri için) veya IntersectionObserver API (ViewTimeline efektleri için) ile oluşturulur.

Kaydırmaya dayalı animasyonlar için yerleşik desteğe sahip tarayıcılarda DAİMA yerel CSS uygulamasını kullanın. Bu uygulamalar daha yüksek performans sağlar.

Yedek yaklaşımı kullanılarak her efektin yeniden oluşturulamayacağını unutmayın.

Özellikle bu kullanım alanında, aşağıdaki komut dosyası, kaydırmaya dayalı animasyonları desteklemeyen tarayıcılar için yedek seçeneği uygular. .wrapper öğesinin görünürlüğünü izlemek için IntersectionObserver kullanır ve katmanların transform özelliğini kaydırma konumuna göre günceller.

// Fallback for browsers that don't support scroll-driven animations
if (!CSS.supports('(animation-timeline: view()) and (animation-range: entry)')) {
  const wrapper = document.querySelector('.wrapper');
  const layers = document.querySelectorAll('.layer');

  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        window.addEventListener('scroll', onScroll);
      } else {
        window.removeEventListener('scroll', onScroll);
      }
    });
  }, { threshold: 0 });

  observer.observe(wrapper);

  function onScroll() {
    const scrollY = window.scrollY;
    const wrapperRect = wrapper.getBoundingClientRect();
    const wrapperTop = wrapperRect.top + scrollY;
    const wrapperHeight = wrapperRect.height;
    const windowHeight = window.innerHeight;

    if (scrollY >= wrapperTop - windowHeight && scrollY <= wrapperTop + wrapperHeight) {
      const scrollPercent = (scrollY - (wrapperTop - windowHeight)) / (wrapperHeight + windowHeight);
      
      layers.forEach((layer, index) => {
        // This matches the effect as defined in the CSS example above.
        // Customize this further if needed.
        const initialTranslateY = 100 * index;
        const translateY = initialTranslateY * (1 - scrollPercent);
        layer.style.transform = `translateY(${translateY}px)`;
      });
    }
  }

  // Trigger onScroll once to set initial positions
  onScroll();
}