App.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of';
  2. import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
  3. import _createClass from 'babel-runtime/helpers/createClass';
  4. import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
  5. import _inherits from 'babel-runtime/helpers/inherits';
  6. import _extends from 'babel-runtime/helpers/extends';
  7. import _toConsumableArray from 'babel-runtime/helpers/toConsumableArray';
  8. import React from 'react';
  9. import PropTypes from 'prop-types';
  10. import GridItem, { checkInContainer } from './GridItem';
  11. import './style.css';
  12. /**
  13. * 这个函数会有副作用,不是纯函数,会改变item的Gridx和GridY
  14. * @param {*} item
  15. */
  16. var correctItem = function correctItem(item, col) {
  17. var _checkInContainer = checkInContainer(item.GridX, item.GridY, col, item.w),
  18. GridX = _checkInContainer.GridX,
  19. GridY = _checkInContainer.GridY;
  20. item.GridX = GridX;
  21. item.GridY = GridY;
  22. };
  23. var correctLayout = function correctLayout(layout, col) {
  24. var copy = [].concat(_toConsumableArray(layout));
  25. for (var i = 0; i < layout.length - 1; i++) {
  26. correctItem(copy[i], col);
  27. correctItem(copy[i + 1], col);
  28. if (collision(copy[i], copy[i + 1])) {
  29. copy = layoutCheck(copy, copy[i], copy[i].key, copy[i].key, undefined);
  30. }
  31. }
  32. return copy;
  33. };
  34. /**
  35. * 用key从layout中拿出item
  36. * @param {*} layout 输入进来的布局
  37. * @param {*} key
  38. */
  39. var layoutItemForkey = function layoutItemForkey(layout, key) {
  40. for (var i = 0, length = layout.length; i < length; i++) {
  41. if (key === layout[i].key) {
  42. return layout[i];
  43. }
  44. }
  45. };
  46. /**
  47. * 初始化的时候调用
  48. * 会把isUserMove和key一起映射到layout中
  49. * 不用用户设置
  50. * @param {*} layout
  51. * @param {*} children
  52. */
  53. var MapLayoutTostate = function MapLayoutTostate(layout, children) {
  54. return layout.map(function (child, index) {
  55. var newChild = _extends({}, child, { isUserMove: true, key: children[index].key, static: children[index].static });
  56. return newChild;
  57. });
  58. };
  59. /**
  60. * 把用户移动的块,标记为true
  61. * @param {*} layout
  62. * @param {*} key
  63. * @param {*} GridX
  64. * @param {*} GridY
  65. * @param {*} isUserMove
  66. */
  67. var syncLayout = function syncLayout(layout, key, GridX, GridY, isUserMove) {
  68. var newlayout = layout.map(function (item) {
  69. if (item.key === key) {
  70. item.GridX = GridX;
  71. item.GridY = GridY;
  72. item.isUserMove = isUserMove;
  73. return item;
  74. }
  75. return item;
  76. });
  77. return newlayout;
  78. };
  79. var collision = function collision(a, b) {
  80. if (a.GridX === b.GridX && a.GridY === b.GridY && a.w === b.w && a.h === b.h) {
  81. return true;
  82. }
  83. if (a.GridX + a.w <= b.GridX) return false;
  84. if (a.GridX >= b.GridX + b.w) return false;
  85. if (a.GridY + a.h <= b.GridY) return false;
  86. if (a.GridY >= b.GridY + b.h) return false;
  87. return true;
  88. };
  89. var sortLayout = function sortLayout(layout) {
  90. return [].concat(layout).sort(function (a, b) {
  91. if (a.GridY > b.GridY || a.GridY === b.GridY && a.GridX > b.GridX) {
  92. if (a.static) return 0; //为了静态,排序的时候尽量把静态的放在前面
  93. return 1;
  94. } else if (a.GridY === b.GridY && a.GridX === b.GridX) {
  95. return 0;
  96. }
  97. return -1;
  98. });
  99. };
  100. /**获取layout中,item第一个碰撞到的物体 */
  101. var getFirstCollison = function getFirstCollison(layout, item) {
  102. for (var i = 0, length = layout.length; i < length; i++) {
  103. if (collision(layout[i], item)) {
  104. return layout[i];
  105. }
  106. }
  107. return null;
  108. };
  109. /**
  110. * 压缩单个元素,使得每一个元素都会紧挨着边界或者相邻的元素
  111. * @param {*} finishedLayout 压缩完的元素会放进这里来,用来对比之后的每一个元素是否需要压缩
  112. * @param {*} item
  113. */
  114. var compactItem = function compactItem(finishedLayout, item) {
  115. if (item.static) return item;
  116. var newItem = _extends({}, item);
  117. if (finishedLayout.length === 0) {
  118. return _extends({}, newItem, { GridY: 0 });
  119. }
  120. /**
  121. * 类似一个递归调用
  122. */
  123. while (true) {
  124. var FirstCollison = getFirstCollison(finishedLayout, newItem);
  125. if (FirstCollison) {
  126. /**第一次发生碰撞时,就可以返回了 */
  127. newItem.GridY = FirstCollison.GridY + FirstCollison.h;
  128. return newItem;
  129. }
  130. newItem.GridY--;
  131. if (newItem.GridY < 0) return _extends({}, newItem, { GridY: 0 /**碰到边界的时候,返回 */
  132. });
  133. }
  134. return newItem;
  135. };
  136. /**
  137. * 压缩layout,使得每一个元素都会紧挨着边界或者相邻的元素
  138. * @param {*} layout
  139. */
  140. var compactLayout = function compactLayout(layout) {
  141. var sorted = sortLayout(layout);
  142. var needCompact = Array(layout.length);
  143. var compareList = [];
  144. for (var i = 0, length = sorted.length; i < length; i++) {
  145. var finished = compactItem(compareList, sorted[i]);
  146. finished.isUserMove = false;
  147. compareList.push(finished);
  148. needCompact[i] = finished;
  149. }
  150. return needCompact;
  151. };
  152. var layoutCheck = function layoutCheck(layout, layoutItem, key, fristItemkey, moving) {
  153. var i = [],
  154. movedItem = []; /**收集所有移动过的物体 */
  155. var newlayout = layout.map(function (item, idx) {
  156. if (item.key !== key) {
  157. if (item.static) {
  158. return item;
  159. }
  160. if (collision(item, layoutItem)) {
  161. i.push(item.key);
  162. /**
  163. * 这里就是奇迹发生的地方,如果向上移动,那么必须注意的是
  164. * 一格一格的移动,而不是一次性移动
  165. */
  166. var offsetY = item.GridY + 1;
  167. /**这一行也非常关键,当向上移动的时候,碰撞的元素必须固定 */
  168. // if (moving < 0 && layoutItem.GridY > 0) offsetY = item.GridY
  169. if (layoutItem.GridY > item.GridY && layoutItem.GridY < item.GridY + item.h) {
  170. /**
  171. * 元素向上移动时,元素的上面空间不足,则不移动这个元素
  172. * 当元素移动到GridY>所要向上交换的元素时,就不会进入这里,直接交换元素
  173. */
  174. offsetY = item.GridY;
  175. }
  176. /**
  177. * 物体向下移动的时候
  178. */
  179. if (moving > 0) {
  180. if (layoutItem.GridY + layoutItem.h < item.GridY) {
  181. (function () {
  182. var collision = void 0;
  183. var copy = _extends({}, item);
  184. while (true) {
  185. var newLayout = layout.filter(function (item) {
  186. if (item.key !== key && item.key !== copy.key) {
  187. return item;
  188. }
  189. });
  190. collision = getFirstCollison(newLayout, copy);
  191. if (collision) {
  192. offsetY = collision.GridY + collision.h;
  193. break;
  194. } else {
  195. copy.GridY--;
  196. }
  197. if (copy.GridY < 0) {
  198. offsetY = 0;
  199. break;
  200. }
  201. }
  202. })();
  203. }
  204. }
  205. movedItem.push(_extends({}, item, { GridY: offsetY, isUserMove: false }));
  206. return _extends({}, item, { GridY: offsetY, isUserMove: false });
  207. }
  208. } else if (fristItemkey === key) {
  209. /**永远保持用户移动的块是 isUserMove === true */
  210. return _extends({}, item, { GridX: layoutItem.GridX, GridY: layoutItem.GridY, isUserMove: true });
  211. }
  212. return item;
  213. });
  214. /** 递归调用,将layout中的所有重叠元素全部移动 */
  215. var length = movedItem.length;
  216. for (var c = 0; c < length; c++) {
  217. newlayout = layoutCheck(newlayout, movedItem[c], i[c], fristItemkey, undefined);
  218. }
  219. return newlayout;
  220. };
  221. function quickSort(a) {
  222. return a.length <= 1 ? a : quickSort(a.slice(1).filter(function (item) {
  223. return item <= a[0];
  224. })).concat(a[0], quickSort(a.slice(1).filter(function (item) {
  225. return item > a[0];
  226. })));
  227. }
  228. var getMaxContainerHeight = function getMaxContainerHeight(layout) {
  229. var elementHeight = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 30;
  230. var elementMarginBottom = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10;
  231. var ar = layout.map(function (item) {
  232. return item.GridY + item.h;
  233. });
  234. var h = quickSort(ar)[ar.length - 1];
  235. var height = h * (elementHeight + elementMarginBottom) + elementMarginBottom;
  236. return height;
  237. };
  238. var getDataSet = function getDataSet(children) {
  239. return children.map(function (child) {
  240. return _extends({}, child.props['data-set'], { isUserMove: true, key: child.key });
  241. });
  242. };
  243. var stringJoin = function stringJoin(source, join) {
  244. return source + (join ? ' ' + join : '');
  245. };
  246. export var DraggerLayout = function (_React$Component) {
  247. _inherits(DraggerLayout, _React$Component);
  248. function DraggerLayout(props) {
  249. _classCallCheck(this, DraggerLayout);
  250. var _this = _possibleConstructorReturn(this, (DraggerLayout.__proto__ || _Object$getPrototypeOf(DraggerLayout)).call(this, props));
  251. _this.onDrag = _this.onDrag.bind(_this);
  252. _this.onDragStart = _this.onDragStart.bind(_this);
  253. _this.onDragEnd = _this.onDragEnd.bind(_this);
  254. var layout = props.layout ? MapLayoutTostate(props.layout, props.children) : getDataSet(props.children);
  255. _this.state = {
  256. GridXMoving: 0,
  257. GridYMoving: 0,
  258. wMoving: 0,
  259. hMoving: 0,
  260. placeholderShow: false,
  261. placeholderMoving: false,
  262. layout: layout,
  263. containerHeight: 500
  264. };
  265. return _this;
  266. }
  267. _createClass(DraggerLayout, [{
  268. key: 'onDragStart',
  269. value: function onDragStart(bundles) {
  270. var GridX = bundles.GridX,
  271. GridY = bundles.GridY,
  272. w = bundles.w,
  273. h = bundles.h,
  274. UniqueKey = bundles.UniqueKey;
  275. var newlayout = syncLayout(this.state.layout, UniqueKey, GridX, GridY, true);
  276. this.setState({
  277. GridXMoving: GridX,
  278. GridYMoving: GridY,
  279. wMoving: w,
  280. hMoving: h,
  281. placeholderShow: true,
  282. placeholderMoving: true,
  283. layout: newlayout
  284. });
  285. this.props.onDragStart && this.props.onDragStart({ GridX: GridX, GridY: GridY });
  286. }
  287. }, {
  288. key: 'onDrag',
  289. value: function onDrag(layoutItem, key) {
  290. var GridX = layoutItem.GridX,
  291. GridY = layoutItem.GridY;
  292. var moving = GridY - this.state.GridYMoving;
  293. var newLayout = layoutCheck(this.state.layout, layoutItem, key, key /*用户移动方块的key */, moving);
  294. var compactedLayout = compactLayout(newLayout);
  295. for (var i = 0; i < compactedLayout.length; i++) {
  296. var compactedItem = compactedLayout[i];
  297. if (key === compactedItem.key) {
  298. /**
  299. * 特殊点:当我们移动元素的时候,元素在layout中的位置不断改变
  300. * 但是当isUserMove=true的时候,鼠标拖拽的元素不会随着位图变化而变化
  301. * 但是实际layout中的位置还是会改变
  302. * (isUserMove=true用于解除placeholder和元素的绑定)
  303. */
  304. compactedItem.isUserMove = true;
  305. layoutItem.GridX = compactedItem.GridX;
  306. layoutItem.GridY = compactedItem.GridY;
  307. break;
  308. }
  309. }
  310. this.setState({
  311. GridXMoving: layoutItem.GridX,
  312. GridYMoving: layoutItem.GridY,
  313. layout: compactedLayout,
  314. containerHeight: getMaxContainerHeight(compactedLayout, this.props.rowHeight, this.props.margin[1])
  315. });
  316. this.props.onDrag && this.props.onDrag({ GridX: GridX, GridY: GridY });
  317. }
  318. }, {
  319. key: 'onDragEnd',
  320. value: function onDragEnd(key) {
  321. var compactedLayout = compactLayout(this.state.layout);
  322. this.setState({
  323. placeholderShow: false,
  324. layout: compactedLayout,
  325. containerHeight: getMaxContainerHeight(compactedLayout, this.props.rowHeight, this.props.margin[1])
  326. });
  327. this.props.onDragEnd && this.props.onDragEnd();
  328. }
  329. }, {
  330. key: 'renderPlaceholder',
  331. value: function renderPlaceholder() {
  332. if (!this.state.placeholderShow) return null;
  333. var _props = this.props,
  334. col = _props.col,
  335. width = _props.width,
  336. padding = _props.padding,
  337. rowHeight = _props.rowHeight,
  338. margin = _props.margin;
  339. var _state = this.state,
  340. GridXMoving = _state.GridXMoving,
  341. GridYMoving = _state.GridYMoving,
  342. wMoving = _state.wMoving,
  343. hMoving = _state.hMoving,
  344. placeholderMoving = _state.placeholderMoving;
  345. return React.createElement(GridItem, {
  346. margin: margin,
  347. col: col,
  348. containerWidth: width,
  349. containerPadding: padding,
  350. rowHeight: rowHeight,
  351. GridX: GridXMoving,
  352. GridY: GridYMoving,
  353. w: wMoving,
  354. h: hMoving,
  355. style: { background: '#d6e4ff', zIndex: 1, transition: ' all .15s' },
  356. isUserMove: !placeholderMoving
  357. });
  358. }
  359. }, {
  360. key: 'componentDidMount',
  361. value: function componentDidMount() {
  362. var _this2 = this;
  363. setTimeout(function () {
  364. var layout = correctLayout(_this2.state.layout, _this2.props.col);
  365. var compacted = compactLayout(layout);
  366. _this2.setState({
  367. layout: compacted,
  368. containerHeight: getMaxContainerHeight(compacted, _this2.props.rowHeight, _this2.props.margin[1])
  369. });
  370. }, 1);
  371. }
  372. }, {
  373. key: 'getGridItem',
  374. value: function getGridItem(child, index) {
  375. var layout = this.state.layout;
  376. var _props2 = this.props,
  377. col = _props2.col,
  378. width = _props2.width,
  379. padding = _props2.padding,
  380. rowHeight = _props2.rowHeight,
  381. margin = _props2.margin;
  382. var renderItem = layoutItemForkey(layout, child.key); //TODO:可以优化速度,这一步不是必须;
  383. return React.createElement(
  384. GridItem,
  385. {
  386. margin: margin,
  387. col: col,
  388. containerWidth: width,
  389. containerPadding: padding,
  390. rowHeight: rowHeight,
  391. GridX: renderItem.GridX,
  392. GridY: renderItem.GridY,
  393. w: renderItem.w,
  394. h: renderItem.h,
  395. onDrag: this.onDrag,
  396. onDragStart: this.onDragStart,
  397. onDragEnd: this.onDragEnd,
  398. index: index,
  399. isUserMove: renderItem.isUserMove,
  400. UniqueKey: child.key,
  401. style: { zIndex: 2 },
  402. 'static': renderItem.static
  403. },
  404. child
  405. );
  406. }
  407. }, {
  408. key: 'render',
  409. value: function render() {
  410. var _this3 = this;
  411. var _props3 = this.props,
  412. layout = _props3.layout,
  413. col = _props3.col,
  414. width = _props3.width,
  415. padding = _props3.padding,
  416. rowHeight = _props3.rowHeight,
  417. className = _props3.className;
  418. var containerHeight = this.state.containerHeight;
  419. return React.createElement(
  420. 'div',
  421. {
  422. className: stringJoin('DraggerLayout', className),
  423. style: { left: 100, width: width, height: containerHeight, zIndex: 1 }
  424. },
  425. React.Children.map(this.props.children, function (child, index) {
  426. return _this3.getGridItem(child, index);
  427. }),
  428. this.renderPlaceholder()
  429. );
  430. }
  431. }]);
  432. return DraggerLayout;
  433. }(React.Component);
  434. DraggerLayout.propTypes = {
  435. /**外部属性 */
  436. layout: PropTypes.array,
  437. col: PropTypes.number,
  438. width: PropTypes.number,
  439. /**每个元素的最小高度 */
  440. rowHeight: PropTypes.number,
  441. padding: PropTypes.number
  442. };
  443. ;
  444. var _temp = function () {
  445. if (typeof __REACT_HOT_LOADER__ === 'undefined') {
  446. return;
  447. }
  448. __REACT_HOT_LOADER__.register(correctItem, 'correctItem', 'app/src/App.js');
  449. __REACT_HOT_LOADER__.register(correctLayout, 'correctLayout', 'app/src/App.js');
  450. __REACT_HOT_LOADER__.register(layoutItemForkey, 'layoutItemForkey', 'app/src/App.js');
  451. __REACT_HOT_LOADER__.register(MapLayoutTostate, 'MapLayoutTostate', 'app/src/App.js');
  452. __REACT_HOT_LOADER__.register(syncLayout, 'syncLayout', 'app/src/App.js');
  453. __REACT_HOT_LOADER__.register(collision, 'collision', 'app/src/App.js');
  454. __REACT_HOT_LOADER__.register(sortLayout, 'sortLayout', 'app/src/App.js');
  455. __REACT_HOT_LOADER__.register(getFirstCollison, 'getFirstCollison', 'app/src/App.js');
  456. __REACT_HOT_LOADER__.register(compactItem, 'compactItem', 'app/src/App.js');
  457. __REACT_HOT_LOADER__.register(compactLayout, 'compactLayout', 'app/src/App.js');
  458. __REACT_HOT_LOADER__.register(layoutCheck, 'layoutCheck', 'app/src/App.js');
  459. __REACT_HOT_LOADER__.register(quickSort, 'quickSort', 'app/src/App.js');
  460. __REACT_HOT_LOADER__.register(getMaxContainerHeight, 'getMaxContainerHeight', 'app/src/App.js');
  461. __REACT_HOT_LOADER__.register(getDataSet, 'getDataSet', 'app/src/App.js');
  462. __REACT_HOT_LOADER__.register(stringJoin, 'stringJoin', 'app/src/App.js');
  463. __REACT_HOT_LOADER__.register(DraggerLayout, 'DraggerLayout', 'app/src/App.js');
  464. }();
  465. ;