实现点击生成随机盒子,无剩余空间时禁用按钮功能
解决方案
核心思路
通过碰撞检测判断容器是否还能容纳最小尺寸的盒子,动态控制按钮的禁用状态,同时监听窗口尺寸变化,确保容器大小改变时状态同步更新。
修改步骤及代码实现
- 添加按钮禁用状态及容器尺寸监听
- 实现碰撞检测函数判断是否可添加新盒子
- 绑定按钮禁用属性
修改后的完整代码:
import { useState, useRef, useEffect } from "react"; import "./styles.css"; export default function App() { const [boxes, setBoxes] = useState([]); const [isButtonDisabled, setIsButtonDisabled] = useState(false); const containerRef = useRef(null); // 判断是否还能添加新盒子:尝试生成10次最小尺寸盒子的位置,检查是否与现有盒子碰撞 const canAddBox = () => { const container = containerRef.current; if (!container) return true; const containerWidth = container.clientWidth; const containerHeight = container.clientHeight; const minBoxSize = 50; // 对应生成盒子的最小尺寸 // 容器本身放不下最小盒子,直接返回false if (containerWidth < minBoxSize || containerHeight < minBoxSize) { return false; } // 尝试10次随机位置,避免单次随机位置不佳导致误判 for (let i = 0; i < 10; i++) { const top = Math.floor(Math.random() * (containerHeight - minBoxSize)); const left = Math.floor(Math.random() * (containerWidth - minBoxSize)); const newBoxRight = left + minBoxSize; const newBoxBottom = top + minBoxSize; // 检查与现有盒子是否碰撞 let hasOverlap = false; for (const box of boxes) { const boxRight = box.left + box.size; const boxBottom = box.top + box.size; // 矩形碰撞检测规则:不满足"完全分离"则为重叠 if (!(left >= boxRight || newBoxRight <= box.left || top >= boxBottom || newBoxBottom <= box.top)) { hasOverlap = true; break; } } if (!hasOverlap) return true; } // 10次尝试都找不到合适位置,判定无法添加 return false; }; // 生成随机盒子 const generateBox = () => { const container = containerRef.current; if (!container) return; const maxSize = Math.min(container.clientWidth, container.clientHeight) / 4; const size = Math.floor(Math.random() * maxSize) + 50; const top = Math.floor(Math.random() * (container.clientHeight - size)); const left = Math.floor(Math.random() * (container.clientWidth - size)); const color = `#${Math.floor(Math.random() * 16777215).toString(16)}`; setBoxes([...boxes, { size, top, left, color }]); }; // 监听盒子列表变化和窗口resize,更新按钮禁用状态 useEffect(() => { const updateButtonStatus = () => { setIsButtonDisabled(!canAddBox()); }; updateButtonStatus(); window.addEventListener("resize", updateButtonStatus); return () => window.removeEventListener("resize", updateButtonStatus); }, [boxes]); return ( <div className="App"> <button onClick={generateBox} disabled={isButtonDisabled}> 生成盒子 </button> <div ref={containerRef} className="container"> {boxes.map((box, index) => ( <div key={index} className="box" style={{ width: `${box.size}px`, height: `${box.size}px`, top: `${box.top}px`, left: `${box.left}px`, backgroundColor: box.color }} /> ))} </div> </div> ); }
关键细节说明
- 碰撞检测逻辑:通过矩形分离轴定理判断新盒子与现有盒子是否重叠,确保判断准确性。
- 多次随机尝试:避免单次随机位置刚好落在已占用区域,导致误判容器无剩余空间。
- 窗口resize监听:容器尺寸变化时重新计算可添加状态,适配窗口大小调整场景。
内容的提问来源于stack exchange,提问作者Achi
相关产品推荐
相关产品推荐

