- 前端
- UI组件
【免费下载链接】vue-grid-layout
A draggable and resizable grid layout, for Vue.js.
导读:本文以 vue-grid-layout 官方中文更新日志 website/docs/zh/changelog/README.md 为骨架,逐版本梳理从 2017 年 2.1.0 到 2022 年 2.4.0 的功能演进脉络,并对照 GridLayout 源码 与 GridItem 源码 讲解每项能力的底层实现。读完本文,你将理解响应式布局、碰撞防止、RTL、缩放拖拽等关键特性的来龙去脉,并能在实际项目中正确使用这些配置项。
一、版本脉络总览
本仓库当前版本为 2.4.0(见 package.json),@interactjs系列依赖固定在 1.10.2。更新日志记录了一条清晰的演进主线:
- 2017(2.1.0 ~ 2.1.13):奠定交互基础——RTL 支持、拖拽/缩放事件、多实例、动态列数;
- 2018(2.2.0 ~ 2.3.3):工程化重构(Vue CLI)、响应式布局、SSR 支持、静态元素;
- 2019(2.3.4 ~ 2.3.7):生命周期事件完善、防止碰撞、容器尺寸监听;
- 2020(2.3.8 ~ 2.3.12):交互配置精细化——响应式断点事件、样式光标开关、宽高比锁定;
- 2022(2.4.0):缩放场景支持(transformScale)、拖拽边界(isBounded)、restoreOnDrag。
需要说明:中文更新日志中的2.4.0 条目内容为 "TODO" 占位符,英文版 website/docs/changelog/README.md 则完整记录了该版本的实际变更。本文的 2.4.0 部分以英文版与源码为准进行补全。
二、2.1.x:交互事件与多实例基础(2017)
2.1.0 引入RTL 支持与移动/缩放事件,是后续一切交互能力的起点。事件体系至今仍是使用该组件的核心方式:
- GridItem 级事件:
move(移动中)、resize(缩放中)、moved(移动结束)、resized(缩放结束)、container-resized(容器尺寸变化),见 事件文档; - GridLayout 级事件:
layout-created、layout-before-mount、layout-mounted、layout-ready、layout-updated、breakpoint-changed。
从源码看,事件流动依赖一个Vue实例作为事件总线:GridLayout在created钩子中创建总线并订阅resizeEvent/dragEvent,而GridItem在handleDrag/handleResize中通过总线上报坐标(GridLayout.vue、GridItem.vue)。
2.1.4 实现同页多栅格实例,2.1.10 为此引入isMirrored配置,允许从右向左渲染栅格元素(水平翻转)。2.1.13 新增动态列数(colNum)支持——这在源码中体现为colNum的 watcher,通过事件总线向所有 GridItem 广播setColNum(GridLayout.vue)。
实战提示:2.1.13 起,
resize事件携带像素尺寸。事件回调签名为(i, newH, newW, newHPx, newWPx),分别对应栅格单位与像素单位的宽高(见 事件文档)。
三、2.2.0 ~ 2.3.3:工程化重构与响应式布局(2018)
2.2.0 是一次结构里程碑:项目改用Vue CLI 构建,新增autoSize()方法(需在子组件中通过this.$parent.autoSize()调用,GridItem.vue),并将 DOM 相关调用抽象化以支持SSR。GridItem.i同时支持数字与字符串,源码中的validateLayout也据此校验i的类型必须为字符串或数字且唯一(utils.js)。
2.3.0 引入响应式布局,这是中大型仪表盘场景的杀手锏。其核心机制在 responsiveUtils.js:
getBreakpointFromWidth:按当前容器宽度匹配最高断点;getColsFromBreakpoint:取该断点的列数;findOrGenerateResponsiveLayout:优先复用已保存的该断点布局,否则从更高断点的布局经correctBounds+compact自动生成。
配合三个配置项使用:
breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }, cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }responsiveLayouts则用于为各断点预定义初始布局。当断点切换时,组件会通过update:layout同步新的布局数组(GridLayout.vue)。
2.3.3 / 2.3.2 将vue作为 webpackexternals处理以修复 UMD 加载问题,属于构建层面的修复,无需业务改动。
四、2.3.4 ~ 2.3.7:静态元素、生命周期事件与防止碰撞(2019)
2.3.4 引入静态元素(static)与GridLayout 四个生命周期事件。静态元素的语义在 properties.md 中定义为"不可拖拽、不可缩放、也不会被其他元素推开"。其在算法中的实现非常关键:compact时静态元素直接进入比较数组,非静态元素只能围绕它们排布(utils.js);moveElement遇到碰撞时若对方是静态元素,则移动当前元素而不是对方(utils.js)。
四个生命周期事件对应 GridLayout 的钩子调用(GridLayout.vue):
| 事件 | 触发时机 |
|---|---|
layout-created | created钩子 |
layout-before-mount | beforeMount钩子 |
layout-mounted | mounted钩子 |
layout-ready | 首次宽度测量完成、GridItem 尺寸就绪后的 nextTick |
2.3.5 引入防止碰撞(preventCollision)与min/max 宽高 watcher。preventCollision为true时,拖拽目标位置与其他元素碰撞则回退到原位置(moveElement中if (preventCollision && collisions.length)分支,utils.js);缩放时则自动收缩到不碰撞的最大尺寸(GridLayout.vue)。同时新增minH/minW/maxH/maxW的响应式监听(GridItem.vue),并在交互层面通过restrictSize交给 interact.js 强制约束。
2.3.7 将 2.3.6 回滚的w/h舍入变更正式重命名了容器变化引发的resized事件:当浏览器窗口或父容器改变尺寸导致栅格项尺寸变化时,也会触发resized与container-resized(前者由 GridItem.vue 的emitContainerResized发出)。
五、2.3.8 ~ 2.3.12:交互配置精细化(2020)
这一阶段密集修复了响应式与交互细节,并新增多个实用配置:
- 2.3.8:
margin支持动态修改(源码中通过$parent.marginwatcher 响应,GridItem.vue);补全layout-updated事件;支持初始响应式布局与断点变更事件(即breakpoint-changed);修复响应式模式下的排序与隐藏/显示尺寸问题。 - 2.3.10:interact.js 升级至 1.10.0 并以 ES6 模块按需导入(
package.json中的@interactjs/*系列依赖即为按需模块);新增useStyleCursor属性——该属性为false时调用interactObj.styleCursor(false)禁用动态光标样式,可在浏览器拖动卡顿场景下缓解问题(GridItem.vue);修复 RTL 模式缩放失效。 - 2.3.11:修复响应式布局回归(#487)。
- 2.3.12:interact.js 升至 1.10.2(即当前锁定版本);新增
preserveAspectRatio属性,为true时通过 interact.js 的aspectRatiomodifier 保持缩放宽高比(GridItem.vue)。
实战提示:
useStyleCursor是非响应式属性(properties.md),需在初始化时设定。
六、2.4.0:缩放场景与拖拽边界(2022)
英文更新日志记录了 2.4.0 的六项变更(中文版此处为 TODO 占位),全部在源码中可找到对应实现:
1.transformScale:适配被 CSStransform: scale()缩放的外层容器
在 Dashboard 嵌套、预览缩放等场景下,外层容器缩放会导致 interact.js 计算的位移与实际视觉位移不一致。transformScale在 GridLayout.vue 中定义为默认 1 的Number。GridItem 在计算拖拽/缩放增量时统一除以该系数:
newPosition.left = this.dragging.left + coreEvent.deltaX / this.transformScale; newPosition.top = this.dragging.top + coreEvent.deltaY / this.transformScale;(见 GridItem.vue,缩放逻辑同理见 L566-L571。)即外层容器放大 2 倍时设:transform-scale="2",鼠标位移除以 2 后映射回真实栅格坐标,同时dragstart读取边界矩形时也除以系数(L642-L654)。该属性可通过事件总线setTransformScale动态更新。
2.isBounded:限制拖拽不越出父容器
isBounded为true时,dragmove计算后会将新位置 clamp 在父容器边界内(GridItem.vue):
const bottomBoundary = event.target.offsetParent.clientHeight - 自身像素高度; const rightBoundary = this.containerWidth - 自身像素宽度; newPosition.top = this.clamp(newPosition.top, 0, bottomBoundary); newPosition.left = this.clamp(newPosition.left, 0, rightBoundary);GridLayout 与 GridItem 均支持该属性,GridItem 未显式设置时继承父级(默认null表示继承,见 properties.md)。
3.restoreOnDrag:非紧凑模式下减少拖拽产生的空隙
当verticalCompact: false时,普通拖动会把重叠元素往下推、留下空隙。restoreOnDrag通过positionsBeforeDrag记录拖拽前各元素位置,拖动过程中以该记录为下限压缩布局(GridLayout.vue),避免元素被意外挤离原始位置。这在自由排布(非紧凑)的看板布局中尤其有用。
4. 其余修复与加固
dragOption/resizeOption:GridItem 新增 interact.js draggable/resizable 配置透传对象(GridItem.vue),可注入自定义 modifier、限制等;- 响应式功能的调用顺序调整(
responsiveGridLayout先于 compact 执行,见 GridLayout.vue); - 增加单元测试与
layoutkey 校验强化(validateLayout校验i唯一性、static布尔类型等,utils.js); - 修复
autoSize()后元素重叠问题。
七、将这些特性串起来:一个完整示例
结合上述演进成果,一个覆盖"响应式 + 防止碰撞 + 静态元素 + 边界约束"的典型配置如下:
<grid-layout :layout="layout" :col-num="12" :row-height="30" :is-draggable="true" :is-resizable="true" :vertical-compact="true" :responsive="true" :responsive-layouts="responsiveLayouts" :breakpoints="{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }" :cols="{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }" :prevent-collision="true" @layout-ready="onLayoutReady" @layout-updated="onLayoutUpdated" @breakpoint-changed="onBreakpointChanged"> <grid-item v-for="item in layout" :x="item.x" :y="item.y" :w="item.w" :h="item.h" :i="item.i" :key="item.i" :static="item.static" @move="onMove" @resize="onResize" @moved="onMoved" @resized="onResized"> {{item.i}} </grid-item> </grid-layout>export default { data() { return { layout: [ { x: 0, y: 0, w: 6, h: 3, i: 'a' }, { x: 6, y: 0, w: 6, h: 3, i: 'b', static: true }, // 静态:不参与拖拽且不被推开 { x: 0, y: 3, w: 4, h: 3, i: 'c', minW: 2, maxH: 4 } ], responsiveLayouts: { lg: [{ x: 0, y: 0, w: 6, h: 3, i: 'a' }, /* ... */], md: [{ x: 0, y: 0, w: 5, h: 3, i: 'a' }, /* ... */] } }; } };八、后续查阅指引
- 全部属性定义与默认值:英文版 properties.md 及中文版 properties.md;
- 事件签名与回调示例:events.md;
- 布局算法(compact、moveElement、correctBounds、碰撞检测):utils.js;
- 响应式断点匹配与布局生成:responsiveUtils.js;
- 单元测试:
test/unit/下的 GridItem.spec.js 与 utils.spec.js 验证了布局算法与组件行为。
版本前提说明:文中所有配置与行为以本仓库 2.4.0 版本及
@interactjs1.10.2 依赖为准;若使用其他版本,部分属性(如transformScale、restoreOnDrag)可能不存在或行为不同。
- 前端
- UI组件
【免费下载链接】vue-grid-layout
A draggable and resizable grid layout, for Vue.js.
相关推荐
NoneBot2 更新日志解读:从 v2.0 到 v2.5 的框架演进与核心能力升级
NoneBot2 更新日志解读:从 v2.0 到 v2.5 的框架演进与核心能力升级 导读 本文以 NoneBot2 官方文档站维护的完整更新日志( websi
后端即时通讯Floci 版本演进全解析:从 1.0.0 到 2.1.0 的变更日志深度解读
Floci 版本演进全解析:从 1.0.0 到 2.1.0 的变更日志深度解读 Floci 是一个免费开源的本地 AWS 模拟器(AWS Local Emula
Video2X深度解析:基于机器学习的视频超分辨率与帧插值架构设计
Video2X深度解析:基于机器学习的视频超分辨率与帧插值架构设计 Video2X是一个基于机器学习的视频超分辨率放大与帧插值框架,采用C/C++语言完全重写,
音视频视频处理图像处理深度学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考