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

React中如何根据下拉框选中值触发对应函数?

解决React下拉框选中值触发对应球员绘制函数的问题

核心思路

先维护好下拉框选中的球员类型状态,再把不同球员的绘制逻辑封装成独立函数,用映射对象将球员类型和对应绘制函数关联起来。当画布被点击时,根据当前选中的球员类型调用对应的绘制函数即可。

完整实现代码

import { useState, useRef, useEffect } from 'react';

const TacticalDraw = () => {
  // 维护选中的球员类型状态
  const [selectedPlayer, setSelectedPlayer] = useState("Player 1");
  const [players, setPlayers] = useState([]);
  const [addingPlayer, setAddingPlayer] = useState(false);
  const canvasRef = useRef(null);

  // 下拉框变更事件:更新选中的球员类型
  const handlePlayerChange = (e) => {
    setSelectedPlayer(e.target.value);
  };

  // 不同球员的绘制逻辑函数
  const drawPlayer1 = (ctx, x, y) => {
    // Player 1:红色圆形球员标记
    ctx.fillStyle = "#ff0000";
    ctx.beginPath();
    ctx.arc(x, y, 15, 0, Math.PI * 2);
    ctx.fill();
  };

  const drawPlayer2 = (ctx, x, y) => {
    // Player 2:蓝色方形球员标记
    ctx.fillStyle = "#0000ff";
    ctx.fillRect(x - 15, y - 15, 30, 30);
  };

  const drawPlayer3 = (ctx, x, y) => {
    // Player 3:绿色三角形球员标记
    ctx.fillStyle = "#00ff00";
    ctx.beginPath();
    ctx.moveTo(x, y - 15);
    ctx.lineTo(x + 15, y + 15);
    ctx.lineTo(x - 15, y + 15);
    ctx.closePath();
    ctx.fill();
  };

  // 球员类型与绘制函数的映射表
  const playerDrawMap = {
    "Player 1": drawPlayer1,
    "Player 2": drawPlayer2,
    "Player 3": drawPlayer3
  };

  // 画布点击事件:处理球员添加与绘制
  const handleCanvasClick = (event) => {
    if (!addingPlayer || !canvasRef.current) return;

    const rect = canvasRef.current.getBoundingClientRect();
    const x = event.clientX - rect.left;
    const y = event.clientY - rect.top;
    const ctx = canvasRef.current.getContext("2d");

    // 创建新球员数据
    const newPlayer = { 
      x, 
      y, 
      id: players.length + 1, 
      playerType: selectedPlayer 
    };

    // 调用对应球员的绘制函数
    const drawFunc = playerDrawMap[selectedPlayer];
    drawFunc && drawFunc(ctx, x, y);

    // 更新球员列表状态
    setPlayers(prev => [...prev, newPlayer]);
  };

  // 监听球员列表变化,重绘所有球员
  useEffect(() => {
    if (!canvasRef.current) return;
    const ctx = canvasRef.current.getContext("2d");
    // 清空画布
    ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
    // 重绘所有已添加的球员
    players.forEach(player => {
      const drawFunc = playerDrawMap[player.playerType];
      drawFunc && drawFunc(ctx, player.x, player.y);
    });
  }, [players]);

  return (
    <div>
      {/* 球员选择下拉框 */}
      <div>
        <label>Players: </label>
        <select value={selectedPlayer} onChange={handlePlayerChange}>
          <option value="Player 1">Player 1</option>
          <option value="Player 2">Player 2</option>
          <option value="Player 3">Player 3</option>
        </select>
        {/* 切换添加球员状态的按钮 */}
        <button onClick={() => setAddingPlayer(prev => !prev)}>
          {addingPlayer ? "停止添加球员" : "开始添加球员"}
        </button>
      </div>
      {/* 战术画布 */}
      <canvas 
        ref={canvasRef} 
        width={800} 
        height={600} 
        style={{ border: "1px solid #333", marginTop: "10px" }}
        onClick={handleCanvasClick}
      />
    </div>
  );
};

export default TacticalDraw;

关键细节说明

  1. 状态管理:selectedPlayer存储当前选中的球员类型,handlePlayerChange负责同步下拉框选中值到状态中。
  2. 映射表优势:用playerDrawMap替代繁琐的if-else判断,后续新增球员类型时,只需添加对应的绘制函数和映射项即可,扩展性更强。
  3. 画布交互:点击画布时,先计算相对画布的坐标,再根据当前选中的球员类型调用对应绘制函数,同时将新球员数据加入状态。
  4. 重绘同步:通过useEffect监听players状态变化,每次状态更新时清空画布并重绘所有球员,保证画布内容与状态完全同步。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 16:34:57