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

Bölünmüş harf ve kelime animasyonlarının nasıl oluşturulacağına dair temel bir genel bakış.

Bu yayında, web'de bölünmüş metin animasyonlarını ve etkileşimlerini minimum düzeyde, erişilebilir ve tarayıcılar arasında çalışan şekilde çözme yolları hakkındaki düşüncelerimi paylaşmak istiyorum. Demoyu deneyin.

Demo

Videoyu tercih ediyorsanız bu yayının YouTube sürümünü burada bulabilirsiniz:

Genel Bakış

Bölünmüş metin animasyonları şaşırtıcı olabilir. Bu yayında, animasyon potansiyelinin sadece küçük bir kısmını ele alacağız. Ancak bu, üzerine inşa edebileceğiniz bir temel sağlar. Amaç, animasyonu kademeli olarak gerçekleştirmektir. Metin, varsayılan olarak okunaklı olmalı ve animasyon üzerine yerleştirilmelidir. Bölünmüş metin hareket efektleri abartılı ve potansiyel olarak rahatsız edici olabilir. Bu nedenle, yalnızca HTML'yi değiştiririz veya kullanıcı hareketi kabul ediyorsa hareket stilleri uygularız.

Aşağıda, iş akışına ve sonuçlara genel bir bakış verilmiştir:

  1. CSS ve JS için azaltılmış hareket koşullu değişkenleri hazırlayın.
  2. JavaScript'te bölünmüş metin yardımcı programlarını hazırlayın.
  3. Sayfa yüklenirken koşullu ifadeleri ve yardımcı programları düzenleyin.
  4. Harfler ve kelimeler için CSS geçişleri ve animasyonları yazın (en heyecan verici kısım budur).

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

Öğeler panelinin açık olduğu, azaltılmış hareketin "azalt" olarak ayarlandığı ve h1 etiketinin bölünmemiş olarak gösterildiği Chrome Geliştirici Araçları ekran görüntüsü
Kullanıcı azaltılmış hareketi tercih ediyor: metin okunaklı / bölünmemiş

Kullanıcı hareketin azaltılmasını tercih ederse HTML dokümanı olduğu gibi bırakılır ve animasyon uygulanmaz. Hareket iyiyse videoyu parçalara ayırırız. JavaScript metni harfe göre böldükten sonra HTML'nin önizlemesi aşağıda verilmiştir.

Öğeler panelinin açık olduğu, azaltılmış hareketin "azalt" olarak ayarlandığı ve h1 etiketinin bölünmemiş olarak gösterildiği Chrome Geliştirici Araçları ekran görüntüsü
Kullanıcı hareketi kabul ediyor; metin birden fazla <span> öğesine bölünmüş

Hareket koşullularını hazırlama

Bu projede, kolayca kullanılabilen @media (prefers-reduced-motion: reduce) medya sorgusu CSS ve JavaScript'den kullanılacaktır. Bu medya sorgusu, metni bölmeye 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'de yapılan değişiklikleri engellemek için kullanılır.

CSS koşullu ifadesi hazırlama

Bir medya sorgusu boole değerini bir değişkende saklayabileceğim Medya Sorguları 5. Seviye söz dizimini etkinleştirmek için PostCSS'yi kullandım:

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

JS koşullu ifadesi hazırlanıyor

JavaScript'te tarayıcı, medya sorgularını kontrol etmenin bir yolunu sağlar. Medya sorgusu kontrolünden elde edilen Boole sonucunu ayıklayıp yeniden adlandırmak için yapı bozma özelliğini kullandım:

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

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

if (motionOK) {
  // document split manipulations
}

İç İçe Sarma Taslağı 1'den @nest söz dizimini etkinleştirmek için PostCSS'yi kullanarak aynı değeri kontrol edebilirim. Bu sayede, animasyonla ilgili tüm mantığı ve ana öğe ile alt öğeler için stil şartlarını tek bir yerde saklayabilirim:

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

PostCSS özel mülkü ve JavaScript doğru/yanlış değeriyle efekti koşullu olarak yükseltmeye hazırız. Bu bizi, dizelerin öğelere dönüştürülmesi için JavaScript'i ayrıntılı olarak incelediğim bir sonraki bölüme götürür.

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 animasyonlu hale getirmek istiyorsak her harfin bir öğe olması gerekir. Her bir kelimeyi animasyonlu hale getirmek istiyorsak her kelimenin bir öğe olması gerekir.

  1. Dizeleri öğelere bölme için JavaScript yardımcı işlevleri oluşturma
  2. Bu yardımcı programların kullanımını koordine etme

Harfleri bölme yardımcı programı işlevi

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

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

ES6'taki yayma söz dizimi, bu işlemin hızlı bir şekilde yapılmasına yardımcı oldu.

Kelime bölme yardımcı programı işlevi

Harfleri bölme işlemine benzer şekilde bu işlev bir dize alır ve her kelimeyi bir diziyle döndürür.

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

JavaScript dizelerindeki split() yöntemi, hangi karakterlerin dilimleneceğini belirtmemize olanak tanır. Kelimeler arasında boşluk olduğunu belirten bir boşluk bıraktım.

Kutuları hizmet işlevi haline getirme

Bu efekt için her harf için kutu gerekir. Bu işlevlerde map() işlevinin span() işleviyle çağrıldığını görüyoruz. 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ı unutmayın. Harf animasyonları için kutulara sahip olmak harika bir özellik olsa da CSS'de kullanılacak bir dizinin olması, büyük etkisi olan küçük bir eklemedir. Bu büyük etkide en dikkat çeken şey staggering. --index değerini, kademeli bir görünüm için animasyonların kaydırılmasını sağlamak amacıyla kullanabiliriz.

Yardımcı Araçlar 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 kullanmalısınız.

Bölünmüş düzenleme

Bölme yardımcı programları kullanıma hazır olduğunda, tüm bunları bir araya getirmek şu avantajları sağlar:

  1. Bölünecek öğeleri bulma
  2. Bunları bölme ve metni HTML ile değiştirme

Ardından CSS devreye girer ve öğeleri / kutuları hareketlendirir.

Öğeleri bulma

İstenilen animasyon ve metnin nasıl bölüneceği hakkındaki bilgileri depolamak için özellikleri ve değerleri kullanmayı tercih ettim. Bu açıklayıcı seçenekleri HTML'ye koymayı sevdim. split-by özelliği, öğeleri bulmak ve harfler veya kelimeler için kutular oluşturmak amacıyla JavaScript'de kullanılır. letter-animation veya word-animation özelliği, öğe alt öğelerini hedeflemek ve dönüştürme ile animasyon uygulamak için CSS'den kullanılır.

Aşağıda, bu iki özelliği gösteren bir HTML örneği verilmiştir:

<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ığı için 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 bölünmüş hedeflerin her biri 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)
  }
})

Düzenleme 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 dilinde okunabilir:

  1. Bazı yardımcı 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 hiçbir işlem yapmayın.
  3. Bölünmek istenen her öğe için.
    1. Kullanıcıların 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 dokümanı bölme işlemi, CSS veya JavaScript ile birçok olası animasyon ve efektin kilidini açtı. Bu makalenin alt kısmında, bölünme potansiyelinize ilham verecek birkaç bağlantı verilmiştir.

Bu fırsatı değerlendirin. 4 CSS destekli animasyon ve geçiş paylaşacağım. 🤓

Bölünmüş harfler

Bölünmüş harf efektleri için temel olarak aşağıdaki CSS'yi kullandım. Tüm geçişleri ve animasyonları hareketli medya sorgusunun arkasına koydum ve ardından her yeni alt harf span'ye bir görüntüleme özelliği ve boşluklarla ne yapılacağıyla ilgili bir stil verdim:

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

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

Geçiş için bölünmüş harf örneği

Bu örnekte, bölünmüş metin efekti için CSS geçişleri kullanılmaktadır. Geçişlerde, motorun animasyon oluşturması için aralarında geçiş yapacağı durumlara ihtiyacımız var. Ben üç durum seçtim: fareyle üzerine gelme yok, cümlede fareyle üzerine gelme, harfin üzerinde fareyle üzerine gelme.

Kullanıcı cümlenin (yani kapsayıcının) üzerine geldiğinde, tüm alt öğeleri kullanıcı onları uzaklaştırıyormuş gibi küçültüyorum. Ardından, kullanıcı fareyle bir harfin üzerine geldiğinde harfi ö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 uygulama örneği

Bu örnekte, her harfi sonsuz olarak animasyonlu hale getirmek için önceden tanımlanmış bir @keyframe animasyonu kullanılmakta ve satır içi özel mülk dizini, kademeli bir efekt oluşturmak için kullanı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 benim için bir kapsayıcı türü olarak çalıştı ve ch birimini sağlıklı bir boşluk uzunluğu olarak iyi bir şekilde kullandı.

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
Sözcük arasındaki boşluğu gösteren Flexbox devtools

Geçiş için kelimeleri bölme örneği

Bu geçiş örneğinde yine fareyle üzerine gelme özelliğini kullanıyorum. Etki, fareyle üzerine gelinene kadar içeriği başlangıçta gizlediğinden, etkileşimin 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 uygulama ö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ç

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

Yaklaşımlarımızı çeşitlendirelim ve web'de uygulama geliştirmenin tüm yollarını öğrenelim. Codepen oluşturun veya kendi demonuzu barındırın, bana tweet atın. Ardından, demoyu aşağıdaki Topluluk remiksleri bölümüne ekleyeceğim.

Kaynak

Daha fazla demo ve ilham kaynağı

Topluluk remiksleri