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

Video kullanmayı tercih ederseniz bu gönderinin YouTube versiyonunu kullanabilirsiniz:

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ç, aşamalı olarak animasyon oluşturmaktır. Metin, animasyon en üst kısma yerleştirilmiş şekilde varsayılan olarak okunabilmelidir. 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 ilişkin genel bir bakış yer almaktadır:

  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ş

Bir kullanıcı daha az hareketi tercih ederse, HTML belgesini olduğu gibi bırakırız ve animasyon yapmayız. 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ı hareket edebilir, metin birden fazla <span> öğesine bölünmüş

Hareket koşullarını hazırlama

Kullanılabilir @media (prefers-reduced-motion: reduce) medya sorgusu, bu projede CSS ve JavaScript'ten 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'i kullandım:

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

JS koşullarını hazırlama

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ğeri sayesinde efekti koşullu olarak yükseltmeye hazırız. Böylece, dizeleri öğelere dönüştürmek için JavaScript'i anlatacağım bir sonraki bölüme geçiyoruz.

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 bir harfi canlandırmak istiyorsak, her harfin bir öğe olması gerekir. Her bir kelimeyi canlandırmak istiyorsanız 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'teki 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ölmeye benzer şekilde, bu işlev de bir dize alır ve bir dizideki her kelimeyi 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 mülkün ayarlandığını belirtmek önemlidir. 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 etkideki en dikkat çeken şey şaşırtıcıdır. --index öğesini, kademeli bir görünüm için animasyonları dengelemenin bir yolu olarak kullanabileceğiz.

Yardımcı Araçlar sonucu

splitting.js modülü tamamlandığında:

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 devralmayı ve öğelerin / kutuların animasyonunu başlatır.

Öğeleri bulma

İstediğim 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 harf veya kelime kutuları oluşturmak için JavaScript'ten 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'de öğeleri 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;
}

Yalnızca boşluk olan aralıkların düzen motoru tarafından daraltılmaması için boşluk stili ö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. Sonra, kullanıcı fareyle bir harfinin ü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);
      }
    }
  }
}

Harfleri bölme animasyon ö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

Bu örneklerde, Flexbox 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 fareyle üzerine gelme özelliğini tekrar 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. Bir 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