7 puan yazan GN⁺ 2024-05-06 | 1 yorum | WhatsApp'ta paylaş
  • CSS matematik fonksiyonlarına verilen desteğin artmasıyla, keyframes yerine zaman tick değişkeni --t ile animasyon durumunu hesaplama yaklaşımı yeniden pratik hale geldi
  • @property --t ile 86400000ms uzunluğunda bir root animasyonu birleştirildiğinde, milisaniye düzeyinde artan bir zaman elde edilebilir ve bu zaman rotate, translate gibi özellik hesaplamalarına bağlanabilir
  • step() kullanılırsa 8fps, 24fps, 60fps gibi frame rate sınırlandırılabilir; tarayıcı optimizasyonları sayesinde 60fps'in üzerindeki güncelleme sıklığı da genelde sorun yaratmaz
  • min(), mod(), sin(), pow(), round() ile zaman değeri sınırlandırılıp tekrarlanabilir, salınım ve easing uygulanabilir; böylece özel easing ve konum hareketleri keyframes olmadan ifade edilebilir
  • css-doodle, @t, @T, @TS gibi kısaltmalar sunarak matematiksel ifadelerin yoğun olduğu demoların daha kısa yazılmasını ve daha hızlı denenmesini sağlar

Zaman tick'ini CSS değişkenine dönüştürmek

  • Geçmişteki yaklaşım, CSS içinde karmaşık matematik hesaplamaları yapmak zor olduğundan sınırlı bir kullanım alanına sahipti
  • Günümüz CSS'i, mod(), round(), trigonometrik fonksiyonlar gibi gerekli matematik fonksiyonlarını desteklediği için zaman tabanlı animasyonlar yeniden denenebilir hale geldi
  • Demoyu görmek için Experimental feature flags etkinleştirilmesi gerekebilir

Temel çalışma şekli

  • CSS, JavaScript gibi doğrudan zamanlayıcı başlatamaz; ancak CSS Houdini API ile özel değişken --t tanımlanarak milisaniye düzeyinde zaman takip edilebilir
  • Temel örnekte @property --t, <integer> olarak bildirilir; @keyframes tick içinde 0 ile 86400000 arasında değiştirilir ve :root üzerine 86400000ms linear infinite animasyonu uygulanır
  • --t, her 1 milisaniyede 1 artar ve 1 saniyede 1000 olur
  • counter() kullanılarak mevcut --t değeri ekranda gösterilebilir
  • rotate: calc(var(--t) * .001turn) gibi --t tabanlı hesaplamalar özelliklere bağlandığında, zaman aktıkça değer değişir ve animasyona dönüşür

Frame rate kontrolü

  • Akıcı animasyonlar için 60fps güncelleme sıklığı yeterlidir
  • Tarayıcıların render optimizasyonları sayesinde güncelleme sıklığı 60fps'in üzerine çıksa da genelde sorun olmaz
  • Gerekirse animation-timing-function içinde step() kullanılarak frame rate doğrudan sınırlandırılabilir
    • 8fps: step(calc(86400000/(1000/8)))
    • 24fps: step(calc(86400000/(1000/24)))
    • 60fps: step(calc(86400000/(1000/60)))

Sürekli artan zaman değerini animasyon değerine dönüştürmek

  • --t değeri tek yönde sürekli artar
  • angle değeri 360deg değerini aşsa da sorun olmaz; ancak tüm CSS özellikleri değeri döngüsel olarak işlemez
  • translate: calc(var(--t) * .001px) kutuyu sürekli sağa taşır
  • min() ile durdurmak

    • Belirli bir ofsete ulaşır ulaşmaz durdurmak için min() kullanılabilir
    • translate: min(270px, calc(var(--t) * .5px)) hareket değerini 270px ile sınırlar
    • Süreyi tam kontrol etmek için --t değerinin kendisi sınırlandırılabilir
    • translate: calc(min(3000, var(--t)) * (270px / 3000))
    • Bu hesaplama, 3 saniyede 270px hareket sağlar
  • mod() ile tekrarlamak

    • Kutu sağa gittikten sonra başa dönsün isteniyorsa mod() kullanılır
    • translate: calc(mod(var(--t)/4, 270) * 1px) ile ofset tekrarlanabilir
  • sin() ile ileri-geri hareket oluşturmak

    • İleri-geri hareket sin() ile ifade edilebilir
    • translate: calc(sin(mod(var(--t)/135, 270)) * 135px) gibi, zaman değeri trigonometrik fonksiyona bağlanır

Özel easing fonksiyonları

  • CSS matematik fonksiyonları ve --t değişkeniyle, cubic-bezier() ile elde edilmesi zor olabilecek özel easing fonksiyonları oluşturulabilir
  • Önce --t, 0 ile 1 arasındaki bir değere sınırlandırılır
    • --t01: calc(min(1000, var(--t)) / 1000) 1 saniye boyunca 0'dan 1'e değişir
  • ease-out-cubic

    • ease-out-cubic, 1 - pow(1 - t, 3) hesabından oluşur
    • --ease-out-cubic değeri oluşturulup translate: calc(var(--ease-out-cubic) * 270px) içine bağlanır
  • ease-out-elastic

    • ease-out-elastic, pow(2, -10t) * sin((10t - .75) * 2/3 * PI) + 1 hesabından oluşur
    • Aynı şekilde --ease-out-elastic değeri oluşturulup translate ile çarpılarak 270px harekete uygulanır

css-doodle ile formülleri kısaltmak

  • Formüller karmaşıklaştıkça var() ve calc(), kodun okunmasını zorlaştırır
  • css-doodle son sürümü, --t ifadesini temsil eden @t fonksiyonu ekledi; bu sayede parametrelerin içinde basit matematik ifadeleri doğrudan yazılabilir
  • rotate ifadesi şu şekilde kısalır
    • Önceki: rotate: calc(mod(var(--t) / 1000, 10) * 5deg)
    • css-doodle: rotate: @t(/1000, %10, *5deg)
  • keyframes yazmadan da kısa kodla matematik tabanlı animasyonlar oluşturulabilir
  • Parametreler değiştirilerek yeni sonuçlar hızlıca denenebilir
  • @T ve @TS

    • @t dışında büyük harfli @T, günün başlangıcından itibaren geçen time tick'leri ifade eder
    • @TS, @t(/1000) için shorthand'tir ve zamanı saniye cinsinden takip eder
    • Saat örneği CodePen üzerinde görülebilir
    • Saniye kolu: rotate: @TS(*6, %360deg)
    • Dakika kolu: rotate: @TS(/60, *6, %360deg)
    • Saat kolu: rotate: @TS(/60, /12, *6, %360deg)
  • round() ile saniye kolunda sıçramalı hareket

    • Saniye kolunun sıçrayarak hareket etmesi için round() kullanılabilir
    • round() fonksiyonunun üçüncü parametresi, yuvarlama aralığını belirler
    • Saatte bir adım 360 / 60 = 6deg olduğundan rotate: round(down, @TS(*6, %360deg), 6deg) kullanılır

Demolar ve uygulama noktaları

  • Renk ve konumu birlikte animasyonlayan örnek CodePen üzerinde görülebilir
  • Metin rengi animasyonu örneği CodePen bağlantısında yer alır
  • keyframes daha sezgisel görünebilir; ancak matematik hesaplarının ve giriş değişkenlerinin fazla olduğu demo sahnelerinde zamanı değişken olarak kullanma yaklaşımı, daha çeşitli sonuçlar üretmeyi kolaylaştırır
  • 17 Mayıs 2024 güncellemesiyle metin rengi animasyonu demosu eklendi

1 yorum

 
GN⁺ 2024-05-06
Hacker News yorumları
  • İlerlemeyi JS ile kontrol etmek istiyorsanız negatif animation-delay değerleri kullanmayı da öneririm.
    Örneğin animation-delay: -1500ms, animasyonu hemen başlatır ama 1,5 saniye noktasına atlar.
    Bu değeri JS ile kontrol ederseniz CSS animasyonlarını JS tarafında fiilen scrub edebilirsiniz; böylece tüm animasyonları oyun motoru tarzı compute-update-render tick döngüsüyle hizalayabilirsiniz.
    https://developer.mozilla.org/en-US/docs/Web/CSS/animation-d...

    • O zaman iOS tarzı kaydırma ofseti tabanlı animasyonlar da mümkün olmaz mı?
  • Basit bir easing fonksiyonunun ya da bir iki kanallı temel keyframe'in biraz ötesine geçince bu yaklaşımın sınırlarına hızla çarpıyorsunuz.
    Son birkaç yıldır Theatre.js kullanıyorum ve oldukça memnunum.
    Keyframe'leri ve Bézier eğrilerini bir zaman çizelgesinde düzenleyen bir stüdyo arayüzü ile bu keyframe'leri zaman çizelgesine göre enterpole eden bir runtime'dan oluşan bir kütüphane.
    Birden fazla animasyonu uyumlu hareket ettirmeniz gerekiyorsa denemeye değer.
    https://www.theatrejs.com/

  • Çok etkileyici; ama bir yandan da sanki onlarca yıl önce Flash ile yapılabilen şeye ancak şimdi yaklaşıyormuşuz gibi hissettiriyor.
    Flash'ın geri dönmesini istemiyorum ama CSS animasyonları oluşturmak için daha kullanıcı dostu araçlar olsa iyi olurdu.

  • CSS artık mod(), round(), trigonometrik fonksiyonlar gibi matematik fonksiyonları için yeterli desteğe sahip.
    CSS, JavaScript gibi zamanlayıcı başlatamaz; ancak bugünlerde CSS Houdini API ile milisaniye düzeyinde zamanı izleyen özel değişkenler tanımlanabiliyor.
    Peki JavaScript varken tüm bunlara neden ihtiyaç var?
    Çizim katmanı çizim temel öğelerine odaklanmamalı mı? Neden oraya daha fazla üst katman işlevi ekleniyor?

    • CSS animasyonlarının donanım hızlandırmalı olduğunu unutmamak gerekir.
      Animasyon için JS'te trigonometrik fonksiyonlar çalıştırıyorsanız, muhtemelen her milisaniyede UI'ı güncellemek için DOM'u da manipüle ediyorsunuzdur; bunun maliyeti çok yüksek.
    • CSS bir çizim katmanı değildir.
      Çizimi, HTML ile oluşturulan veya JS ile güncellenen DOM yönlendirir.
      CSS ise DOM üzerinde ne yapılacağına dair parametreleri painting engine'e iletip varsayılanları ezerek bunun üstüne eklenir.
      DOM blokları ekleyebilir, karmaşık algoritmalarla konumlandırma davranışını değiştirebilir; margin bile aslında o kadar basit değildir.
      Harika bir çözüm olduğunu düşünmüyorum; ama DOM render edilirken renkleri değiştirmek için HTML kodunu güncellemek zorunda kalınmasın diye başlamış gibi görünüyor.
      Muhtemelen HTML kodunu güncellemek hiç keyifli olmadığı içindi; ama bence baştan yanlış problemi çözmüşler.
      O dönemin “webmaster” dönemi olması da var tabii.
    • Aklıma gelen bir avantaj uygulama mantığı ile sunumun ayrılması.
      Fetch mantığının içinde loader DOM öğesinin geometrik ilerleme mantığı, örneğin dönüş açısını hesaplama ve inline style uygulama gibi şeyler karışmadığında okumak daha kolay oluyor.
      Günümüzde çikolata ile fıstık ezmesini ayrı tutmanın pek popüler olmadığını biliyorum; ama bu tür teamüller, kodun nerede olacağını ve nereye konması gerektiğini kestirmeyi kolaylaştıran sınırlar çizer.
    • JS temelde tek iş parçacıklıdır; elbette worker'lar var, ama UI'ı istikrarlı bir kare hızında tutmak için işi başka yere devredebildiğiniz her şey iyidir.
    • CSS'e WebSocket gelene kadar bekleyelim!
      Şaka bir yana, CSS deklaratiftir ve JS ile CSS'in çözebildiği problemlerin çakışması doğal bence.
      CSS'e son dönemde eklenen yeni özellikler gerçekten heyecan verici ve yorumlanan kodun azalması iyi bir şey.
  • Yazıda kullanılan özel CSS @property harika ve tarayıcı desteği de %88 [1].
    Ancak yedek initial-value ayarlanırken tarayıcılar arasındaki davranış farklarına dikkat etmek gerekiyor.
    Chrome, CSS değişkeni tanımlı değilse veya geçersiz bir değere ayarlanmışsa initial-value kullanıyor.
    Firefox ise initial-value değerini yalnızca değişken tanımlı olmadığında kullanıyor.
    Çoğu projede sorun olmaz; ama yakın tarihli bir projede tarayıcı implementasyon farklarını eşitlemek için Firefox'ta varsayılan değeri JavaScript ile ayarlamak zorunda kaldık.
    [1] https://caniuse.com/?search=%40property

    • Demoyu Firefox'ta denemek istiyorsanız about:config sayfasına gidip layout.css.properties-and-values.enabled seçeneğini açabilirsiniz.
    • Gerektiğinde JavaScript'te özellik değerini ayarlama yöntemi şöyle:
      :root{} içinde tanımlı özellik değerini almak:
      getComputedStyle(document.documentElement).getPropertyValue("--some-custom-css-var");
      Özellik değerini ayarlamak:
      document.documentElement.style.setProperty("--some-custom-css-var", "some value");
      CSS @property desteği henüz açık olmayan tarayıcılarda polyfill gibi kullanılabilir.
  • Güncel Android Chrome'da ilginç biçimde Experimental feature flags olmadan da mod() öncesi demolar çalışıyor; ama mod() ve sin() hiçbir şey yapmıyor.

  • Bir süredir bunu istememin nedeni box-breathing gibi bir şey yapmak istememdi.
    Nefes alma-durma-verme-durma aşamalarını bedene ve istenen etkiye göre tamamen ayarlayabilmek istiyordum.
    Bu sayfadaki gibi bir yöntem olmadan bunu JS'siz ya da saçma derecede karmaşık hale getirmeden uygulamanın bir yolunu bulamamıştım.
    Vay canına, stil sayfası! Bu sayfadaki demolar gerçekten yaratıcı.

    • Bu sadece CSS'in animasyon eğrisi değil mi?
  • Yazı gerçekten çok iyi yazılmış.
    Chrome, CSS mod() desteğini henüz kararlı sürümde yayınlamadığı için Chrome önizleme sürümü kullanmıyorsanız sayfadaki örneklerin çoğu animasyonlu çalışmıyor.
    Tick kontrolüne ihtiyacınız yoksa ve tüm tarayıcılarda yalnızca akıcı CSS animasyonları istiyorsanız FLIP tekniği kullanışlıdır.
    https://medium.com/outsystems-experts/flip-your-60-fps-anima...

  • Sayfanın altındaki animasyon çok etkileyici.
    Tek tek öğelerden ziyade bir video gibi görünüyor.

  • https://RTCode.io yenileme olmadan, durum sıfırlanmadan çalışan gerçek zamanlı bir web playground olduğu için CSS animasyonları veya transition'larla uğraşanlara çok yardımcı olabilir.
    Demo https://RTCode.io üzerinde.
    Bir Pen'i içe aktarmak için URL'deki host adını xcodepen.io olarak değiştirmeniz yeterli.

    • RTCode'u deneyen var mı? İlginç görünüyor; ama ana sayfaya bakınca gerçek zamanlı destekli bir CodePen alternatifi mi, yoksa production'da da kullanılabilir mi biraz kafa karıştırıcı.