Compare commits

...

1 Commits

Author SHA1 Message Date
Timur Gibadullin
932437ae58 Improve perfomance while reordering 2018-03-28 00:03:58 +03:00
3 changed files with 166 additions and 47 deletions

View File

@ -1,7 +1,7 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {ScrollView, View, StyleSheet, Platform, RefreshControl, ViewPropTypes} from 'react-native';
import {shallowEqual, swapArrayElements} from './utils';
import {inRange, shallowEqual, swapArrayElements} from './utils';
import Row from './Row';
const AUTOSCROLL_INTERVAL = 100;
@ -220,7 +220,6 @@ export default class SortableList extends Component {
const {horizontal, rowActivationTime, sortingEnabled, renderRow} = this.props;
const {animated, order, data, activeRowKey, releasedRowKey, rowsLayouts} = this.state;
let nextX = 0;
let nextY = 0;
@ -302,11 +301,13 @@ export default class SortableList extends Component {
}
_onUpdateLayouts() {
const {horizontal} = this.props;
const {order} = this.state;
Promise.all([this._headerLayout, this._footerLayout, ...Object.values(this._rowsLayouts)])
.then(([headerLayout, footerLayout, ...rowsLayouts]) => {
// Can get correct containers layout only after rowss layouts.
this._container.measure((x, y, width, height, pageX, pageY) => {
const rowsLayoutsByKey = {};
let rowsLayoutsByKey = {};
let contentHeight = 0;
let contentWidth = 0;
@ -315,10 +316,12 @@ export default class SortableList extends Component {
contentHeight += layout.height;
contentWidth += layout.width;
});
rowsLayoutsByKey = this._getRowsLocations(rowsLayoutsByKey, order);
this.setState({
containerLayout: {x, y, width, height, pageX, pageY},
rowsLayouts: rowsLayoutsByKey,
rowsSwapRanges: this._getRowsSwapRanges(rowsLayoutsByKey, order),
headerLayout,
footerLayout,
contentHeight,
@ -330,6 +333,54 @@ export default class SortableList extends Component {
});
}
_getRowsLocations(_rowsLayouts, order) {
const {horizontal} = this.props;
const rowsLayouts = {};
let nextX = 0;
let nextY = 0;
for (let i = 0, len = order.length; i < len; i++) {
const rowKey = order[i];
const rowLayout = _rowsLayouts[rowKey];
rowsLayouts[rowKey] = {
...rowLayout,
x: nextX,
y: nextY,
};
if (horizontal) {
nextX += rowLayout.width;
} else {
nextY += rowLayout.height;
}
}
return rowsLayouts;
}
_getRowsSwapRanges(rowsLayouts, order) {
const {horizontal} = this.props;
const rowsSwapRanges = {};
for (let i = 0, len = order.length; i < len; i++) {
const rowKey = order[i];
const rowLayout = rowsLayouts[rowKey];
rowsSwapRanges[rowKey] = horizontal
? {
left: [rowLayout.x + rowLayout.width / 3, rowLayout.x + rowLayout.width],
right: [rowLayout.x, rowLayout.x + 2 * rowLayout.width / 3],
}
: {
top: [rowLayout.y + rowLayout.height / 3, rowLayout.y + rowLayout.height],
bottom: [rowLayout.y, rowLayout.y + 2 * rowLayout.height / 3],
};
}
return rowsSwapRanges;
}
_scroll(animated) {
this._scrollView.scrollTo({...this._contentOffset, animated});
}
@ -339,7 +390,8 @@ export default class SortableList extends Component {
* swaps them, else shifts rows.
*/
_setOrderOnMove() {
const {activeRowKey, activeRowIndex, order} = this.state;
const {activeRowKey, activeRowIndex, order, rowsLayouts} = this.state;
const {horizontal} = this.props;
if (activeRowKey === null || this._autoScrollInterval) {
return;
@ -369,9 +421,14 @@ export default class SortableList extends Component {
nextOrder.splice(rowUnderActiveIndex, 0, activeRowKey);
}
const nextRowsLayouts = this._getRowsLocations(rowsLayouts, nextOrder);
const nextRowsSwapRanges = this._getRowsSwapRanges(nextRowsLayouts, nextOrder);
this.setState({
order: nextOrder,
activeRowIndex: rowUnderActiveIndex,
rowsLayouts: nextRowsLayouts,
rowsSwapRanges: nextRowsSwapRanges,
}, () => {
if (this.props.onChangeOrder) {
this.props.onChangeOrder(nextOrder);
@ -381,53 +438,114 @@ export default class SortableList extends Component {
}
/**
* Finds a row, which was covered with the moving rows half.
* Finds a row, which was covered with the moving rows third.
*/
_findRowUnderActiveRow() {
const {horizontal} = this.props;
const {rowsLayouts, activeRowKey, activeRowIndex, order} = this.state;
const movingRowLayout = rowsLayouts[activeRowKey];
const rowLeftX = this._activeRowLocation.x
const rowRightX = rowLeftX + movingRowLayout.width;
const rowTopY = this._activeRowLocation.y;
const rowBottomY = rowTopY + movingRowLayout.height;
const {rowsLayouts, rowsSwapRanges, activeRowKey, activeRowIndex, order} = this.state;
const movingDirection = this._movingDirection;
const rowsCount = order.length;
const activeRowLayout = rowsLayouts[activeRowKey];
const activeRowLeftX = this._activeRowLocation.x
const activeRowRightX = this._activeRowLocation.x + activeRowLayout.width;
const activeRowTopY = this._activeRowLocation.y;
const activeRowBottomY = this._activeRowLocation.y + activeRowLayout.height;
for (
let currentRowIndex = 0, x = 0, y = 0, rowsCount = order.length;
currentRowIndex < rowsCount - 1;
currentRowIndex++
const prevRowIndex = activeRowIndex - 1;
const prevRowKey = order[prevRowIndex];
const prevRowSwapRages = rowsSwapRanges[prevRowKey]
const nextRowIndex = activeRowIndex + 1;
const nextRowKey = order[nextRowIndex];
const nextRowSwapRages = rowsSwapRanges[nextRowKey]
if (horizontal
? (movingDirection === 1
? (
(activeRowIndex === 0 || activeRowLeftX > prevRowSwapRages.right[0]) &&
(activeRowIndex === rowsCount - 1 || activeRowRightX < nextRowSwapRages.left[0])
)
: (
(activeRowIndex === 0 || activeRowLeftX > prevRowSwapRages.right[1]) &&
(activeRowIndex === rowsCount - 1 || activeRowRightX < nextRowSwapRages.left[1])
)
)
: (movingDirection === 1
? (
(activeRowIndex === 0 || activeRowTopY > prevRowSwapRages.bottom[0]) &&
(activeRowIndex === rowsCount - 1 || activeRowBottomY < nextRowSwapRages.top[0])
)
: (
(activeRowIndex === 0 || activeRowTopY > prevRowSwapRages.bottom[1]) &&
(activeRowIndex === rowsCount - 1 || activeRowBottomY < nextRowSwapRages.top[1])
)
)
) {
const currentRowKey = order[currentRowIndex];
const currentRowLayout = rowsLayouts[currentRowKey];
const nextRowIndex = currentRowIndex + 1;
const nextRowLayout = rowsLayouts[order[nextRowIndex]];
return {
rowKey: activeRowKey,
rowIndex: activeRowIndex,
};
}
x += currentRowLayout.width;
y += currentRowLayout.height;
if (currentRowKey !== activeRowKey && (
horizontal
? ((x - currentRowLayout.width <= rowLeftX || currentRowIndex === 0) && rowLeftX <= x - currentRowLayout.width / 3)
: ((y - currentRowLayout.height <= rowTopY || currentRowIndex === 0) && rowTopY <= y - currentRowLayout.height / 3)
)) {
return {
rowKey: order[currentRowIndex],
rowIndex: currentRowIndex,
if (movingDirection === 1) {
if (horizontal
? inRange(activeRowRightX, ...nextRowSwapRages.left)
: inRange(activeRowBottomY, ...nextRowSwapRages.top)
) {
return {
rowKey: nextRowKey,
rowIndex: nextRowIndex,
};
}
} else {
if (horizontal
? (x + nextRowLayout.width / 3 <= rowRightX && (rowRightX <= x + nextRowLayout.width || nextRowIndex === rowsCount - 1))
: (y + nextRowLayout.height / 3 <= rowBottomY && (rowBottomY <= y + nextRowLayout.height || nextRowIndex === rowsCount - 1))
? inRange(activeRowLeftX, ...prevRowSwapRages.right)
: inRange(activeRowTopY, ...prevRowSwapRages.bottom)
) {
return {
rowKey: order[nextRowIndex],
rowIndex: nextRowIndex,
return {
rowKey: prevRowKey,
rowIndex: prevRowIndex,
};
}
}
return {rowKey: activeRowKey, rowIndex: activeRowIndex};
// let startIndex = 0;
// let endIndex = rowsCount - 1;
// let middleIndex;
// let it = 0
// console.log(movingDirection);
// while (startIndex < endIndex) {
// middleIndex = Math.floor((endIndex - startIndex) / 2);
// if (it++ > 10) {
// console.log(startIndex, middleIndex, endIndex);
// break
// }
// const middleRowSwapRanges = rowsSwapRanges[middleIndex];
// if (horizontal) {
// if (movingDirection === 1) {
// if (inRange(activeRowRightX, ...middleRowSwapRanges.left)) break;
// else if (activeRowRightX < middleRowSwapRanges.left[0]) endIndex = middleIndex;
// else if (activeRowRightX > middleRowSwapRanges.left[1]) startIndex = middleIndex;
// } else {
// if (inRange(activeRowLeftX, ...middleRowSwapRanges.right)) break;
// else if (activeRowLeftX < middleRowSwapRanges.right[0]) endIndex = middleIndex;
// else if (activeRowLeftX > middleRowSwapRanges.right[1]) startIndex = middleIndex;
// }
// } else {
// if (movingDirection === 1) {
// if (inRange(activeRowBottomY, ...middleRowSwapRanges.top)) break;
// else if (activeRowBottomY < middleRowSwapRanges.top[0]) endIndex = middleIndex;
// else if (activeRowBottomY > middleRowSwapRanges.top[1]) startIndex = middleIndex;
// } else {
// if (inRange(activeRowTopY, ...middleRowSwapRanges.bottom)) break;
// else if (activeRowTopY < middleRowSwapRanges.bottom[0]) endIndex = middleIndex;
// else if (activeRowTopY > middleRowSwapRanges.bottom[1]) startIndex = middleIndex;
// }
// }
// }
// return {rowKey: order[middleIndex], rowIndex: middleIndex};
}
_scrollOnMove(e) {
@ -595,9 +713,11 @@ export default class SortableList extends Component {
const prevMovingDirection = this._movingDirection;
this._activeRowLocation = location;
this._movingDirection = this.props.horizontal
? prevMovingRowX < this._activeRowLocation.x
: prevMovingRowY < this._activeRowLocation.y;
this._movingDirection = (
this.props.horizontal
? prevMovingRowX <= this._activeRowLocation.x
: prevMovingRowY <= this._activeRowLocation.y
) ? 1 : -1;
this._movingDirectionChanged = prevMovingDirection !== this._movingDirection;
this._setOrderOnMove();

3
src/utils/inRange.js Normal file
View File

@ -0,0 +1,3 @@
export default function inRange(number, start, end) {
return start <= number && number <= end;
}

View File

@ -1,7 +1,3 @@
import shallowEqual from './shallowEqual';
import swapArrayElements from './swapArrayElements';
export {
shallowEqual,
swapArrayElements
};
export {default as inRange} from './inRange'
export {default as shallowEqual} from './shallowEqual';
export {default as swapArrayElements} from './swapArrayElements';