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

React中实现Select切换时精灵图shake动画的问题求助

解决方案

问题根源

你在onChange里调用styled(Img)只是创建了一个新的Styled组件,但并没有替换页面上已渲染的<Img>元素,所以完全不会触发动画。另外,页面加载时动画触发是因为Img组件默认包含了animation样式,但切换宝可梦时没有重新触发动画的逻辑。

修复步骤

  1. 添加动画触发状态:在组件中新增一个状态来控制动画的启停,当切换宝可梦时触发动画,动画结束后重置状态。
  2. 修改Styled组件支持动态动画:让Img组件根据传入的props决定是否应用抖动动画。

修改后的代码

Select.ts

import { closest } from "fastest-levenshtein";
import React, { useState, useEffect } from "react";
import { cries, pokemons } from "../data";
import styled from "styled-components";
import { Img, ani } from "../styles";

function Select() {
  const [src, setSrc] = useState(require("../data/sprites/bulbasaur.png"));
  const [isShaking, setIsShaking] = useState(false); // 新增动画控制状态

  // 用map简化选项生成逻辑
  const opts: { label: string; value: string }[] = pokemons.map(pokemon => ({
    label: pokemon[0].toUpperCase() + pokemon.slice(1),
    value: pokemon,
  }));

  const handlePokemonChange = (evt: React.ChangeEvent<HTMLSelectElement>) => {
    const selectedPokemon = evt.target.value;
    // 更新精灵图地址
    setSrc(require(`../data/sprites/${selectedPokemon}.png`));
    // 播放对应音效
    const str = closest(selectedPokemon, cries);
    const res = new Audio(require(`../data/cries/${str}`));
    res.volume = 0.2;
    res.play();
    // 触发抖动动画
    setIsShaking(true);
  };

  // 动画结束后重置状态,保证下次切换能再次触发动画
  useEffect(() => {
    if (isShaking) {
      const timer = setTimeout(() => setIsShaking(false), 300); // 时长和动画定义的0.3s一致
      return () => clearTimeout(timer);
    }
  }, [isShaking]);

  return (
    <div>
      <div>
        <select onChange={handlePokemonChange}>
          {opts.map(({ label, value }) => (
            <option key={value} label={label} value={value}></option>
          ))}
        </select>
      </div>
      {/* 传入状态props控制动画 */}
      <Img src={src} alt="" className="sprite" isShaking={isShaking} />
    </div>
  );
}

export default Select;

img.styled.ts

import styled, { css } from "styled-components";
import { shake } from "./shake.stiled";

const ani = css(["", " 0.3s linear"] as any as TemplateStringsArray, shake);

// 添加props类型定义,支持动态控制动画
const Img = styled.img<{ isShaking: boolean }>`
  margin-left: 2.5em;
  // 根据isShaking状态决定是否应用抖动动画
  ${props => props.isShaking && css`
    animation: ${ani};
  `}
`;

export { Img, ani };

额外优化说明

  • 将原循环生成选项的逻辑替换为map,更符合React的函数式编程风格
  • 提取handlePokemonChange独立函数,让代码结构更清晰
  • 用useEffect配合定时器自动重置动画状态,确保每次切换宝可梦都能触发一次抖动动画

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 12:10:35