Membuat animasi teks terpisah

Ringkasan dasar tentang cara membuat animasi huruf dan kata terpisah.

Dalam posting ini saya ingin berbagi pemikiran tentang cara memecahkan animasi teks terpisah dan interaksi untuk web yang minimal, dapat diakses, dan berfungsi di seluruh browser. Coba demo.

Demo

Jika Anda lebih suka menonton video, berikut versi YouTube untuk postingan ini:

Ringkasan

Animasi teks terpisah bisa sangat menarik. Kita tidak akan membahas potensi animasi dalam postingan ini, tetapi hal ini memberikan fondasi untuk membangun pada saat yang sama. Tujuannya adalah untuk menganimasikan secara progresif. Teks harus dapat dibaca oleh secara default, dengan animasi yang dibuat di atasnya. Efek gerakan teks terpisah bisa terlalu berlebihan dan berpotensi mengganggu, jadi kita hanya akan memanipulasi HTML, atau menerapkan gaya gerakan jika pengguna setuju dengan gerakan.

Berikut ini ringkasan umum tentang alur kerja dan hasilnya:

  1. Siapkan pengurangan kondisi gerakan variabel untuk CSS dan JS.
  2. Prepare aplikasi teks terpisah di pada JavaScript.
  3. Orkestrasi kondisional dan utilitas di halaman memuat halaman.
  4. Menulis transisi dan animasi CSS untuk huruf dan kata (bagian rad!).

Berikut adalah pratinjau hasil kondisional yang akan kita dapatkan:

screenshot Chrome DevTools dengan panel Elemen terbuka dan gerakan dikurangi ke 'kurang' dan h1 ditampilkan tanpa dipisah
Pengguna menyukai gerakan yang dikurangi: teks dapat dibaca / tidak dipisah

Jika pengguna menginginkan pengurangan gerakan, kita biarkan dokumen HTML saja dan tidak animasi. Jika {i>motion <i}dapat dilakukan, kita akan lanjutkan dan memotongnya menjadi beberapa bagian. Berikut adalah pratinjau HTML setelah JavaScript membagi teks menurut huruf.

screenshot Chrome DevTools dengan panel Elemen terbuka dan gerakan dikurangi ke &#39;kurang&#39; dan h1 ditampilkan tanpa dipisah
Pengguna tidak keberatan dengan gerakan; teks dibagi menjadi beberapa <span> elemen

Menyiapkan kondisional gerakan

Kueri media @media (prefers-reduced-motion: reduce) yang tersedia dengan mudah akan digunakan dari CSS dan JavaScript di project ini. Kueri media ini adalah kondisional utama kita untuk memutuskan untuk memisahkan teks atau tidak. Kueri media CSS akan digunakan untuk menahan transisi dan animasi, sedangkan kueri media JavaScript akan digunakan untuk menahan manipulasi HTML.

Menyiapkan kondisional CSS

Saya menggunakan PostCSS untuk mengaktifkan sintaksis Kueri Media Level 5, tempat saya dapat menyimpan boolean kueri media ke dalam variabel:

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

Menyiapkan kondisional JS

Di JavaScript, {i>browser<i} menyediakan cara untuk memeriksa kueri media, saya menggunakan penguraian untuk mengekstrak dan mengganti nama hasil boolean dari pemeriksaan kueri media:

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

Kemudian saya dapat menguji motionOK, dan hanya mengubah dokumen jika pengguna belum melakukannya diminta untuk mengurangi gerakan.

if (motionOK) {
  // document split manipulations
}

Saya dapat memeriksa nilai yang sama menggunakan PostCSS untuk mengaktifkan sintaksis @nest dari Draf Bertingkat 1. Hal ini memungkinkan saya untuk menyimpan semua logika tentang animasi dan persyaratan gayanya untuk orang tua dan anak, di satu tempat:

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

Dengan properti khusus PostCSS dan boolean JavaScript, kita siap untuk mengupgrade efek secara bersyarat. Selanjutnya kita akan membahas bagian berikutnya, di mana saya memecah JavaScript untuk mengubah {i>string<i} menjadi elemen.

Memisahkan Teks

Huruf teks, kata, baris, dll. tidak dapat dianimasikan satu per satu dengan CSS atau JS. Untuk mencapai efek ini, kita membutuhkan kotak. Jika kita ingin menganimasikan setiap huruf, maka setiap huruf harus menjadi elemen. Jika kita ingin menganimasikan setiap kata, maka kata harus menjadi elemen.

  1. Membuat fungsi utilitas JavaScript untuk memisahkan string menjadi elemen
  2. Orkestrasikan penggunaan utilitas ini

Fungsi utilitas huruf terpisah

Tempat yang menyenangkan untuk memulai adalah dengan fungsi yang mengambil string dan menampilkan masing-masing huruf dalam array.

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

Tujuan penyebaran {i>syntax<i} dari ES6 benar-benar membantu menjadikannya tugas yang cepat.

Fungsi utilitas kata pemisahan

Mirip dengan memisahkan huruf, fungsi ini mengambil string dan menampilkan setiap kata dalam array.

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

Tujuan split() pada {i>string<i} JavaScript memungkinkan kita untuk menentukan karakter mana yang akan diiris. Saya melewati ruang kosong, yang menunjukkan pemisahan di antara kata.

Membuat fungsi utilitas box

Efeknya membutuhkan kotak untuk setiap huruf, dan kita lihat di fungsi itu, bahwa map() dipanggil dengan fungsi span(). Berikut adalah fungsi span().

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

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

  return node
}

Penting untuk diperhatikan bahwa properti khusus yang disebut --index sedang ditetapkan dengan posisi array. Memiliki kotak-kotak untuk animasi huruf itu bagus, tapi memiliki indeks untuk digunakan dalam CSS adalah tambahan yang tampak kecil dengan dampak yang besar. Yang paling terkenal dalam dampak besar ini adalah sangat mengejutkan. Kita akan dapat menggunakan --index sebagai cara untuk mengalihkan animasi untuk lihat.

Kesimpulan utilitas

Modul splitting.js telah selesai:

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)

Berikutnya adalah mengimpor dan menggunakan fungsi byLetter() dan byWord() ini.

Orkestrasi pemisahan

Dengan utilitas terpisah yang siap digunakan, menyatukan semuanya berarti:

  1. Menemukan elemen yang akan dipisahkan
  2. Memisahkan dan mengganti teks dengan HTML

Setelah itu, CSS akan mengambil alih dan akan menganimasikan elemen / kotak.

Elemen Temuan

Saya memilih menggunakan atribut dan nilai untuk menyimpan informasi tentang animasi dan cara memisahkan teks. Saya suka menggunakan opsi deklaratif di dalam HTML. Atribut split-by digunakan dari JavaScript untuk menemukan elemen dan membuat kotak untuk huruf atau kata. Atribut letter-animation atau word-animation digunakan dari CSS, untuk menargetkan elemen turunan dan menerapkan transformasi dan animasi.

Berikut adalah contoh HTML yang menunjukkan kedua atribut tersebut:

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

Menemukan elemen dari JavaScript

Saya menggunakan sintaks pemilih CSS untuk keberadaan atribut guna mengumpulkan daftar elemen yang ingin teksnya terpisah:

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

Menemukan elemen dari CSS

Saya juga menggunakan pemilih kehadiran atribut di CSS untuk memberikan semua animasi huruf gaya dasar yang sama. Nanti, kita akan menggunakan nilai atribut untuk menambahkan nilai yang lebih spesifik gaya untuk memperoleh suatu efek.

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

Memisahkan teks di tempatnya

Untuk setiap target pemisahan yang ditemukan di JavaScript, kita akan membagi teksnya berdasarkan nilai atribut dan petakan setiap string ke <span>. Kita dapat kemudian ganti teks elemen dengan kotak yang kita buat:

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)
  }
})

Kesimpulan orkestrasi

index.js dalam proses:

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 dapat dibaca dalam bahasa Inggris berikut:

  1. Impor beberapa fungsi utilitas bantuan.
  2. Periksa apakah gerakan diperbolehkan untuk pengguna ini, jika tidak, jangan lakukan apa pun.
  3. Untuk setiap elemen yang ingin dipisah.
    1. Memisahkannya berdasarkan cara pemisahannya.
    2. Ganti teks dengan elemen.

Memisahkan animasi dan transisi

Manipulasi pemisahan dokumen di atas baru saja membuka banyak animasi dan efek yang potensial dengan CSS atau JavaScript. Ada beberapa tautan di bagian bawah artikel ini untuk membantu menginspirasi potensi pemisahan Anda.

Saatnya menunjukkan apa yang bisa Anda lakukan dengan ini! Saya akan membagikan 4 animasi berbasis CSS dan transisi. 🤓

Pisahkan huruf

Sebagai fondasi untuk efek huruf terpisah, saya menemukan bahwa CSS berikut membantu. Saya meletakkan semua transisi dan animasi di belakang kueri media {i>motion<i} dan lalu beri setiap huruf turunan baru span properti tampilan dan gaya untuk dilakukan dengan spasi putih:

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

Gaya spasi putih penting supaya bentang yang hanya merupakan sebuah spasi, tidak diciutkan oleh mesin tata letak. Sekarang, kita masuk ke hal-hal menyenangkan yang stateful.

Contoh huruf terpisah transisi

Contoh ini menggunakan transisi CSS ke efek teks terpisah. Dengan transisi, kita memerlukan status untuk dianimasikan mesin, dan saya memilih tiga status: tidak arahkan kursor, arahkan kursor ke kalimat, arahkan kursor ke huruf.

Saat pengguna mengarahkan kursor ke kalimat, yang disebut juga penampung, saya anak-anak seolah-olah pengguna mendorong mereka semakin jauh. Lalu, saat pengguna mengarahkan kursor saya, saya akan meneruskannya.

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

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

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

Menganimasikan contoh huruf terpisah

Contoh ini menggunakan animasi @keyframe yang telah ditentukan untuk animasi tanpa batas dan memanfaatkan indeks properti khusus inline untuk membuat pengaruh tersebut.

@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;
  }
}

Pisahkan kata

Flexbox berfungsi sebagai jenis kontainer bagi saya dalam contoh-contoh ini, dengan baik memanfaatkan unit ch sebagai panjang gap yang sehat.

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
Flexbox devtools menampilkan jarak antarkata

Contoh kata terpisah transisi

Dalam contoh transisi ini, saya menggunakan arah kursor lagi. Karena efek mula-mula menyembunyikan konten hingga kursor diarahkan, saya memastikan bahwa interaksi dan gaya hanya diterapkan jika perangkat memiliki kemampuan untuk mengarahkan kursor.

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

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

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

Menganimasikan contoh kata terpisah

Dalam contoh animasi ini, saya menggunakan CSS @keyframes lagi untuk membuat animasi tak terbatas pada paragraf teks reguler.

[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;
  }
}

Kesimpulan

Sekarang setelah Anda tahu cara saya melakukannya, bagaimana Anda akan melakukannya?! 🙂

Mari kita diversifikasi pendekatan kami dan mempelajari semua cara untuk membangun di web. Buat Codepen atau host demo Anda sendiri, kirim tweet kepada saya, dan saya akan menambahkannya ke Bagian remix komunitas di bawah ini.

Sumber

Lebih banyak demo dan inspirasi

Remix komunitas