분할 텍스트 애니메이션 빌드

문자 및 단어 분할 애니메이션을 빌드하는 방법에 관한 기본적인 개요입니다.

이 게시물에서는 최소한의 접근성이 있고 여러 브라우저에서 작동하는 웹용 분할 텍스트 애니메이션과 상호작용을 해결하는 방법에 관한 생각을 공유하고자 합니다. 데모 사용해 보기

데모

동영상을 선호한다면 이 게시물의 YouTube 버전을 참조하세요.

개요

텍스트 분할 애니메이션은 놀라울 수 있습니다. 이 게시물에서는 애니메이션의 잠재력에 대해 대략적으로만 다루겠지만 빌드를 위한 기초를 제공합니다. 순차적으로 애니메이션을 만드는 것이 목표입니다. 텍스트는 기본적으로 읽을 수 있어야 하며 애니메이션은 위에 작성되어 있어야 합니다. 텍스트 분할 모션 효과는 지나치게 화려하고 지장을 줄 수 있으므로 Google에서는 HTML을 조작하거나 사용자가 모션에 문제가 없는 경우에만 모션 스타일을 적용합니다.

워크플로와 결과의 일반적인 개요는 다음과 같습니다.

  1. CSS 및 JS용으로 축소된 모션 조건부 변수를 준비합니다.
  2. JavaScript에서 분할 텍스트 유틸리티를 준비합니다.
  3. 페이지 로드 시 조건부 및 유틸리티를 조정합니다.
  4. 문자와 단어의 CSS 전환 및 애니메이션을 작성합니다.

다음은 조건부 결과의 미리보기입니다.

요소 패널이 열려 있고 축소된 모션이 '축소'로 설정되어 있으며 h1이 분할되지 않은 상태로 표시된 Chrome DevTools의 스크린샷
사용자가 축소된 모션을 선호함: 텍스트를 읽을 수 있거나 분할되지 않음

사용자가 모션을 줄이는 것을 선호한다면 HTML 문서를 그대로 두고 애니메이션을 사용하지 않습니다. 움직임이 괜찮으면 가공되지 않은 조각을 자릅니다. 다음은 JavaScript가 텍스트를 문자별로 분할한 후의 HTML 미리보기입니다.

요소 패널이 열려 있고 축소된 모션이 '축소'로 설정되어 있으며 h1이 분할되지 않은 상태로 표시된 Chrome DevTools의 스크린샷
사용자가 움직임을 받아들이며 텍스트가 여러 <span> 요소로 분할됨

모션 조건문 준비

편리하게 사용 가능한 @media (prefers-reduced-motion: reduce) 미디어 쿼리는 이 프로젝트의 CSS 및 JavaScript에서 사용됩니다. 이 미디어 쿼리는 텍스트 분할 여부를 결정하기 위한 기본 조건부입니다 CSS 미디어 쿼리는 전환 및 애니메이션을 보류하는 데 사용되고, 자바스크립트 미디어 쿼리는 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를 사용하여 Nesting Draft 1에서 @nest 구문을 사용 설정하여 동일한 값을 확인할 수 있습니다. 이렇게 하면 애니메이션에 관한 모든 로직과 상위 요소 및 하위 요소의 스타일 요구사항을 한곳에 저장할 수 있습니다.

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

PostCSS 맞춤 속성과 자바스크립트 부울을 사용하여 효과를 조건부로 업그레이드할 수 있습니다. 그러면 문자열을 요소로 변환하는 JavaScript에 대해 자세히 설명하는 다음 섹션으로 넘어가게 됩니다.

텍스트 분할

텍스트 문자, 단어, 행 등은 CSS 또는 JS로 개별적으로 애니메이션 처리할 수 없습니다. 효과를 얻으려면 상자가 필요합니다. 각 문자에 애니메이션을 적용하려면 각 문자가 요소여야 합니다. 각 단어에 애니메이션을 적용하려면 각 단어가 요소여야 합니다.

  1. 문자열을 요소로 분할하는 JavaScript 유틸리티 함수를 만듭니다.
  2. 유틸리티 사용 조정

문자 분할 유틸리티 함수

먼저 문자열을 받아 배열의 각 문자를 반환하는 함수부터 살펴보겠습니다.

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

ES6의 스프레드 문법이 이러한 작업을 신속하게 수행하는 데 큰 도움이 되었습니다.

단어 분할 유틸리티 함수

문자를 분할하는 것과 마찬가지로 이 함수는 문자열을 취하고 각 단어를 배열로 반환합니다.

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

JavaScript 문자열의 split() 메서드를 사용하면 슬라이스할 문자를 지정할 수 있습니다. 단어 간 분할을 나타내는 빈 공간을 전달했습니다.

상자 유틸리티 기능 만들기

이 효과를 사용하려면 각 문자에 상자가 필요하며 이러한 함수에서 span() 함수를 사용하여 map()가 호출되는 것을 확인할 수 있습니다. 다음은 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;
}

공백 스타일은 공간일 뿐인 스팬이 레이아웃 엔진에 의해 축소되지 않도록 하는 데 중요합니다. 이제 스테이트풀(Stateful) 재미있는 작업을 살펴보겠습니다.

전환 분할 문자 예

이 예에서는 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을 만들거나 데모를 호스팅하고 트윗해 주시면 아래 커뮤니티 리믹스 섹션에 추가하겠습니다.

소스

더 많은 데모 및 아이디어

커뮤니티 리믹스