You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中使用styled-components实现跳跃文字效果失效,求问题排查

问题:React中实现跳跃文字动画无效果

我想实现跳跃文字动画效果,用React+styled-components创建组件后,测试时没有出现预期效果。以下是我的代码:

App.tsx

import styled from "styled-components";

const Jumper = styled.p`
  @keyframes trampoline {
    0% {
      transform: translateY(100%);
      animation-timing-function: ease-out;
    }

    50% {
      transform: translateY(0);
      animation-timing-function: ease-in;
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    [word-animation] {
      display: inline-flex;
      flex-wrap: wrap;
      gap: 1ch;
    }

    [word-animation="trampoline"] > span {
      display: inline-block;
      transform: translateY(100%);
      animation: trampoline 3s ease calc(var(--index) * 150 * 1ms) infinite
        alternate;
    }
  }
`;

export default function App() {
  const span = (text, index) => {
    const node = document.createElement("span");

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

    return node;
  };

  const byWord = (text) => text.split(" ").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 = byWord(node.innerText);

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

  return <Jumper>split a paragraph of content 🤘💀</Jumper>;
}

错误原因及修复方案

1. CSS选择器语法错误

代码中的&gt;是HTML转义字符,CSS无法识别,需替换为原生的>符号:

/* 错误写法 */
[word-animation="trampoline"] &gt; span { ... }

/* 正确写法 */
[word-animation="trampoline"] > span { ... }

2. 缺少必要的DOM属性匹配

  • CSS样式针对带有word-animation="trampoline"属性的元素下的span,但<Jumper>组件未添加该属性,需补上;
  • DOM操作的目标是带有split-by属性的元素,必须给<Jumper>添加该属性,否则querySelectorAll("[split-by]")找不到目标元素:
return <Jumper word-animation="trampoline" split-by>split a paragraph of content 🤘💀</Jumper>;

3. DOM操作时机错误

在组件函数体直接执行document.querySelectorAll时,组件还未挂载到DOM树,无法找到元素。需用useEffect钩子在组件挂载后执行DOM操作:

import { useEffect } from "react";

export default function App() {
  const span = (text, index) => {
    const node = document.createElement("span");
    node.textContent = text;
    node.style.setProperty("--index", index);
    return node;
  };

  const byWord = (text) => text.split(" ").map(span);

  useEffect(() => {
    const { matches: motionOK } = window.matchMedia(
      "(prefers-reduced-motion: no-preference)"
    );

    if (motionOK) {
      const splitTargets = document.querySelectorAll("[split-by]");
      splitTargets.forEach((node) => {
        // 清空原有内容后插入分割后的span
        node.innerHTML = "";
        const nodes = byWord(node.innerText);
        nodes.forEach(spanNode => node.appendChild(spanNode));
      });
    }
  }, []);

  return <Jumper word-animation="trampoline" split-by>split a paragraph of content 🤘💀</Jumper>;
}

4. Styled-components选择器作用域问题

Styled-components会给组件添加唯一类名,全局属性选择器[word-animation="trampoline"]可能匹配失效。可以调整CSS写法,直接针对组件内部的span,更符合组件封装特性:

const Jumper = styled.p`
  @keyframes trampoline {
    0% {
      transform: translateY(100%);
      animation-timing-function: ease-out;
    }

    50% {
      transform: translateY(0);
      animation-timing-function: ease-in;
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 1ch;

    > span {
      display: inline-block;
      transform: translateY(100%);
      animation: trampoline 3s ease calc(var(--index) * 150 * 1ms) infinite alternate;
    }
  }
`;

内容的提问来源于stack exchange,提问作者user14992713

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 10:03:22