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

React useState控制点击颜色正反keyframes动画 解决首次加载自动触发问题

问题根因

首屏加载时activeDiv初始值为false,代码会直接给::after伪元素绑定unsetColor反向动画,浏览器渲染元素时检测到animation属性存在有效值,会默认立即执行一次动画,这就是未点击就自动触发回退动画的核心原因。
之前尝试animation-play-state没有达到预期,是因为初始状态下动画本身就被直接绑定,默认状态就是running,单纯修改播放状态解决不了「绑定即执行」的逻辑问题。另外原代码中transform-origin: bottom center};末尾多了冗余右花括号,属于语法错误,也会干扰样式正常解析。

实现方案

方案1:最小改动修复原有Keyframes逻辑

核心思路是新增交互标记,避免首次加载时就给元素绑定反向动画,同时补全动画填充模式保证动画结束后状态不跳变:

import { useState } from 'react';
import styled, { css, keyframes } from 'styled-components';

export default function ClickAnimDiv() {
  const [activeDiv, setDivState] = useState(false);
  // 标记是否发生过用户点击,拦截初始动画
  const [hasInteracted, setHasInteracted] = useState(false);

  const setColor = (colorTo) => keyframes` 
    0% {transform: scaleY(0); }
    100% {transform: scaleY(1); background-color: ${colorTo}} 
  `;
  const unsetColor = (colorFrom) => keyframes`
    0% {transform: scaleY(1); background-color: ${colorFrom}}
    100% {transform: scaleY(0); background-color: transparent;}
  `;

  const BigDiv = styled("div")(
    ({ theme, activeDiv }) => css`
      min-width: 220px;
      height: 100%;
      position: relative;
      padding: ${theme?.spacing?.(2, 0) || '16px 0'};
      border-bottom: red 4px solid;
      background-color: transparent;
      cursor: pointer;
      text-align: center;
      will-change: transform;

      ::after {
        content: '';
        position: absolute;
        bottom: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: transparent;
        z-index: 0;
        /* 首次未交互时不绑定任何动画,交互后根据状态切换对应动画 */
        animation: ${
          !hasInteracted 
            ? 'none' 
            : activeDiv 
              ? `${setColor("red")} 0.5s ease forwards` 
              : `${unsetColor("red")} 0.5s ease forwards`
        };
        transform-origin: bottom center;
      }
    `
  );

  const SecondDiv = styled("div")(
    () => css`
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      text-align: center;
      position: relative;
      z-index: 1;
    `
  );

  return (
    <BigDiv
      activeDiv={activeDiv}
      onClick={() => {
        if (!hasInteracted) setHasInteracted(true);
        setDivState(!activeDiv);
      }}
    >
      <SecondDiv>
        <p>Click to animate</p>
      </SecondDiv>
    </BigDiv>
  );
}

方案2:Transition实现(更推荐的最优方案)

这类双向状态切换的缩放+变色效果,不需要编写两套Keyframes,用CSS transition 就能实现更流畅稳定的效果,从根源上避免初始动画误触发的问题,代码也更易维护:

import { useState } from 'react';
import styled, { css } from 'styled-components';

export default function ClickAnimDiv() {
  const [activeDiv, setDivState] = useState(false);

  const BigDiv = styled("div")(
    ({ theme, activeDiv }) => css`
      min-width: 220px;
      height: 100%;
      position: relative;
      padding: ${theme?.spacing?.(2, 0) || '16px 0'};
      border-bottom: red 4px solid;
      background-color: transparent;
      cursor: pointer;
      text-align: center;

      ::after {
        content: '';
        position: absolute;
        bottom: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 0;
        /* 直接通过状态控制最终样式 */
        background-color: ${activeDiv ? "red" : "transparent"};
        transform: scaleY(${activeDiv ? 1 : 0});
        transform-origin: bottom center;
        /* 统一设置过渡效果,自动处理双向动画 */
        transition: all 0.5s ease;
      }
    `
  );

  const SecondDiv = styled("div")(
    () => css`
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      text-align: center;
      position: relative;
      z-index: 1;
    `
  );

  return (
    <BigDiv
      activeDiv={activeDiv}
      onClick={() => setDivState(!activeDiv)}
    >
      <SecondDiv>
        <p>Click to animate</p>
      </SecondDiv>
    </BigDiv>
  );
}

该方案不需要额外的交互标记状态,初始状态下scaleY(0)+透明背景本身就是预期的初始样式,点击切换状态时transition会自动识别属性变化生成平滑过渡,不会出现首屏自动执行动画的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 18:15:44