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

如何使用CSS和JS创建可滚动、支持触控拖动的网格?

自适应不溢出正方形网格实现方案

核心布局逻辑(满足不溢出、正方形、行列数动态要求)

  • 父容器前置要求:给承载网格的父级div加min-w-0 min-h-0 overflow-hidden(Tailwind类),注意这一步是核心,否则网格作为flex/grid子项时会触发浏览器默认最小内容尺寸规则,直接撑破父容器原有尺寸,同时裁掉超出的内容,不会影响页面其他元素布局。
  • 网格根容器:固定取父容器可用宽、高中的较小值作为正方形边长,永远不会超出父容器边界:
    • Tailwind写法直接加类:w-auto h-auto max-w-full max-h-full aspect-square overflow-hidden relative,其中aspect-square强制1:1比例,max-w-full/max-h-full锁死尺寸不超父容器
    • 原生CSS/SCSS写法对应属性:
      .grid-container {
        max-width: 100%;
        max-height: 100%;
        aspect-ratio: 1 / 1;
        overflow: hidden;
        position: relative;
        // 可选:网格在父容器内自动居中
        margin: auto;
      }
      
  • 动态行列实现:网格内层用CSS Grid布局,行列数通过JS动态传入,每格自动均分尺寸,不需要手动计算单元格宽高:
    // rowCount/colCount替换为业务传入的实际行列数
    const gridInner = document.querySelector('.grid-inner')
    gridInner.style.gridTemplateColumns = `repeat(${colCount}, 1fr)`
    gridInner.style.gridTemplateRows = `repeat(${rowCount}, 1fr)`
    
    对应Tailwind给内层加w-full h-full grid类即可,原生CSS对应属性为display: grid; width: 100%; height: 100%;,单元格直接加边框、填充内容就会自动适配。

跨端拖拽/滑动交互实现

不需要引入第三方库,统一封装鼠标、触摸事件逻辑即可:监听按下/触摸起点坐标,拖动时实时更新内层网格偏移量,同时阻止默认的文本选中、页面误滚动等干扰行为。

let isDragging = false
let startX, startY, initialTranslateX = 0, initialTranslateY = 0
const gridContainer = document.querySelector('.grid-container')
const gridInner = document.querySelector('.grid-inner')

// 拖动开始逻辑
const dragStart = (clientX, clientY) => {
  isDragging = true
  startX = clientX
  startY = clientY
  // 读取当前已有的偏移量
  const transform = window.getComputedStyle(gridInner).transform
  if (transform !== 'none') {
    const matrixVal = transform.match(/matrix.*\((.+)\)/)[1].split(', ')
    initialTranslateX = Number(matrixVal[4])
    initialTranslateY = Number(matrixVal[5])
  }
  gridInner.style.cursor = 'grabbing'
}

// 拖动中逻辑
const dragMove = (clientX, clientY) => {
  if (!isDragging) return
  const deltaX = clientX - startX
  const deltaY = clientY - startY
  // 可在此处追加边界判断逻辑,禁止网格拖出容器可视范围
  gridInner.style.transform = `translate(${initialTranslateX + deltaX}px, ${initialTranslateY + deltaY}px)`
}

// 拖动结束逻辑
const dragEnd = () => {
  isDragging = false
  gridInner.style.cursor = 'grab'
}

// 绑定桌面端鼠标事件
gridContainer.addEventListener('mousedown', (e) => {
  e.preventDefault()
  dragStart(e.clientX, e.clientY)
})
window.addEventListener('mousemove', (e) => {
  e.preventDefault()
  dragMove(e.clientX, e.clientY)
})
window.addEventListener('mouseup', dragEnd)
gridContainer.addEventListener('mouseleave', dragEnd)

// 绑定触控端触摸事件
gridContainer.addEventListener('touchstart', (e) => {
  const touch = e.touches[0]
  dragStart(touch.clientX, touch.clientY)
}, { passive: true })
gridContainer.addEventListener('touchmove', (e) => {
  const touch = e.touches[0]
  dragMove(touch.clientX, touch.clientY)
}, { passive: true })
gridContainer.addEventListener('touchend', dragEnd)
  • 交互优化点:鼠标事件中加e.preventDefault()阻止默认文本选中行为,触摸事件加passive: true配置提升滑动流畅度,拖动事件绑定在window上避免鼠标移出容器后拖动状态丢失。

完整结构示例(Tailwind版)

<!-- 父容器,任意尺寸都可,网格不会撑破其原有尺寸 -->
<div class="w-[80vw] h-[60vh] p-4 border flex items-center justify-center min-w-0 min-h-0 overflow-hidden">
  <!-- 网格根容器,永远保持正方形,不超出父容器可用空间 -->
  <div class="grid-container w-auto h-auto max-w-full max-h-full aspect-square overflow-hidden cursor-grab select-none">
    <!-- 网格内层,动态生成行列和单元格 -->
    <div class="grid-inner w-full h-full grid">
      <!-- 单元格示例,实际使用时动态生成,总数量=rowCount*colCount -->
      <div class="border border-gray-300"></div>
      <div class="border border-gray-300"></div>
    </div>
  </div>
</div>

如果需要适配SCSS/原生CSS,把上述类对应的样式拆出编写即可,核心逻辑完全一致。如果需要网格内容超出后支持滚动查看,只要把网格根容器的overflow-hidden改为overflow-auto,将拖动逻辑改为修改容器scrollLeft/scrollTop属性即可,不需要调整核心布局规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:21:28