如何在React中动态修改已有元素的className
记忆游戏答题正误反馈:动态修改元素类名实现闪色效果
问题描述
我正在开发一款简易记忆游戏,希望通过修改已有元素的className,使其在玩家猜对答案时闪绿色、猜错时闪红色,目前仅能在控制台打印'CORRECT'和'WRONG'。
现有代码
渲染需变色元素的函数
const renderMajorSegment = (musicKey, index) => { let arc = calculateArc(outerRadius, innerRadius, musicKey.segmentMetadata.startAngle, musicKey.segmentMetadata.endAngle); let [arcCenterX, arcCenterY] = arc.centroid(); return <g className={`circle-segment ${musicKey.segmentMetadata.majorCircle.isVisible ? 'isVisible': ''}`} key={index}> <path d={arc.apply()} // apply() is needed to generate the string that goes into the 'd' attribute /> <text x={arcCenterX} y={arcCenterY} transform={`rotate(15, ${arcCenterX}, ${arcCenterY})`}> {abbreviateKeyFromChord(musicKey.chords[0])} </text> </g> }
函数组件核心部分
return ( <div className='circle-of-fifths-container'> <svg height={diameter*1.1} width={diameter*1.1}> <g className='circle-container' transform={`translate(${diameter/2 + 25},${diameter/2 + 25}) rotate(-15)`}> <circle r={outerRadius} className='base-circle'/> <g className='outer-circle-segments-container'> {musicKeysObject.map((musicKey, index) => renderMajorSegment(musicKey, index))} </g> <g className='inner-circle-segments-container'> {musicKeysObject.map((musicKey, index) => renderMinorSegment(musicKey, index))} </g> {mode === READY && <g id='play-button-container' onMouseUp={startRound}> <circle r={innerRadius2} id='play-button'/> <text id='play-button-text' transform='rotate(15)'>PLAY GAME</text> </g>} </g> </svg> {mode === PLAYING && <input placeholder='Your answer' onKeyDown={handleUserInput}></input> } </div> )
处理用户输入的逻辑函数
const handleUserInput = (event) => { var index; if (hiddenSegment.major) { index = 0; } else { index = 5; } if (event.key === 'Enter') { if (event.target.value === abbreviateKeyFromChord(hiddenSegment.chords[index])) { console.log('CORRECT'); // Set both major and minor circles to true // 1 will already be true so it makes no difference, sets the non-visible one to true hiddenSegment.segmentMetadata.majorCircle.isVisible = true; hiddenSegment.segmentMetadata.minorCircle.isVisible = true; // Modify the css classes to make the segments go green startRound(); } else { // Modify the css classes to make the segments go red console.log('WRONG'); } } };
我的思路
我梳理了两种可能的实现方向:
- 给
renderMajorSegment()添加参数,根据正误状态字符串修改className; - 通过React状态存储正误标识,参考现有
isVisible的用法动态修改类名。
解决方案
推荐使用React状态管理的方案,更符合React数据流模式,避免直接修改数据对象的不可控问题。具体步骤如下:
1. 添加状态存储答题反馈
在组件中新增状态,记录当前答题的正误状态和对应的目标片段索引:
const [feedback, setFeedback] = useState({ segmentIndex: null, isCorrect: null });
2. 修改渲染函数,动态添加反馈类名
更新renderMajorSegment,根据状态判断是否添加闪色类名(renderMinorSegment做相同修改即可):
const renderMajorSegment = (musicKey, index) => { let arc = calculateArc(outerRadius, innerRadius, musicKey.segmentMetadata.startAngle, musicKey.segmentMetadata.endAngle); let [arcCenterX, arcCenterY] = arc.centroid(); // 根据当前反馈状态判断是否添加闪色类 const feedbackClass = feedback.segmentIndex === index ? feedback.isCorrect ? 'flash-green' : 'flash-red' : ''; return <g className={`circle-segment ${musicKey.segmentMetadata.majorCircle.isVisible ? 'isVisible': ''} ${feedbackClass}`} key={index}> <path d={arc.apply()} /> <text x={arcCenterX} y={arcCenterY} transform={`rotate(15, ${arcCenterX}, ${arcCenterY})`}> {abbreviateKeyFromChord(musicKey.chords[0])} </text> </g> }
3. 更新输入处理函数,设置并自动清除反馈状态
修改handleUserInput,设置反馈状态后自动清除,实现闪烁效果:
const handleUserInput = (event) => { var index; if (hiddenSegment.major) { index = 0; } else { index = 5; } // 找到当前hiddenSegment在musicKeysObject中的索引 const segmentIndex = musicKeysObject.findIndex(key => key === hiddenSegment); if (event.key === 'Enter') { const isCorrect = event.target.value === abbreviateKeyFromChord(hiddenSegment.chords[index]); if (isCorrect) { console.log('CORRECT'); hiddenSegment.segmentMetadata.majorCircle.isVisible = true; hiddenSegment.segmentMetadata.minorCircle.isVisible = true; } else { console.log('WRONG'); } // 设置反馈状态,触发视图更新 setFeedback({ segmentIndex, isCorrect }); // 1秒后清除反馈,结束闪烁;答对则进入下一轮 setTimeout(() => { setFeedback({ segmentIndex: null, isCorrect: null }); if (isCorrect) startRound(); }, 1000); // 清空输入框 event.target.value = ''; } };
4. 添加CSS闪烁动画
在样式文件中定义闪色动画:
@keyframes flash-green { 0% { fill: #2ecc71; } 50% { fill: initial; } 100% { fill: #2ecc71; } } @keyframes flash-red { 0% { fill: #e74c3c; } 50% { fill: initial; } 100% { fill: #e74c3c; } } .flash-green path { animation: flash-green 1s ease-in-out; } .flash-red path { animation: flash-red 1s ease-in-out; }
方案说明
- 采用React状态管理反馈信息,符合单向数据流原则,避免直接修改数据导致的视图不更新问题;
- 通过
setTimeout自动清除状态,实现自动结束闪烁的效果; - CSS动画实现闪烁,性能优于JavaScript操作样式;
- 同时支持major和minor片段的反馈,只需同步修改
renderMinorSegment即可。
内容的提问来源于stack exchange,提问作者Mo0rBy
相关产品推荐
相关产品推荐

