React中实现Select切换时精灵图shake动画的问题求助
解决方案
问题根源
你在onChange里调用styled(Img)只是创建了一个新的Styled组件,但并没有替换页面上已渲染的<Img>元素,所以完全不会触发动画。另外,页面加载时动画触发是因为Img组件默认包含了animation样式,但切换宝可梦时没有重新触发动画的逻辑。
修复步骤
- 添加动画触发状态:在组件中新增一个状态来控制动画的启停,当切换宝可梦时触发动画,动画结束后重置状态。
- 修改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
相关产品推荐
相关产品推荐

