You need to enable JavaScript to run this app. React状态变更时实现文本对象淡入淡出动画的相关问题
优惠活动
大模型
产品
解决方案
定价
更多

React状态变更时实现文本对象淡入淡出动画的相关问题

React 价格文本变更动画实现方案

现有代码问题

  • 单个updateNotificationRef被绑定到循环生成的多个<td>元素上,最终ref只会指向最后一个价格单元格,前两个价格变更时无法触发动画
  • 动画关键帧opacity: [0, 1, 0]执行结束后元素透明度会变为0,和“最终展示新价格”的需求冲突

方案1:修改现有Web Animations API逻辑

调整后代码如下:

export const MatchPrices = () => {
  const markets = useStore($markets);
  // 用数组存储所有价格单元格的ref
  const priceRefs = useRef([]);

  useEffect(() => {
    if (markets.length === 0) return;

    // 遍历所有价格元素触发动画
    priceRefs.current.forEach(el => {
      if (!el) return;
      el.animate(
        {
          // 动画逻辑:原有透明度1 -> 降到0 -> 回到1(淡出后淡入)
          opacity: [1, 0, 1],
          // 可选加高亮效果:短暂变黄
          backgroundColor: ["transparent", "#fff3cd", "transparent"]
        },
        {
          duration: 800,
          easing: "ease",
          // 动画结束后保持最终状态
          fill: "forwards"
        }
      );
    });
  }, [markets]);

  // 每次渲染前清空ref数组
  priceRefs.current = [];

  return (
    <Card className="price-display">
      <Table className="card-table prices-table" size="sm">
        <thead className="card-header">
          <tr>
            <th>Home</th>
            <th>Draw</th>
            <th>Away</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            {markets.length !== 0 ? (
              markets
                .filter((market) => market.mt === "MATCH_WINNER")[0]
                .selections.map((selection, index) => (
                  <td
                    className="match-price"
                    key={selection.id}
                    // 给每个元素绑定单独的ref
                    ref={el => priceRefs.current[index] = el}
                  >
                    {selection.price.toFixed(2)}
                  </td>
                ))
            ) : (
              <td colSpan={3} style={{textAlign: 'center'}}>
                <Spinner
                  animation="border"
                  role="status"
                  style={{
                    marginLeft: "Auto",
                    marginRight: "Auto",
                  }}
                ></Spinner>
              </td>
            )}
          </tr>
        </tbody>
      </Table>
    </Card>
  );
};

方案2:CSS类触发动画(更推荐,兼容性更好)

先添加CSS样式:

.match-price {
  transition: all 0.3s ease;
}
/* 变更动画类 */
.match-price.price-updating {
  animation: priceChange 0.8s ease forwards;
}

@keyframes priceChange {
  0% {
    opacity: 1;
    background: transparent;
  }
  40% {
    opacity: 0;
    background: #fff3cd;
  }
  100% {
    opacity: 1;
    background: transparent;
  }
}

调整React逻辑,不需要维护ref数组,直接通过类名控制动画触发:

export const MatchPrices = () => {
  const markets = useStore($markets);

  useEffect(() => {
    if (markets.length === 0) return;
    // 选中所有价格单元格
    const priceEls = document.querySelectorAll(".match-price");
    priceEls.forEach(el => {
      // 先移除动画类,保证下次变更可以重复触发
      el.classList.remove("price-updating");
      // 强制重绘,确保类名移除生效后再加回
      void el.offsetWidth;
      el.classList.add("price-updating");
      // 动画结束后移除类
      setTimeout(() => el.classList.remove("price-updating"), 800);
    });
  }, [markets]);

  return (
    <Card className="price-display">
      <Table className="card-table prices-table" size="sm">
        <thead className="card-header">
          <tr>
            <th>Home</th>
            <th>Draw</th>
            <th>Away</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            {markets.length !== 0 ? (
              markets
                .filter((market) => market.mt === "MATCH_WINNER")[0]
                .selections.map((selection) => (
                  <td
                    className="match-price"
                    key={selection.id}
                  >
                    {selection.price.toFixed(2)}
                  </td>
                ))
            ) : (
              <td colSpan={3} style={{textAlign: 'center'}}>
                <Spinner
                  animation="border"
                  role="status"
                  style={{
                    marginLeft: "Auto",
                    marginRight: "Auto",
                  }}
                ></Spinner>
              </td>
            )}
          </tr>
        </tbody>
      </Table>
    </Card>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 07:51:04