建立尊重使用者動作偏好設定的無限字母效果。

完整文章 · YouTube 影片 · GitHub 上的資料來源

<h1 split-by="letter" letter-animation="breath">
  animated letters
</h1>

       
@keyframes breath {
  from
{
   
animation-timing-function: ease-out;
 
}

  to
{
   
transform: scale(1.25) translateY(-5px) perspective(1px);
   
text-shadow: 0 0 40px var(--glow-color);
   
animation-timing-function: ease-in-out;
 
}
}

@media (prefers-reduced-motion:no-preference) {
 
[letter-animation] > span {
   
display: inline-block;
   
white-space: break-spaces;
 
}

 
[letter-animation=breath] {
   
--glow-color: white;
 
}

 
[letter-animation=breath]>span {
   
animation: breath 1.2s ease calc(var(--index) * 100 * 1ms) infinite alternate;
 
}
}

@media (prefers-reduced-motion:no-preference) and (prefers-color-scheme: light) {
 
[letter-animation=breath] {
   
--glow-color: black;
 
}
}
       

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

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

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

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

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

  splitTargets
.forEach(node => {
    let nodes
= byLetter(node.innerText)

   
if (nodes)
      node
.firstChild.replaceWith(...nodes)
 
})
}