react-grid-layout 栅格布局

前言

最近接触了一个 react-grid-layout 栅格布局系统,在使用过程当中发现它的功能不少,可是对于其中的属性的使用和事件有不少不理解的地方,因此这里概括(翻译)一下它的属性及用途css

react-grid-layout

React-Grid-Layout 是只提供给 React 组件而且不须要使用 JQuery 的栅格布局系统,相似的像:Packery Gridster,但 RGL 是响应式的,而且支持 breakpoints 断点,用户能够自定义设置断点,也能够由 RGL 自动生成。来自官网的强大动图: react

RGL

Grid Layout Props

RGL 支持下面这些属性:css3

基础属性

// 基础属性
// 在服务器端设置的初始宽度,除非你使用了 HOC <WidthProvider> 或者其余相似的组件,那么 width 属性是必须的。
width: number,

// 当为 true 的时候,容器高度自适应以适应内容
autoSize: ?boolean = true,

// 将当前布局分为多少列
cols: ?number = 12|{},

// 属性值为标签的 css 选择器,被标记后标签就不可拖动了
// 好比说:draggableCancel:'.MyNonDraggableAreaClassName'
draggableCancel: ?string = '',

// 属性值为标签的 css 选择器,被标记后标签就能够被拖动
// 好比说: draggableHandle:'.MyDragHandleClassName'
draggableHandle: ?string = '',

// 为 true 时,会在竖直方向上紧凑排列
verticalCompact: ?boolean = true,

// 紧凑排列的方向
compactType: ?('vertical' | 'horizontal') = 'vertical';

// layouts 属性值是由这样格式{x: number, y: number, w: number, h: number}的对象组成的数组
// 须要注意的是,layout在数组中的下标必须和他们做用的组件的 key 值匹配
// 若是但愿在组件中使用自定义的 key 值,那么你能够在 layout 中指定那个 key 值
// 最后这个对象数组可能像是这样: {i: string, x: number, y: number, w: number, h: number}
// 也能够在子组件中单独设置
layout: ?array = null, 

// 每一个包含块之间 margin:[x,y] 单位为 px
margin: ?[number, number] = [10, 10],

//包含块内部的 padding:[x,y] 单位为 px
// Padding inside the container [x, y] in px
containerPadding: ?[number, number] = margin,

// 每行的静态高度,可是若是须要的话你能够基于 breakpoints 来改变它,
rowHeight: ?number = 150,

// droping 元素的配置,它是一个"虚拟的"元素,当您从外部拖动某个元素时,它会出现
// 它能够经过传递特定参数被改变:
// i - 元素的 id
// w - 元素的宽度
// h - 元素的高度
droppingItem?: { i: string, w: number, h: number }

//
// Flags
//
isDraggable: ?boolean = true,
isResizable: ?boolean = true,
// 使用css3的translate() 代替定位中的 top 和 left,能使绘制过程的性能提高 6 倍
useCSSTransforms: ?boolean = true,
// 若是父节点:ResponsiveReactGridLayout 或者 ReactGridLayout 有 transform: scale(n) 的 css 属性
// 咱们应该设置比例系数,以免在拖动的过程当中 render artefacts
transformScale: ?number = 1,

// 若是设置true,在拖拽过程中,被拖拽的组件不会改变位置
preventCollision: ?boolean = false;

// 若是设置为 true,可拖动的元素(带有 draggable={true} 属性)能够在网格上被拖动,他会引起 "onDrop" 回调函数,位置信息和事件对象 做为参数会被传到回调函数中,

// 注意:假如使用的是 Firefox 你应该添加 onDragStart={e => e.dataTransfer.setData('text/plain', '')} 属性和 draggable={true},不然这个特性不会正确的工做
// Firefox 需 要onDragStart 属性来进行拖放初始化
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=568313
isDroppable: ?boolean = false

//
// Callbacks
//

// 用来保存布局的回调,每次 drag 或者 resize 结束以后返回当前的布局
onLayoutChange: (layout: Layout) => void,

//下面全部的回调都有这些参数(layout, oldItem, newItem, placeholder, e, element)
//开始和结束的回调函数中参数:placeholder为undefined
//
type ItemCallback = (layout: Layout, oldItem: LayoutItem, newItem: LayoutItem,
                     placeholder: LayoutItem, e: MouseEvent, element: HTMLElement) => void;


onDragStart: ItemCallback,
onDrag: ItemCallback,
onDragStop: ItemCallback,
onResizeStart: ItemCallback,
onResize: ItemCallback,
onResizeStop: ItemCallback,
onDrop: (elemParams: { x: number, y: number, e: Event }) => void
复制代码

响应式栅格布局属性

响应式栅格布局是能够被使用的,它支持👆除 layout 以外全部的基本属性,新属性和改变的部分以下:git

// 断点名是任意的,但必须与cols和layouts对象匹配。
breakpoints: ?Object = {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0},

// 针对 cols 的断点
cols: ?Object = {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},

// 固定间隔或者响应式间隔 e.g.[10,10]| `{lg: [10, 10], md: [10, 10], ...}.
margin: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]}


// 同上,固定内边距或者响应式内边距:e.g. [10,10]|`{lg: [10, 10], md: [10, 10], ...}
containerPadding: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]}


// 由断点查找的 layouts 对象 e.g. {lg: Layout, md: Layout, ...}
layouts: {[key: $Keys<breakpoints>]: Layout}

//
// Callbacks
//

onBreakpointChange: (newBreakpoint: string, newCols: number) => void,

onLayoutChange: (currentLayout: Layout, allLayouts: {[key: $Keys<breakpoints>]: Layout}) => void,

// 当页面宽度变化时的回调,这样你就能够根据须要修改 layout 啦
onWidthChange: (containerWidth: number, margin: [number, number], cols: number, containerPadding: [number, number]) => void;
复制代码

栅格元素的属性

请注意,若是提供了一个网格项,但不完整(缺乏x、y、w或h中的一个)的时候将抛出一个错误,以便您能够更正布局。github

若是没有为网格项提供属性,那么将生成一个宽度和高度为1的属性。数组

您能够为每一个维度设置最小值和最大值。这是用来调整大小的;固然,若是禁用了调整大小的功能,则不会产生任何影响。若是你的最小值和最大值互相重叠,或者你的初始尺寸超出范围将会抛出错误。服务器

任何直接定义在中的属性都将优先于全局设置选项。例如,若是布局的属性是isDraggable: false,可是网格项的道具是isDraggable: true,那么这个项目就是draggable,即便这个项目被标记为static: true。ide

{

  // 组件的 key 值
  i: string,

  // 这些都是网格单元,而不是像素
  x: number, // 占几列
  y: number, // rowHeight 的倍数
  w: number, // 同 x 计算方法
  h: number, // 同 y 计算方法
  minW: ?number = 0,
  maxW: ?number = Infinity,
  minH: ?number = 0,
  maxH: ?number = Infinity,

  // 为 true 的时候至关于设置 "isDraggable: false, isResizable: false"
  static: ?boolean = false,
  // 为 false 的时候,不能被拖动,覆盖掉 static 属性
  isDraggable: ?boolean = true,
  // 为 false 的时候,不能从新设置大小,覆盖掉 static 属性
  isResizable: ?boolean = true
}
复制代码

使用

组件的选取

非响应式直接引用 GridLayout 组件,以下:函数

import GridLayout from 'react-grid-layout';
复制代码

GridLayout 不支持cols、layouts 等支持基于断点调整值的属性的属性值为断点调整的对象(eg:{lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0}),强行使用会使页面展现效果与预想的不一致,也就是出错了,当想使用响应式的布局的时候提倡使用下面的组件。布局

响应式的使用方法:

当使用响应式模式时,您应该使 layouts 属性值至少提供一个断点下的布局,而且暂时不支持为单个 grid-item 设置断点布局。

一、使用 ResponsiveGridLayout 组件

import { Responsive as ResponsiveGridLayout } from 'react-grid-layout';
复制代码

二、使用 WidthProvider 函数包装后的组件 <ResponsiveGridLayout> 和 <ReactGridLayout> 组件在拖动事件中用 width 去计算位置,更简单的状况下可使用专用的 WidthProvider 组件能够在初始化(initialization)和 resize 事件中自动计算宽度

import { Responsive, WidthProvider } from 'react-grid-layout';

const ResponsiveGridLayout = WidthProvider(Responsive);
复制代码

断点的使用

上面提到的不少属性,那些属性值能够为对象的属性都支持根据断点来控制元素的布局,好比说:cols、layouts、containerPadding、margin,它们的使用方法基本一致,以cols 和 margin 为例:

import React from "react";
import {WidthProvider, Responsive} from 'react-grid-layout';

const ResponsiveGridLayout = WidthProvider(Responsive);
export default class MyFirstGrid extends React.Component {
  render() {
    const layout = [
      {i: 'a', x: 0, y: 0, w: 1, h: 2, static: true},
      {i: 'b', x: 1, y: 0, w: 2, h: 2, minW: 2, maxW: 4},
      {i: 'c', x: 0, y: 2, w: 1, h: 2}
    ];
    return (
      <ResponsiveGridLayout className="layout" layouts={{lg:layout} } rowHeight={30} breakpoints={{lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0}} margin={{lg: [30,30], md: [20,20], sm: [10,10], xs: [5,5]}} > <div key="a" style={{"background": "red"}}>a</div> <div key="b" style={{"background": "red"}}>b</div> <div key="c" style={{"background": "red"}}>c</div> </ResponsiveGridLayout>
    )
  }
}
复制代码

效果以下:

能够看见在断点处会出现元素宽度和间隔的变化。

复杂一点的,若是想控制元素的位置即每一个元素的 x,y 坐标,则可使用 layouts 属性,针对屏幕不一样的尺寸更改元素的位置。

一些能够辅助响应式变化的的事件函数

  • onBreakpointChange 页面大小从一个断点区间过渡到另外一个区间的时候触发,当想响应式的修改其余原生不支持响应式的属性的时候(eg: rowHeight)能够在这里面修改
  • onLayoutChange 相似于 onBreakpointChange 会在断点的交界处被触发,回调函数会传入当前 layout 和 layouts 的属性值,能够在里面修改对应 key 的布局
  • onWidthChange 页面尺寸变化的回调,传入容器宽度、当前 margin 的值,cols 值、containerPadding 值

巧用紧凑型对齐方式

整个页面以左上角为 (0,0)点。

在基本的属性中注意到有两个属性:verticalCompact 和 compactType,分别取值为 boolean 类型和 string 类型,表示是否采用垂直方向的紧凑对齐或者在什么方向上紧凑对齐。当 compactType 和 verticalCompact 同时存在的时候,compactType 优先级比 verticalCompact 的优先级高。

使用不一样的对齐方式会让页面的排布有不同的神奇效果: 使用垂直方向上的对齐,当横坐标相同的位置上若是这个元素的前面已经有元素占据了前面的位置(即纵坐标也相同),那么这个元素排在相同横坐标下最后一个元素的下方,不然排在(x,0)的位置;

特别的是:当水平方向上的空间不足时,谁的 w 值越大谁越靠近 y = 0;

使用水平方向上的对齐,横纵都坐标相同时,从左到右后来者居左,标记为 static 的除外,会保持在本身的 x 坐标上,纵坐标相同横坐标不一样,按照横坐标的数值大小从左到右排列,横坐标相同纵坐标不一样,在这个纵坐标前面已经有元素占据了位置,那么排在最后一个元素的右边。

总结

在一些业务场景下,须要使用到这样的栅格系统来对页面进行分片再填充,那么使用 RGL 是比较方便的,但在实践过程当中多是了解的不够深刻吧,按照github上的例子实践的时候也出现了一些问题好比即便设置了 isResizable 以后也不能对 grid-item 进行resize,对于他的响应特性、可拖动特性、grid-item 的 resize特性会在之后的实践中再继续总结补充。