יצירת אנימציות של טקסט מפוצל

סקירה כללית בסיסית של בניית אנימציות של אותיות ומילים.

בפוסט הזה אני רוצה לשתף חשיבה על דרכים לפתרון אנימציות טקסט מפוצלות אינטראקציות מינימליות באינטרנט, נגישות ועובדות בדפדפנים שונים. אפשר לנסות את ההדגמה.

הדגמה

אם אתם מעדיפים סרטון, הנה גרסה של YouTube לפוסט:

סקירה כללית

אנימציות של טקסט מפוצלות יכולות להיות מדהימות. אנחנו כמעט לא נצליח לגרד את פני השטח של את הפוטנציאל של האנימציה בפוסט הזה, אבל הוא מספק בסיס מופעל. המטרה היא להוסיף אנימציה הדרגתית. הטקסט צריך להיות קריא כברירת מחדל, והאנימציה בנויה מעליה. אפקטים של תנועת טקסט מפוצל יהיו מיותרים ועלולים לשבש, לכן נבצע רק שינויים ב-HTML, או החלת סגנונות תנועה אם המשתמש מסכים לתנועה.

סקירה כללית של תהליך העבודה והתוצאות:

  1. הכנות: תנועה מוקטנת מותנית ל-CSS ול-JS.
  2. הכן כלי טקסט מפוצל JavaScript.
  3. תזמור התנאים וההגבלות בדף טעינה.
  4. כתיבה של מעברים ואנימציות ב-CSS לאותיות ולמילים (החלק המרכזי!).

לפניכם תצוגה מקדימה של התוצאות המותנות שנרצה להשתמש בהן:

צילום מסך של כלי הפיתוח של Chrome כשחלונית הרכיבים פתוחה, והתנועה מופחתת ל'הקטנה' וה-h1 מוצג ללא פיצול
המשתמש מעדיפים תנועה מופחתת: הטקסט קריא / לא מפוצל

אם המשתמש מעדיף תנועה מופחתת, אנחנו משאירים את מסמך ה-HTML במקומו אנימציה. אם זה בסדר מבחינתך, אנחנו חותכים אותה לחתיכות. הנה תצוגה מקדימה של ה-HTML אחרי ש-JavaScript פוצל את הטקסט לפי אות.

צילום מסך של כלי הפיתוח של Chrome כשחלונית הרכיבים פתוחה, והתנועה מופחתת ל'הקטנה' וה-h1 מוצג ללא פיצול
אין בעיה עם התנועה; הטקסט מחולק לכמה <span> רכיבים

תנאי התנועה בהכנה

ייעשה שימוש בשאילתת המדיה שזמינה @media (prefers-reduced-motion: reduce) מ-CSS וגם JavaScript בפרויקט הזה. שאילתת המדיה הזו היא התנאי העיקרי שלנו להחליט אם לפצל טקסט או לא. שאילתת המדיה של שירות CSS תשמש לניכוי המס במקור והאנימציות, ושאילתת המדיה של JavaScript תשמש למנוע את המניפולציה של ה-HTML.

הכנת ה-CSS מותנה

השתמשתי ב-PostCSS כדי להפעיל את התחביר של שאילתות מדיה ברמה 5, שבה אפשר לאחסן ערך בוליאני של שאילתת מדיה למשתנה:

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

הכנת ה-JS מותנית

ב-JavaScript, הדפדפן מאפשר לבדוק שאילתות מדיה, להרוס כדי לחלץ את התוצאה הבוליאנית מבדיקת שאילתת המדיה ולשנות את השם שלה:

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

לאחר מכן אוכל לבצע בדיקה לאיתור motionOK ולשנות את המסמך רק אם המשתמש לא שהתבקש לצמצם את התנועה.

if (motionOK) {
  // document split manipulations
}

אפשר לבדוק את אותו הערך על ידי שימוש ב-PostCSS כדי להפעיל את התחביר @nest מ: מציבים טיוטה 1. הפעולה הזאת מאפשרת לי לשמור את כל הלוגיקה של האנימציה ואת דרישות הסגנון עבור הורים וילדים, במקום אחד:

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

עם המאפיין המותאם אישית של PostCSS ועם ערך בוליאני של JavaScript, אנחנו מוכנים לשדרג את האפקט באופן מותנה. וכל זה מעביר אותנו לקטע הבא שבו להציג פירוט של ה-JavaScript לטרנספורמציה של מחרוזות לרכיבים.

פיצול טקסט

לא ניתן להוסיף אנימציה של אותיות טקסט, מילים, שורות וכו' בנפרד באמצעות CSS או JS. כדי להשיג את האפקט, אנחנו צריכים תיבות. אם אנחנו רוצים להוסיף אנימציה לכל אות, כל אות צריכה להיות אלמנט. אם אנחנו רוצים להוסיף אנימציה לכל מילה, המילה צריכה להיות רכיב.

  1. יצירת פונקציות עזר של JavaScript לפיצול מחרוזות לרכיבים
  2. תזמור השימוש בכלים האלה

פונקציית פיצול האותיות

מקום כיף להתחיל בו הוא פונקציה שמקבלת מחרוזת ומחזירה אות במערך.

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

spread התחביר מ-ES6 באמת עזר להפוך את המשימה הזו למשימה מהירה.

פונקציית פיצול המילים

בדומה לפיצול אותיות, הפונקציה הזו לוקחת מחרוזת ומחזירה כל מילה במערך.

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

split() במחרוזות JavaScript, אנחנו יכולים לציין את התווים שבהם תרצו לפצל. העברתי שטח ריק, שמציין פיצול בין מילים.

יצירת כלי שירות של תיבות

לאפקט נדרשות תיבות בכל אות, ואנחנו רואים בפונקציות האלה map() מופעלת באמצעות הפונקציה span(). זוהי הפונקציה span().

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

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

  return node
}

חשוב לציין שמוגדר נכס מותאם אישית בשם --index את מיקום המערך. זה נהדר שיש תיבות לאנימציות של אותיות, אבל הוספת אינדקס ב-CSS היא תוספת קטנה לכאורה שיש לה השפעה גדולה. הבולטת ביותר בהשפעה הגדולה הזו היא מדהימות. נוכל להשתמש ב---index כדרך להשבית אנימציות של אנימציות מראה.

סיכום על תשתיות

המודול splitting.js הושלם:

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)

השלב הבא הוא ייבוא הפונקציות byLetter() ו-byWord() והשימוש בהן.

תזמור פיצול

השילוב של כל הכלים האלה מאפשר:

  1. איך למצוא אילו רכיבים לפצל
  2. פיצול שלהם והחלפת טקסט ב-HTML

לאחר מכן, שירות ה-CSS ישתלט ותוסיף את הרכיבים או התיבות.

חיפוש רכיבים

בחרתי להשתמש במאפיינים ובערכים כדי לשמור מידע לגבי אנימציה ואיך לפצל את הטקסט. אהבתי להשתמש באפשרויות ההצהרתיות האלה ל-HTML. המאפיין split-by משמש מ-JavaScript כדי למצוא ויוצרים תיבות עבור אותיות או מילים. המאפיין נעשה שימוש ב-letter-animation או ב-word-animation מ-CSS כדי לטרגט לרכיב וליישם טרנספורמציות ואנימציות.

הנה דוגמה של קוד HTML שמדגים את שני המאפיינים:

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

איתור רכיבים מ-JavaScript

השתמשתי בתחביר הסלקטור ב-CSS לציון נוכחות מאפיינים כדי לאסוף את הרשימה של רכיבים שרוצים לפצל את הטקסט שלהם:

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

חיפוש רכיבים מ-CSS

השתמשתי גם בבורר נוכחות המאפיינים ב-CSS כדי להציג את כל האנימציות של האותיות אותם סגנונות בסיס. מאוחר יותר, נשתמש בערך המאפיין כדי להוסיף עוד מאפיינים ספציפיים כדי ליצור אפקט.

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

פיצול הטקסט במקומו

לכל אחד מהיעדים המפוצלים שאנחנו מוצאים ב-JavaScript, נפצל את הטקסט שלהם על סמך ערך המאפיין ולמפות כל מחרוזת ל-<span>. אנחנו יכולים לאחר מכן מחליפים את הטקסט של הרכיב בתיבות שיצרנו:

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

סיכום תזמור

index.js הושלמו:

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 באנגלית הבאה:

  1. עליך לייבא כמה פונקציות עזר נוספות.
  2. אפשר לבדוק אם התנועה מתאימה למשתמש הזה ואם לא לעשות כלום.
  3. לכל רכיב שרוצים לפצל
    1. כדאי לפצל אותן לפי האופן שבו הן יפוצלו.
    2. החלפת טקסט ברכיבים.

פיצול אנימציות ומעברים

בעקבות פיצול המסמכים שלמעלה, להשתמש ב-CSS או ב-JavaScript כדי ליצור אנימציות ואפקטים פוטנציאליים. יש כמה קישורים בסוף המאמר הזה כדי לקבל השראה לפיצול שלכם.

הגיע הזמן להראות מה אפשר לעשות עם זה! אני אשתף 4 אנימציות מבוססות-CSS ו מעברים שונים. 🤓

אותיות מפוצלות

כבסיס לאפקטים של האותיות המפוצלות, מצאתי שה-CSS הבא שימושי. שמתי את כל המעברים והאנימציות מאחורי שאילתת המדיה עם התנועה ואז מזינים לכל אות צאצא חדשה span מאפיין לרשת המדיה וגם סגנון מה עושים עם רווחים לבנים:

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

חשוב להקפיד שהסגנון של המרחבים הלבנים יהיה רווח, לא מכווצים על ידי מנוע הפריסה. עכשיו עוברים לדברים הכיפיים ומהנים.

דוגמה לאותיות מפוצלות למעבר

בדוגמה הזו נעשה שימוש במעברי CSS לאפקט הטקסט המפוצל. בעזרת המעברים צריך מצבים שבהם המנוע יוכל ליצור אנימציה, ובחרתי שלושה מצבים: לא מעבירים את העכבר מעל למשפט, מעבירים את העכבר מעל האות, מעבירים את העכבר מעליה.

כשהמשתמש מעביר את העכבר מעל המשפט, כלומר הכלי בקונטיינר, המערכת משנה את הדירוג של כל הילדים כאילו המשתמש הרחיק אותם. לאחר מכן, בזמן שהמשתמש מעביר את סמן העכבר מעל מכתב, אני אעביר אותו.

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

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

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

דוגמה להנפשה של אותיות מפוצלות

בדוגמה הזו נעשה שימוש באנימציה מוגדרת מראש של @keyframe כדי ליצור אנימציה אינסופית אות, והוא משתמש באינדקס הנכס המותאם אישית המוטבע כדי ליצור !

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

פיצול מילים

ה-Flexbox פעל לי כסוג קונטיינר בדוגמאות האלה, שימוש ביחידה ch כאורך פער תקין.

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
כלי פיתוח Flexbox שמראים את הפער בין המילים

דוגמה למילים מפוצלות במעבר

בדוגמת המעבר הזו, השתמשתי שוב בהעברת העכבר מעליה. מאחר שהאפקט מסתיר בהתחלה את עד להעברת העכבר מעל התוכן, וידאתי שהאינטראקציה והסגנונות יחולו רק על התוכן. אם המכשיר יכול היה לרחף.

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

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

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

דוגמה להנפשה של מילים מפוצלות

באנימציה הזו, אני משתמש שוב ב-CSS @keyframes כדי ליצור אנימציה אינסופית בפסקה רגילה של טקסט.

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

סיכום

עכשיו אתה יודע איך עשיתי את זה, איך היית?! 🙂

בואו לגוון את הגישות שלנו ונלמד את כל הדרכים לבניית אתרים באינטרנט. יוצרים Codepen או מארחים הדגמה משלכם, שולחים לי הודעה ב-Twitter ואני אוסיף אותו הקטע 'רמיקסים' של הקהילה בהמשך.

מקור

עוד הדגמות והשראה

רמיקסים קהילתיים