dragact.tsx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import * as React from "react";
  2. import GridItem, { GridItemEvent } from './GridItem'
  3. import { compactLayout } from './util/compact';
  4. import { getMaxContainerHeight } from './util/sort';
  5. import { layoutCheck } from './util/collison';
  6. import { correctLayout } from './util/correction';
  7. import { getDataSet, stringJoin } from './utils';
  8. import { layoutItemForkey, syncLayout, MapLayoutTostate } from './util/initiate';
  9. import './style.css';
  10. export interface DragactLayout {
  11. GridX: number
  12. GridY: number
  13. static?: Boolean
  14. w: number
  15. h: number
  16. isUserMove?: Boolean
  17. key?: number | string
  18. }
  19. interface DragactProps {
  20. layout?: DragactLayout[] //暂时不推荐使用
  21. /**
  22. * 宽度切分比
  23. * 这个参数会把容器的宽度平均分为col等份
  24. * 于是容器内元素的最小宽度就等于 containerWidth/col
  25. */
  26. col: number,
  27. /**
  28. * 容器的宽度
  29. */
  30. width: number,
  31. /**容器内每个元素的最小高度 */
  32. rowHeight: number,
  33. /**
  34. * 容器内部的padding
  35. */
  36. padding?: number,
  37. children: any[] | any
  38. //
  39. // interface GridItemEvent {
  40. // event: any //浏览器拖动事件
  41. // GridX: number //在布局中的x格子
  42. // GridY: number //在布局中的y格子
  43. // w: number //元素的宽度
  44. // h: number //元素的高度
  45. // UniqueKey: string | number //元素的唯一key
  46. // }
  47. /**
  48. * 拖动开始的回调
  49. */
  50. onDragStart?: (event: GridItemEvent) => void
  51. /**
  52. * 拖动中的回调
  53. */
  54. onDrag?: (event: GridItemEvent) => void
  55. /**
  56. * 拖动结束的回调
  57. */
  58. onDragEnd?: (key: number | string) => void
  59. /**
  60. * 每个元素的margin,第一个参数是左右,第二个参数是上下
  61. */
  62. margin: [number, number]
  63. /**
  64. * layout的名字
  65. */
  66. className: number | string
  67. }
  68. interface DragactState {
  69. GridXMoving: number
  70. GridYMoving: number
  71. wMoving: number
  72. hMoving: number
  73. placeholderShow: Boolean,
  74. placeholderMoving: Boolean,
  75. layout: DragactLayout[],
  76. containerHeight: number
  77. }
  78. export class Dragact extends React.Component<DragactProps, DragactState> {
  79. constructor(props: DragactProps) {
  80. super(props)
  81. this.onDrag = this.onDrag.bind(this)
  82. this.onDragStart = this.onDragStart.bind(this)
  83. this.onDragEnd = this.onDragEnd.bind(this)
  84. const layout = props.layout ?
  85. MapLayoutTostate(props.layout, props.children)
  86. :
  87. getDataSet(props.children);
  88. this.state = {
  89. GridXMoving: 0,
  90. GridYMoving: 0,
  91. wMoving: 0,
  92. hMoving: 0,
  93. placeholderShow: false,
  94. placeholderMoving: false,
  95. layout: layout,
  96. containerHeight: 500
  97. }
  98. }
  99. onDragStart(bundles: GridItemEvent) {
  100. const { GridX, GridY, w, h, UniqueKey } = bundles
  101. const newlayout = syncLayout(this.state.layout, UniqueKey, GridX, GridY, true)
  102. this.setState({
  103. GridXMoving: GridX,
  104. GridYMoving: GridY,
  105. wMoving: w,
  106. hMoving: h,
  107. placeholderShow: true,
  108. placeholderMoving: true,
  109. layout: newlayout,
  110. })
  111. this.props.onDragStart && this.props.onDragStart(bundles)
  112. }
  113. onDrag(layoutItem: GridItemEvent) {
  114. const { GridY, UniqueKey } = layoutItem
  115. const moving = GridY - this.state.GridYMoving
  116. const newLayout = layoutCheck(this.state.layout, layoutItem, UniqueKey, UniqueKey/*用户移动方块的key */, moving)
  117. const compactedLayout = compactLayout(newLayout)
  118. for (let i = 0; i < compactedLayout.length; i++) {
  119. const compactedItem = compactedLayout[i];
  120. if (UniqueKey === compactedItem.key) {
  121. /**
  122. * 特殊点:当我们移动元素的时候,元素在layout中的位置不断改变
  123. * 但是当isUserMove=true的时候,鼠标拖拽的元素不会随着位图变化而变化
  124. * 但是实际layout中的位置还是会改变
  125. * (isUserMove=true用于解除placeholder和元素的绑定)
  126. */
  127. compactedItem.isUserMove = true
  128. layoutItem.GridX = compactedItem.GridX
  129. layoutItem.GridY = compactedItem.GridY
  130. break
  131. }
  132. }
  133. this.setState({
  134. GridXMoving: layoutItem.GridX,
  135. GridYMoving: layoutItem.GridY,
  136. layout: compactedLayout,
  137. containerHeight: getMaxContainerHeight(compactedLayout, this.props.rowHeight, this.props.margin[1])
  138. })
  139. this.props.onDrag && this.props.onDrag(layoutItem);
  140. }
  141. onDragEnd(key: number | string) {
  142. const compactedLayout = compactLayout(this.state.layout)
  143. this.setState({
  144. placeholderShow: false,
  145. layout: compactedLayout,
  146. containerHeight: getMaxContainerHeight(compactedLayout, this.props.rowHeight, this.props.margin[1])
  147. })
  148. this.props.onDragEnd && this.props.onDragEnd(key);
  149. }
  150. renderPlaceholder() {
  151. if (!this.state.placeholderShow) return null
  152. var { col, width, padding, rowHeight, margin } = this.props
  153. const { GridXMoving, GridYMoving, wMoving, hMoving, placeholderMoving } = this.state
  154. if (!padding) padding = 0;
  155. return (
  156. <GridItem
  157. margin={margin}
  158. col={col}
  159. containerWidth={width}
  160. containerPadding={[padding, padding]}
  161. rowHeight={rowHeight}
  162. GridX={GridXMoving}
  163. GridY={GridYMoving}
  164. w={wMoving}
  165. h={hMoving}
  166. style={{ background: '#d6e4ff', zIndex: 1, transition: ' all .15s' }}
  167. isUserMove={!placeholderMoving}
  168. />
  169. )
  170. }
  171. componentDidMount() {
  172. setTimeout(() => {
  173. let layout = correctLayout(this.state.layout, this.props.col)
  174. const compacted = compactLayout(layout);
  175. this.setState({
  176. layout: compacted,
  177. containerHeight: getMaxContainerHeight(compacted, this.props.rowHeight, this.props.margin[1])
  178. })
  179. }, 1);
  180. }
  181. getGridItem(child: any, index: number) {
  182. const { layout } = this.state
  183. var { col, width, padding, rowHeight, margin } = this.props;
  184. const renderItem = layoutItemForkey(layout, child.key);//TODO:可以优化速度,这一步不是必须;
  185. if (renderItem) {
  186. if (!padding) padding = 0;
  187. return (
  188. <GridItem
  189. margin={margin}
  190. col={col}
  191. containerWidth={width}
  192. containerPadding={[padding, padding]}
  193. rowHeight={rowHeight}
  194. GridX={renderItem.GridX}
  195. GridY={renderItem.GridY}
  196. w={renderItem.w}
  197. h={renderItem.h}
  198. onDrag={this.onDrag}
  199. onDragStart={this.onDragStart}
  200. onDragEnd={this.onDragEnd}
  201. isUserMove={renderItem.isUserMove !== void 666 ? renderItem.isUserMove : false}
  202. UniqueKey={child.key}
  203. style={{ zIndex: 2 }}
  204. static={renderItem.static}
  205. >
  206. {child}
  207. </GridItem >
  208. )
  209. }
  210. }
  211. render() {
  212. const { width, className } = this.props;
  213. const { containerHeight } = this.state;
  214. return (
  215. <div
  216. className={stringJoin('DraggerLayout', className + '')}
  217. style={{ left: 100, width: width, height: containerHeight, zIndex: 1 }}
  218. >
  219. {React.Children.map(this.props.children,
  220. (child, index) => this.getGridItem(child, index)
  221. )}
  222. {this.renderPlaceholder()}
  223. </div>
  224. )
  225. }
  226. }