如何用最少JS实现符合规则的4x4网格项随机化布局
4x4网格随机布局实现(无空单元格、符合尺寸规则)
需求说明
需要用尽可能少的JavaScript,在4×4网格中随机生成网格项的数量、尺寸和位置,同时满足以下规则:
- 网格项的宽度或高度不得小于对应维度的50%(即在4列网格中宽度至少占2列,或4行网格中高度至少占2行)
- 网格项的面积不得超过9个单元格
- 网格必须被完全填充,无空单元格
我之前找到的HTML/CSS示例只能预定义网格项,且存在空单元格,无法满足需求:
<div class="shuffleWrap"> <button class="shuffle">Shuffle order of boxes</button> </div> <div class="grid"> <div class="box red square"></div> <div class="box red square"></div> <div class="box green square"></div> <div class="box green square"></div> <div class="box blue square"></div> <div class="box red wide"></div> <div class="box green wide"></div> <div class="box blue wide"></div> <div class="box green tall"></div> <div class="box blue tall"></div> </div>
.grid { width: 900px; max-width: 100%; display: grid; margin: 0 auto; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); grid-auto-rows: 1fr; grid-auto-flow: row dense; grid-gap: 10px; counter-reset: boxes; } .tall { grid-column: span 1; grid-row: span 2; } .wide { grid-row: span 1; grid-column: span 2; } .square { grid-row: span 1; grid-column: span 1; } @media(max-width: 500px) { .grid { grid-template-columns: 1fr; } .wide { grid-column: span 1; } } .box { position: relative; font-family: sans-serif; font-size: 2rem; color: rgba(255,255,255,.7); counter-increment: boxes; &::before { content: ""; width: 100%; padding-top: 100%; display: block; } &::after { content: counter(boxes); width: 100%; height: 100%; position: absolute; top: 0; left: 0; display: flex; justify-content: center; align-items: center; } } .blue { background: #4040e7; border: 5px solid rgba(0,0,0,.5); } .red { background: #db3c3c; border: 5px solid rgba(0,0,0,.5); } .green { background: #3ddb3d; border: 5px solid rgba(0,0,0,.5); } .shuffleWrap { padding: 20px; display: flex; justify-content: center; }
const button = document.querySelector(".shuffle"), grid = document.querySelector(".grid") if (button && grid) { button.addEventListener("click", () => { for (var i = grid.children.length; i >= 0; i--) { grid.appendChild(grid.children[Math.random() * i | 0]) } }) }
解决方案
可以通过简单的JavaScript算法动态生成符合规则的布局,步骤如下:
算法思路
- 用二维数组标记4×4网格的单元格占用状态,初始全为未占用
- 循环生成网格项:
- 从随机的未占用单元格开始
- 随机选择符合规则的尺寸(可选:2×2、2×3、3×2、3×3、2×4、4×2)
- 检查该尺寸是否能放入当前位置(不超出网格边界,且覆盖的单元格均未被占用)
- 若符合条件,标记对应单元格为已占用,记录该网格项的位置和尺寸
- 直到所有单元格被占用,停止循环
- 根据记录的网格项数据,动态创建DOM元素并添加到网格容器中
完整代码
<div class="shuffleWrap"> <button class="shuffle">生成随机布局</button> </div> <div class="grid"></div>
.grid { width: 900px; max-width: 100%; display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(4, 1fr); margin: 0 auto; grid-gap: 10px; counter-reset: boxes; } .box { position: relative; font-family: sans-serif; font-size: 2rem; color: rgba(255,255,255,.7); counter-increment: boxes; border: 5px solid rgba(0,0,0,.5); } .box::before { content: ""; width: 100%; padding-top: 100%; display: block; } .box::after { content: counter(boxes); width: 100%; height: 100%; position: absolute; top: 0; left: 0; display: flex; justify-content: center; align-items: center; } .blue { background: #4040e7; } .red { background: #db3c3c; } .green { background: #3ddb3d; } .shuffleWrap { padding: 20px; display: flex; justify-content: center; } @media(max-width: 500px) { .grid { grid-template-columns: 1fr; grid-template-rows: auto; } }
const button = document.querySelector(".shuffle"), grid = document.querySelector(".grid"); // 可选尺寸:符合宽度/高度≥50%、面积≤9的规则 const validSizes = [[2,2], [2,3], [3,2], [3,3], [2,4], [4,2]]; const colors = ['red', 'green', 'blue']; // 初始化网格占用状态 function initGrid() { return Array.from({length:4}, () => Array(4).fill(false)); } // 检查尺寸是否能放入指定位置 function canPlace(gridState, row, col, size) { const [rows, cols] = size; if (row + rows > 4 || col + cols > 4) return false; for (let i = row; i < row + rows; i++) { for (let j = col; j < col + cols; j++) { if (gridState[i][j]) return false; } } return true; } // 标记单元格为已占用 function markOccupied(gridState, row, col, size) { const [rows, cols] = size; for (let i = row; i < row + rows; i++) { for (let j = col; j < col + cols; j++) { gridState[i][j] = true; } } } // 生成随机布局 function generateLayout() { const gridState = initGrid(); const items = []; while (gridState.flat().some(cell => !cell)) { // 找一个未占用的随机单元格 let row, col; do { row = Math.floor(Math.random() * 4); col = Math.floor(Math.random() * 4); } while (gridState[row][col]); // 随机选尺寸,直到找到能放下的 let size; do { size = validSizes[Math.floor(Math.random() * validSizes.length)]; } while (!canPlace(gridState, row, col, size)); // 标记占用并记录项 markOccupied(gridState, row, col, size); items.push({ row: row + 1, // grid行/列从1开始 col: col + 1, spanRow: size[0], spanCol: size[1], color: colors[Math.floor(Math.random() * colors.length)] }); } return items; } // 渲染布局 function renderLayout(items) { grid.innerHTML = ''; items.forEach(item => { const box = document.createElement('div'); box.classList.add('box', item.color); box.style.gridRow = `${item.row} / span ${item.spanRow}`; box.style.gridColumn = `${item.col} / span ${item.spanCol}`; grid.appendChild(box); }); } // 绑定按钮事件 if (button && grid) { button.addEventListener('click', () => { const layout = generateLayout(); renderLayout(layout); }); // 初始渲染一次 renderLayout(generateLayout()); }
内容的提问来源于stack exchange,提问作者oldboy
相关产品推荐
相关产品推荐

