Bölünmüş metin animasyonları oluşturma

Bölünmüş harf ve kelime animasyonları oluşturma hakkında temel bilgiler.

Bu yayında, web için bölünmüş metin animasyonlarını ve etkileşimlerini çözmenin en az çaba gerektiren, erişilebilir ve tarayıcılar arasında çalışan yolları hakkındaki düşüncelerimi paylaşmak istiyorum. Demoyu deneyin.

Demo

Video tercih ediyorsanız bu yayının YouTube versiyonunu aşağıda bulabilirsiniz:

Genel Bakış

Bölünmüş metin animasyonları harika olabilir. Bu yayında animasyon potansiyelinin sadece küçük bir kısmına değineceğiz ancak bu bilgiler, üzerine inşa edebileceğiniz bir temel oluşturacaktır. Hedef, animasyonu kademeli olarak oluşturmaktır. Metin, varsayılan olarak okunabilir olmalı ve animasyon bunun üzerine kurulmalıdır. Metin hareket efektlerini bölme işlemi, abartılı ve rahatsız edici olabilir. Bu nedenle yalnızca HTML'yi değiştiririz veya kullanıcı hareketten rahatsız olmuyorsa hareket stilleri uygularız.

İş akışına ve sonuçlara genel bir bakış:

  1. CSS ve JS için hazırlanmış hareket koşullu değişkenlerini azaltın.
  2. JavaScript'te hazırlama, metin yardımcı programlarını bölme.
  3. Sayfa yüklemesinde koşulları ve yardımcı programları düzenleyin.
  4. Harfler ve kelimeler için CSS geçişleri ve animasyonları yazın (en eğlenceli kısım!).

Hedeflediğimiz koşullu sonuçların önizlemesini aşağıda görebilirsiniz:

Öğeler paneli açıkken ve hareket azaltma özelliği "azalt" olarak ayarlanmışken Chrome Geliştirici Araçları'nın ekran görüntüsü. h1 bölünmemiş olarak gösteriliyor.
Kullanıcı, azaltılmış hareketi tercih ediyor: Metin okunabilir / bölünmemiş

Kullanıcı, hareketi azaltmayı tercih ediyorsa HTML belgesini olduğu gibi bırakırız ve animasyon eklemeyiz. Hareket iyiyse videoyu parçalara ayırırız. JavaScript, metni harflere ayırdıktan sonraki HTML'nin önizlemesini aşağıda görebilirsiniz.

Öğeler paneli açıkken ve hareket azaltma özelliği "azalt" olarak ayarlanmışken Chrome Geliştirici Araçları'nın ekran görüntüsü. h1 bölünmemiş olarak gösteriliyor.
Kullanıcı, hareketten rahatsız olmuyor. Metin birden fazla <span> öğesine bölünmüş.

Hareket koşullarını hazırlama

Bu projede CSS ve JavaScript'ten kolayca kullanılabilen @media (prefers-reduced-motion: reduce) medya sorgusu kullanılacak. Bu medya sorgusu, metni bölüp bölmeyeceğimize karar verirken kullandığımız birincil koşuldur. CSS medya sorgusu, geçişleri ve animasyonları engellemek için kullanılırken JavaScript medya sorgusu, HTML manipülasyonunu engellemek için kullanılır.

CSS koşulunu hazırlama

Media Queries Level 5'in söz dizimini etkinleştirmek için PostCSS'yi kullandım. Burada bir medya sorgusu boole'unu değişkende saklayabilirim:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

JS koşulunu hazırlama

JavaScript'te tarayıcı, medya sorgularını kontrol etmenin bir yolunu sunar. Medya sorgusu kontrolünden Boole sonucunu ayıklamak ve yeniden adlandırmak için yapı bozma kullandım:

const {matches:motionOK} = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
)

Ardından motionOK için test yapabilir ve yalnızca kullanıcı hareket azaltma isteğinde bulunmadıysa dokümanı değiştirebilirim.

if (motionOK) {
  // document split manipulations
}

PostCSS'yi kullanarak Nesting Draft 1'deki @nest söz dizimini etkinleştirerek aynı değeri kontrol edebilirim. Bu sayede, animasyonla ilgili tüm mantığı ve stil gereksinimlerini üst öğe ve alt öğeler için tek bir yerde saklayabilirim:

letter-animation {
  @media (--motionOK) {
    /* animation styles */
  }
}

PostCSS özel özelliği ve bir JavaScript boole'u ile efekti koşullu olarak yükseltmeye hazırız. Bu da bizi, dizeleri öğelere dönüştürmek için JavaScript'i ayrıntılı olarak açıkladığım bir sonraki bölüme götürüyor.

Metni Bölme

Metin harfleri, kelimeleri, satırları vb. CSS veya JS ile tek tek animasyonlu hale getirilemez. Bu efekti elde etmek için kutulara ihtiyacımız var. Her harfi ayrı ayrı canlandırmak istiyorsak her harf bir öğe olmalıdır. Her kelimeyi canlandırmak istiyorsak her kelime bir öğe olmalıdır.

  1. Dizeleri öğelere bölmek için JavaScript yardımcı işlevleri oluşturma
  2. Bu yardımcı programların kullanımını düzenleme

Harfleri bölme yardımcı işlevi

Başlamak için eğlenceli bir yer, bir dize alan ve her harfi bir dizide döndüren bir işlevdir.

export const byLetter = text =>
  [...text].map(span)

ES6'daki spread söz dizimi, bu görevin hızlı bir şekilde tamamlanmasına gerçekten yardımcı oldu.

Kelimeleri bölme yardımcı işlevi

Bu işlev, harfleri bölmeye benzer şekilde bir dize alır ve her kelimeyi bir dizide döndürür.

export const byWord = text =>
  text.split(' ').map(span)

JavaScript dizelerindeki split() yöntemi, hangi karakterlerin kesileceğini belirtmemize olanak tanır. Kelimeler arasında boşluk olduğunu belirten boş bir alan geçtim.

Kutuları hizmet işlevi haline getirme

Efekt için her harf için kutu gerekir ve bu işlevlerde map() öğesinin span() işleviyle çağrıldığını görürüz. span() işlevi aşağıda verilmiştir.

const span = (text, index) => {
  const node = document.createElement('span')

  node.textContent = text
  node.style.setProperty('--index', index)

  return node
}

Dizi konumuyla --index adlı bir özel özelliğin ayarlandığını unutmamak önemlidir. Harf animasyonları için kutuların olması harika bir özellik. Ancak CSS'de kullanılacak bir dizinin olması, küçük gibi görünse de büyük bir etki yaratıyor. Bu büyük etki içinde en dikkat çekici olanı şaşırtıcı. --index, animasyonları kademeli bir görünüm için kaydırmak amacıyla kullanılabilir.

Yardımcı programlar sonucu

Tamamlanan splitting.js modülü:

const span = (text, index) => {
  const node = document.createElement('span')

  node.textContent = text
  node.style.setProperty('--index', index)

  return node
}

export const byLetter = text =>
  [...text].map(span)

export const byWord = text =>
  text.split(' ').map(span)

Ardından, bu byLetter() ve byWord() işlevlerini içe aktarıp kullanma gelir.

Bölünmüş düzenleme

Bölme yardımcı programları kullanıma hazır olduğunda, tüm bunları bir araya getirmek için:

  1. Hangi öğelerin bölüneceğini bulma
  2. Metinleri bölme ve HTML ile değiştirme

Ardından CSS devreye girer ve öğelere / kutulara animasyon uygular.

Öğeleri bulma

İstediğim animasyon ve metnin nasıl bölüneceğiyle ilgili bilgileri depolamak için özellikleri ve değerleri kullanmayı tercih ettim. Bu bildirimsel seçenekleri HTML'ye eklemeyi beğendim. split-by özelliği, JavaScript'ten kullanılarak öğeler bulunur ve harfler veya kelimeler için kutular oluşturulur. Özellik letter-animation veya word-animation, CSS'den öğe alt öğelerini hedeflemek ve dönüştürmeler ile animasyonlar uygulamak için kullanılır.

İki özelliği gösteren bir HTML örneğini aşağıda bulabilirsiniz:

<h1 split-by="letter" letter-animation="breath">animated letters</h1>
<h1 split-by="word" word-animation="trampoline">hover the words</h1>

JavaScript'ten öğe bulma

Metinlerinin bölünmesini isteyen öğelerin listesini toplamak için özellik varlığıyla ilgili CSS seçici söz dizimini kullandım:

const splitTargets = document.querySelectorAll('[split-by]')

CSS'den öğe bulma

Ayrıca, tüm harf animasyonlarına aynı temel stilleri vermek için CSS'de özellik varlığı seçicisini kullandım. Daha sonra, bir efekt elde etmek için daha spesifik stiller eklemek üzere özellik değerini kullanacağız.

letter-animation {
  @media (--motionOK) {
    /* animation styles */
  }
}

Metni yerinde bölme

JavaScript'te bulduğumuz her bölünmüş hedef için metinlerini özelliğin değerine göre böler ve her dizeyi bir <span> ile eşleriz. Ardından, öğenin metnini oluşturduğumuz kutularla değiştirebiliriz:

splitTargets.forEach(node => {
  const type = node.getAttribute('split-by')
  let nodes = null

  if (type === 'letter') {
    nodes = byLetter(node.innerText)
  }
  else if (type === 'word') {
    nodes = byWord(node.innerText)
  }

  if (nodes) {
    node.firstChild.replaceWith(...nodes)
  }
})

Orkestrasyon sonucu

index.js tamamlandı:

import {byLetter, byWord} from './splitting.js'

const {matches:motionOK} = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
)

if (motionOK) {
  const splitTargets = document.querySelectorAll('[split-by]')

  splitTargets.forEach(node => {
    const type = node.getAttribute('split-by')
    let nodes = null

    if (type === 'letter')
      nodes = byLetter(node.innerText)
    else if (type === 'word')
      nodes = byWord(node.innerText)

    if (nodes)
      node.firstChild.replaceWith(...nodes)
  })
}

JavaScript aşağıdaki İngilizce metinde okunabilir:

  1. Bazı yardımcı program işlevlerini içe aktarın.
  2. Bu kullanıcı için hareketin uygun olup olmadığını kontrol edin. Uygun değilse herhangi bir işlem yapmayın.
  3. Bölünmek istenen her öğe için.
    1. Bunları, nasıl bölünmek istediklerine göre bölün.
    2. Metni öğelerle değiştirin.

Animasyonları ve geçişleri bölme

Yukarıdaki belge bölme işlemi, CSS veya JavaScript ile çok sayıda potansiyel animasyon ve efektin kilidini açtı. Bu makalenin en altında, bölme potansiyelinizi artırmanıza yardımcı olacak birkaç bağlantı bulabilirsiniz.

Bu araçla neler yapabileceğinizi gösterme zamanı! CSS ile çalışan 4 animasyon ve geçiş paylaşacağım. 🤓

Bölünmüş harfler

Bölünmüş harf efektlerinin temeli olarak aşağıdaki CSS'nin faydalı olduğunu gördüm. Tüm geçişleri ve animasyonları hareket medya sorgusunun arkasına yerleştirip her yeni alt harfe span bir görüntüleme özelliği ve boşluklarla ne yapılacağına dair bir stil veriyorum:

[letter-animation] > span {
  display: inline-block;
  white-space: break-spaces;
}

Yalnızca boşluk olan aralıkların düzen motoru tarafından daraltılmaması için boşluk stili önemlidir. Şimdi de durum bilgisi içeren eğlenceli konulara geçelim.

Geçişte bölünmüş harfler örneği

Bu örnekte, bölünmüş metin efektine CSS geçişleri uygulanmaktadır. Geçişlerde, motorun animasyon oluşturmak için kullanacağı durumlar gerekir. Ben üç durum seçtim: fareyle üzerine gelmeme, fareyle cümle üzerinde gezinme ve fareyle harf üzerinde gezinme.

Kullanıcı, kapsayıcı olarak da bilinen cümlenin üzerine geldiğinde, kullanıcı çocukları daha da itmiş gibi tüm çocukları küçültüyorum. Ardından, kullanıcı bir harterin üzerine geldiğinde onu öne çıkarıyorum.

@media (--motionOK) {
  [letter-animation="hover"] {
    &:hover > span {
      transform: scale(.75);
    }

    & > span {
      transition: transform .3s ease;
      cursor: pointer;

      &:hover {
        transform: scale(1.25);
      }
    }
  }
}

Bölünmüş harflere animasyon ekleme örneği

Bu örnekte, her harfi sonsuza kadar animasyonlu hale getirmek için önceden tanımlanmış bir @keyframe animasyonu kullanılmakta ve kademeli bir efekt oluşturmak için satır içi özel özellik dizininden yararlanılmaktadır.

@media (--motionOK) {
  [letter-animation="breath"] > span {
    animation:
      breath 1200ms ease
      calc(var(--index) * 100 * 1ms)
      infinite alternate;
  }
}

@keyframes breath {
  from {
    animation-timing-function: ease-out;
  }
  to {
    transform: translateY(-5px) scale(1.25);
    text-shadow: 0 0 25px var(--glow-color);
    animation-timing-function: ease-in-out;
  }
}

Kelimeleri bölme

Flexbox, bu örneklerde kapsayıcı türü olarak işe yaradı ve ch birimini sağlıklı bir boşluk uzunluğu olarak güzel bir şekilde kullandı.

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
Kelimeler arasındaki boşluğu gösteren Flexbox DevTools

Geçişte bölünmüş kelimeler örneği

Bu geçiş örneğinde tekrar fareyle üzerine gelme özelliğini kullanıyorum. Efekt, fareyle üzerine gelene kadar içeriği gizlediğinden etkileşim ve stillerin yalnızca cihazda fareyle üzerine gelme özelliği varsa uygulandığından emin oldum.

@media (hover) {
  [word-animation="hover"] {
    overflow: hidden;
    overflow: clip;

    & > span {
      transition: transform .3s ease;
      cursor: pointer;

      &:not(:hover) {
        transform: translateY(50%);
      }
    }
  }
}

Bölünmüş kelimelere animasyon ekleme örneği

Bu animasyon örneğinde, normal bir metin paragrafında kademeli sonsuz animasyon oluşturmak için tekrar CSS @keyframes kullanıyorum.

[word-animation="trampoline"] > span {
  display: inline-block;
  transform: translateY(100%);
  animation:
    trampoline 3s ease
    calc(var(--index) * 150 * 1ms)
    infinite alternate;
}

@keyframes trampoline {
  0% {
    transform: translateY(100%);
    animation-timing-function: ease-out;
  }
  50% {
    transform: translateY(0);
    animation-timing-function: ease-in;
  }
}

Sonuç

Benim nasıl yaptığımı öğrendiğinize göre, siz nasıl yapardınız? 🙂

Yaklaşımlarımızı çeşitlendirelim ve web'de içerik oluşturmanın tüm yollarını öğrenelim. Codepen oluşturun veya kendi demoyu barındırın, bunu içeren bir tweet ile bana ulaşın. Ben de demoyu aşağıdaki Topluluk remiksleri bölümüne ekleyeyim.

Kaynak

Daha fazla demo ve ilham kaynağı

Topluluk remiksleri