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

如何用最少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算法动态生成符合规则的布局,步骤如下:

算法思路

  1. 用二维数组标记4×4网格的单元格占用状态,初始全为未占用
  2. 循环生成网格项:
    • 从随机的未占用单元格开始
    • 随机选择符合规则的尺寸(可选:2×2、2×3、3×2、3×3、2×4、4×2)
    • 检查该尺寸是否能放入当前位置(不超出网格边界,且覆盖的单元格均未被占用)
    • 若符合条件,标记对应单元格为已占用,记录该网格项的位置和尺寸
  3. 直到所有单元格被占用,停止循环
  4. 根据记录的网格项数据,动态创建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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 04:45:54