React实现维吉尼亚密码:如何逐步骤高亮对应行列?
实现维吉尼亚密码逐步骤高亮行列的方案
要实现逐步骤高亮行列的效果,核心是把每一步的行列信息存储起来,通过定时器依次切换高亮状态。以下是具体修改方案:
1. 新增状态变量
在组件中添加几个用于管理步骤和动画的状态:
const [currentStep, setCurrentStep] = React.useState(-1); // 当前步骤索引,-1表示未开始 const [steps, setSteps] = React.useState([]); // 存储每一步的行列信息 const [isAnimating, setIsAnimating] = React.useState(false); // 标记是否正在动画,防止重复触发 const animationTimerRef = React.useRef(null); // 定时器引用,用于清理定时器
2. 重构加密按钮点击逻辑
修改handleCipherButtonClick函数,先收集所有有效步骤,再启动逐步骤动画:
const handleCipherButtonClick = () => { if (!password || !text) { setCiphered(''); alert(`შეავსეთ პაროლის და ტექსტის ველები`); return; } // 如果正在动画,先停止并重置状态 if (isAnimating) { clearTimeout(animationTimerRef.current); setIsAnimating(false); setCurrentStep(-1); setRow(null); setCol(null); return; } let k = 0; let cipheredText = ''; const stepList = []; for (let i = 0; i < text.length; i++) { if (text[i] === ' ') { cipheredText += ' '; } else { const rowIndex = georgianLetters.indexOf(text[i]); const colIndex = georgianLetters.indexOf(password[k]); if (rowIndex !== -1 && colIndex !== -1) { cipheredText += table[rowIndex][colIndex]; // 收集当前步骤的行列信息 stepList.push({ row: rowIndex, col: colIndex }); k = (k + 1) % password.length; } } } setSteps(stepList); setCurrentStep(-1); setIsAnimating(true); setCiphered(''); // 动画过程中清空加密结果,结束后再显示 // 逐步骤执行动画 const animateSteps = () => { setCurrentStep(prev => { const nextStep = prev + 1; if (nextStep >= stepList.length) { // 动画结束,显示最终加密结果 setCiphered(cipheredText); setIsAnimating(false); setRow(null); setCol(null); return -1; } // 更新当前高亮的行列 const { row: r, col: c } = stepList[nextStep]; setRow(r); setCol(c); // 继续下一个步骤,可调整间隔时间(当前为1000ms) animationTimerRef.current = setTimeout(animateSteps, 1000); return nextStep; }); }; // 启动第一个步骤 animationTimerRef.current = setTimeout(animateSteps, 0); };
3. 清理定时器(避免内存泄漏)
组件卸载时清除定时器:
React.useEffect(() => { return () => { if (animationTimerRef.current) { clearTimeout(animationTimerRef.current); } }; }, []);
4. 优化按钮交互
根据动画状态修改按钮文本,提升用户体验:
<button onClick={handleCipherButtonClick}> {isAnimating ? 'წყვეტა' : 'დაშიფვრა'} </button>
完整修改后的代码
// import { useState, useEffect, useRef } from 'react'; // import './App.css'; function VigenereCipher() { const georgianLetters = 'აბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰ'; const table = Array.from({ length: 33 }, (_, i) => Array.from({ length: 33 }, (_, j) => georgianLetters[(i + j) % 33]) ); const [password, setPassword] = React.useState(''); const [text, setText] = React.useState(''); const [ciphered, setCiphered] = React.useState(''); const [row, setRow] = React.useState(null); const [col, setCol] = React.useState(null); const [currentStep, setCurrentStep] = React.useState(-1); const [steps, setSteps] = React.useState([]); const [isAnimating, setIsAnimating] = React.useState(false); const animationTimerRef = React.useRef(null); // 组件卸载时清理定时器 React.useEffect(() => { return () => { if (animationTimerRef.current) { clearTimeout(animationTimerRef.current); } }; }, []); const handlePasswordChange = (event) => { setPassword(event.target.value); }; const handleTextChange = (event) => { setText(event.target.value); }; const handleCipherButtonClick = () => { if (!password || !text) { setCiphered(''); alert(`შეავსეთ პაროლის და ტექსტის ველები`); return; } if (isAnimating) { clearTimeout(animationTimerRef.current); setIsAnimating(false); setCurrentStep(-1); setRow(null); setCol(null); return; } let k = 0; let cipheredText = ''; const stepList = []; for (let i = 0; i < text.length; i++) { if (text[i] === ' ') { cipheredText += ' '; } else { const rowIndex = georgianLetters.indexOf(text[i]); const colIndex = georgianLetters.indexOf(password[k]); if (rowIndex !== -1 && colIndex !== -1) { cipheredText += table[rowIndex][colIndex]; stepList.push({ row: rowIndex, col: colIndex }); k = (k + 1) % password.length; } } } setSteps(stepList); setCurrentStep(-1); setIsAnimating(true); setCiphered(''); const animateSteps = () => { setCurrentStep(prev => { const nextStep = prev + 1; if (nextStep >= stepList.length) { setCiphered(cipheredText); setIsAnimating(false); setRow(null); setCol(null); return -1; } const { row: r, col: c } = stepList[nextStep]; setRow(r); setCol(c); animationTimerRef.current = setTimeout(animateSteps, 1000); return nextStep; }); }; animationTimerRef.current = setTimeout(animateSteps, 0); }; return ( <div className="main"> <table> <tbody> {georgianLetters.split('').map((letter, i) => ( <tr key={i} style={i === row ? {backgroundColor: 'red'}: {}}> {georgianLetters.split('').map((innerLetter, j) => ( <td key={j} style={{ backgroundColor: i === row && j === col ? 'purple': j === col ? 'red': 'none', }} >{table[i][j]}</td> ))} </tr> ))} </tbody> </table> <div className="right-side"> <div className='pass-text'> <label>პაროლი</label> <input type="text" value={password} onChange={handlePasswordChange} /> <label>ტექსტი</label> <textarea value={text} onChange={handleTextChange}></textarea> </div> <button onClick={handleCipherButtonClick}> {isAnimating ? 'წყვეტა' : 'დაშიფვრა'} </button> <div className="ciphered"> <textarea readOnly value={ciphered}></textarea> </div> </div> </div> ); } ReactDOM.render(<VigenereCipher/>, document.getElementById('root'));
关键说明
- 用
stepList收集所有有效步骤(跳过空格),避免空格打断动画流程 - 通过定时器逐步骤更新
row和col状态,React会自动触发重渲染更新高亮效果 - 加入
isAnimating状态防止重复触发动画,同时支持中途停止动画 - 动画过程中清空加密结果,结束后再显示,让整个加密流程更直观
内容的提问来源于stack exchange,提问作者DoLee
相关产品推荐
相关产品推荐

