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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 02:47:07