분할된 글자와 단어 애니메이션을 만드는 방법에 관한 기본적인 개요입니다.
이 게시물에서는 최소한의 접근성을 갖추고 여러 브라우저에서 작동하는 웹용 텍스트 분할 애니메이션 및 상호작용을 해결하는 방법에 관한 생각을 공유하고자 합니다. 데모를 사용해 보세요.
동영상을 선호하는 경우 이 게시물의 YouTube 버전을 확인하세요.
개요
텍스트 분할 애니메이션은 멋진 효과를 낼 수 있습니다. 이 게시물에서는 애니메이션의 가능성을 거의 다루지 않지만, 이를 기반으로 빌드할 수 있는 토대를 제공합니다. 목표는 점진적으로 애니메이션을 적용하는 것입니다. 텍스트는 기본적으로 읽을 수 있어야 하며 애니메이션은 텍스트 위에 빌드되어야 합니다. 텍스트 분할 모션 효과는 과도해져서 방해가 될 수 있으므로 사용자가 모션에 동의하는 경우에만 HTML을 조작하거나 모션 스타일을 적용합니다.
다음은 워크플로 및 결과에 대한 일반적인 개요입니다.
- CSS 및 JS용 모션 감소 조건부 변수를 준비합니다.
- JavaScript에서 준비 분할 텍스트 유틸리티
- 페이지 로드 시 조건문과 유틸리티를 조정합니다.
- 글자 및 단어에 CSS 전환 및 애니메이션을 작성합니다 (재미있는 부분).
다음은 원하는 조건부 결과의 미리보기입니다.

사용자가 동작 줄이기를 선호하는 경우 HTML 문서를 그대로 두고 애니메이션을 실행하지 않습니다. 동작이 괜찮으면 여러 조각으로 자릅니다. 다음은 JavaScript가 텍스트를 글자로 분할한 후의 HTML 미리보기입니다.

모션 조건 준비
이 프로젝트에서는 편리하게 사용할 수 있는 @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를 사용하여 Nesting Draft 1에서 @nest
문법을 사용 설정하여 동일한 값을 확인할 수 있습니다. 이를 통해 애니메이션과 부모 및 자식의 스타일 요구사항에 관한 모든 로직을 한곳에 저장할 수 있습니다.
letter-animation {
@media (--motionOK) {
/* animation styles */
}
}
PostCSS 맞춤 속성과 JavaScript 불리언을 사용하면 효과를 조건부로 업그레이드할 수 있습니다. 이제 문자열을 요소로 변환하는 JavaScript를 설명하는 다음 섹션으로 넘어갑니다.
텍스트 분할
텍스트 글자, 단어, 줄 등은 CSS 또는 JS로 개별적으로 애니메이션을 적용할 수 없습니다. 이 효과를 내려면 상자가 필요합니다. 각 글자를 애니메이션으로 만들려면 각 글자가 요소여야 합니다. 각 단어를 애니메이션으로 만들려면 각 단어가 요소여야 합니다.
- 문자열을 요소로 분할하는 JavaScript 유틸리티 함수 만들기
- 이러한 유틸리티의 사용을 오케스트레이션합니다.
문자 분할 유틸리티 함수
문자열을 가져와 배열의 각 문자를 반환하는 함수로 시작하는 것이 좋습니다.
export const byLetter = text =>
[...text].map(span)
ES6의 spread 문법은 이 작업을 신속하게 처리하는 데 큰 도움이 되었습니다.
단어 분할 유틸리티 함수
이 함수는 글자를 분할하는 것과 마찬가지로 문자열을 가져와 각 단어를 배열로 반환합니다.
export const byWord = text =>
text.split(' ').map(span)
JavaScript 문자열의 split()
메서드를 사용하면 슬라이스할 문자를 지정할 수 있습니다.
단어 사이의 분할을 나타내는 빈 공간을 전달했습니다.
상자 유틸리티 함수 만들기
이 효과에는 각 글자에 대한 상자가 필요하며, 이러한 함수에서 map()
가 span()
함수와 함께 호출되는 것을 확인할 수 있습니다. span()
함수는 다음과 같습니다.
const span = (text, index) => {
const node = document.createElement('span')
node.textContent = text
node.style.setProperty('--index', index)
return node
}
--index
이라는 맞춤 속성이 배열 위치와 함께 설정되고 있습니다. 글자 애니메이션용 상자가 있는 것은 좋지만 CSS에서 사용할 색인이 있으면 큰 영향을 미치는 작은 추가 기능이 됩니다.
이러한 큰 영향에서 가장 눈에 띄는 것은 staggering입니다.
--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()
함수를 가져와 사용하는 것입니다.
분할 조정
분할 유틸리티를 사용할 준비가 되었으므로 모든 것을 통합하는 것은 다음을 의미합니다.
- 분할할 요소를 찾기
- 텍스트를 분할하고 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는 다음과 같이 영어로 읽을 수 있습니다.
- 일부 도우미 유틸리티 함수를 가져옵니다.
- 이 사용자의 동작이 괜찮은지 확인하고, 그렇지 않으면 아무것도 하지 않습니다.
- 분할하려는 각 요소
- 분할하려는 방식에 따라 분할합니다.
- 텍스트를 요소로 바꿉니다.
애니메이션 및 전환 분할
위의 분할 문서 조작을 통해 CSS 또는 JavaScript로 다양한 애니메이션과 효과를 사용할 수 있게 되었습니다. 이 도움말 하단에는 분할 가능성을 파악하는 데 도움이 되는 몇 가지 링크가 있습니다.
이제 이 기능을 활용해 보세요. CSS 기반 애니메이션과 전환을 4가지 공유해 드릴게요. 🤓
글자 분할
분할된 글자 효과의 기반으로 다음 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;
}
전환 분할 단어 예
이 전환 예시에서는 호버를 다시 사용합니다. 효과로 인해 마우스를 가져갈 때까지 콘텐츠가 숨겨지므로 기기에 마우스를 가져갈 수 있는 기능이 있는 경우에만 상호작용과 스타일이 적용되도록 했습니다.
@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을 만들거나 자체 데모를 호스팅하고, 데모를 포함하여 트윗을 보내주세요. 아래의 커뮤니티 리믹스 섹션에 추가해 드리겠습니다.
소스
더 많은 데모 및 아이디어
커뮤니티 리믹스
- CodeSandbox의 gnehcwu가 만든
<text-hover>
웹 구성요소