Membuat animasi teks terpisah

Ikhtisar dasar tentang cara membuat animasi huruf dan kata terpisah.

Dalam postingan ini, saya ingin berbagi pemikiran tentang cara memecahkan interaksi dan animasi teks terpisah untuk web yang minimal, mudah diakses, dan berfungsi di berbagai browser. Coba demonya.

Demo

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

Ringkasan

Animasi {i>split text<i} bisa menjadi luar biasa. Kita hampir tidak akan membahas potensi animasi dalam postingan ini, tetapi hal ini memberikan fondasi untuk membangun. Tujuannya adalah untuk menganimasikan secara progresif. Teks harus dapat dibaca secara default, dengan animasi di atasnya. Efek gerakan teks terpisah bisa sangat luar biasa dan berpotensi mengganggu, jadi kita hanya akan memanipulasi HTML, atau menerapkan gaya gerakan jika pengguna mengizinkan gerakan.

Berikut ini ringkasan umum alur kerja dan hasilnya:

  1. Menyiapkan variabel kondisional gerakan yang dikurangi untuk CSS dan JS.
  2. Menyiapkan utilitas teks terpisah di JavaScript.
  3. Orkestrasi kondisional dan utilitas pada pemuatan halaman.
  4. Tulis transisi dan animasi CSS untuk huruf dan kata (bagian ujung!).

Berikut adalah pratinjau hasil kondisional yang akan kita cari:

screenshot Chrome DevTools dengan panel Elemen terbuka dan gerakan yang direduksi disetel ke &#39;kurangi&#39; dan tombol h1 ditampilkan tidak terpisah
Pengguna lebih memilih gerakan yang lebih kecil: teks dapat dibaca / tidak dipisahkan

Jika pengguna lebih memilih gerakan yang lebih kecil, biarkan dokumen HTML itu sendiri dan tidak melakukan animasi. Jika ada gerakan yang wajar, kita akan melanjutkan dan membaginya menjadi beberapa bagian. Berikut adalah pratinjau HTML setelah JavaScript membagi teks menurut huruf.

screenshot Chrome DevTools dengan panel Elemen terbuka dan gerakan yang direduksi disetel ke &#39;kurangi&#39; dan tombol h1 ditampilkan tidak terpisah
Pengguna mengizinkan 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 dalam project ini. Kueri media ini adalah kondisional utama untuk memutuskan membagi teks atau tidak. Kueri media CSS akan digunakan untuk menahan transisi dan animasi, sedangkan kueri media JavaScript akan digunakan untuk menahan manipulasi HTML.

Menyiapkan CSS kondisional

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

Dalam JavaScript, browser menyediakan cara untuk memeriksa kueri media. Saya menggunakan destrukturisasi 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 meminta untuk mengurangi gerakan.

if (motionOK) {
  // document split manipulations
}

Saya dapat memeriksa nilai yang sama dengan menggunakan PostCSS untuk mengaktifkan sintaksis @nest dari Nesting Draft 1. Dengan begitu, saya dapat menyimpan semua logika tentang animasi dan persyaratan gayanya untuk induk dan turunan, di satu tempat:

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

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

Memisahkan Teks

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

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

Fungsi utilitas memisahkan huruf

Tempat yang menyenangkan untuk memulai adalah fungsi yang mengambil string dan menampilkan setiap huruf dalam array.

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

Sintaksis spread dari ES6 benar-benar membantu menjadikan tugas tersebut cepat.

Fungsi utilitas memisahkan kata

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

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

Metode split() pada string JavaScript memungkinkan kita untuk menentukan karakter mana yang akan diiris. Saya meneruskan spasi kosong, yang menunjukkan pemisahan antarkata.

Menjadikan kotak berfungsi sebagai utilitas

Efeknya memerlukan kotak untuk setiap huruf, dan kita melihat dalam fungsi tersebut, 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 disetel dengan posisi array. Memiliki kotak untuk animasi huruf memang bagus, tetapi memiliki indeks untuk digunakan dalam CSS merupakan tambahan yang terlihat kecil dengan dampak yang besar. Yang paling terlihat dalam dampak besar ini adalah menakjubkan. Kita akan dapat menggunakan --index sebagai cara mengimbangi animasi untuk tampilan bertahap.

Kesimpulan utilitas

Modul splitting.js yang 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)

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

Orkestrasi terpisah

Setelah beberapa utilitas yang terpisah 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 yang diinginkan dan cara memisahkan teks. Saya suka menempatkan opsi deklaratif ini ke 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 turunan elemen dan menerapkan transformasi dan animasi.

Berikut adalah contoh HTML yang menunjukkan kedua atribut:

<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 sintaksis pemilih CSS untuk kehadiran atribut guna mengumpulkan daftar elemen yang ingin memisahkan teks:

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

Menemukan elemen dari CSS

Saya juga menggunakan pemilih kehadiran atribut dalam CSS untuk memberi semua animasi huruf gaya dasar yang sama. Nanti, kita akan menggunakan nilai atribut untuk menambahkan gaya yang lebih spesifik guna mencapai efek.

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

Memisahkan teks di tempat

Untuk setiap target pemisahan yang ditemukan di JavaScript, kita akan memisahkan teksnya berdasarkan nilai atribut dan memetakan setiap string ke <span>. Kemudian kita bisa mengganti 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 selesai:

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. Mengimpor beberapa fungsi utilitas bantuan.
  2. Periksa apakah gerakan diperbolehkan untuk pengguna ini, jika tidak melakukan apa pun.
  3. Untuk setiap elemen yang ingin dipisahkan.
    1. Membaginya berdasarkan cara pemisahannya.
    2. Mengganti teks dengan elemen.

Memisahkan animasi dan transisi

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

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

Pisahkan huruf

Sebagai dasar untuk efek huruf terpisah, menurut saya CSS berikut sangat membantu. Saya menempatkan semua transisi dan animasi di belakang kueri media gerakan, lalu memberi setiap huruf turunan baru span sebuah properti tampilan serta gaya untuk apa yang harus dilakukan dengan ruang kosong:

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

Gaya ruang putih penting agar span yang hanya berupa spasi, tidak diciutkan oleh mesin tata letak. Sekarang, kita lanjutkan ke hal-hal yang menyenangkan.

Contoh huruf terpisah transisi

Contoh ini menggunakan transisi CSS ke efek teks terpisah. Dengan transisi, kami memerlukan status agar mesin dapat dianimasikan, dan saya memilih tiga status: tanpa mengarahkan kursor, mengarahkan kursor pada kalimat, mengarahkan kursor pada huruf.

Saat pengguna mengarahkan kursor ke kalimat, alias container, saya memperkecil skala semua turunan seolah-olah pengguna mendorongnya lebih jauh. Kemudian, saat pengguna menggeser sebuah huruf, saya akan menampilkannya.

@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 menganimasikan setiap huruf tanpa batas, dan memanfaatkan indeks properti kustom inline untuk membuat efek bertahap.

@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

Dalam contoh-contoh ini, Flexbox berfungsi sebagai jenis container, memanfaatkan unit ch dengan baik sebagai panjang celah yang sehat.

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

Contoh kata terpisah transisi

Dalam contoh transisi ini, saya menggunakan pengarahan kursor lagi. Karena efek awalnya menyembunyikan konten hingga pengarahan kursor, 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 pemisahan kata

Dalam contoh animasi ini, saya menggunakan CSS @keyframes lagi untuk membuat animasi tanpa batas yang bertahap pada paragraf teks biasa.

[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 Anda tahu bagaimana saya melakukannya, bagaimana Anda akan?! 🙂

Mari lakukan diversifikasi pendekatan dan pelajari semua cara untuk membangun di web. Buat Codepen atau host demo Anda sendiri, kirim tweet, dan saya akan menambahkannya ke bagian remix Komunitas di bawah.

Sumber

Demo dan inspirasi lainnya

Remix komunitas