Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 105 additions & 14 deletions lib/AutoDragSortableView.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ const {width,height} = Dimensions.get('window')
const defaultZIndex = 8
const touchZIndex = 99

function determineInitialItemRenderCountForOptimization(itemHeight, columnCount) {
return Math.max(Math.ceil(Dimensions.get("window").height / itemHeight) * columnCount, 15);
}

export default class AutoDragSortableView extends Component{

constructor(props) {
Expand All @@ -20,6 +24,9 @@ export default class AutoDragSortableView extends Component{
// this.reComplexDataSource(true,props) // react < 16.3
// react > 16.3 Fiber
const rowNum = parseInt(props.parentWidth / itemWidth);

const forLoadOptimizationInitialRenderCount = determineInitialItemRenderCountForOptimization(itemHeight, rowNum);

const dataSource = props.dataSource.map((item, index) => {
const newData = {}
const left = (index % rowNum) * itemWidth
Expand All @@ -34,16 +41,25 @@ export default class AutoDragSortableView extends Component{
y: parseInt(top + 0.5),
})
newData.scaleValue = new Animated.Value(1)
newData.shouldRender = props.enableInitialLoadOptimization ? index < forLoadOptimizationInitialRenderCount : true;
return newData
});
this.state = {
dataSource: dataSource,
curPropsDataSource: props.dataSource,
height: Math.ceil(dataSource.length / rowNum) * itemHeight,
columnCount: rowNum,
rowCount: Math.ceil(dataSource.length / rowNum),
itemWidth,
itemHeight,
};

this.viewableItemsData = {
layoutDimensions: undefined,
mostRecentStartItemIndex: undefined,
mostRecentEndItemIndex: undefined,
};

this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => {
Expand Down Expand Up @@ -76,6 +92,7 @@ export default class AutoDragSortableView extends Component{
if (nextprops.dataSource != prevState.curPropsDataSource || itemWidth !== prevState.itemWidth || itemHeight !== prevState.itemHeight) {

const rowNum = parseInt(nextprops.parentWidth / itemWidth);
const forLoadOptimizationInitialRenderCount = determineInitialItemRenderCountForOptimization(itemHeight, rowNum);
const dataSource = nextprops.dataSource.map((item, index) => {
const newData = {};
const left = index % rowNum * itemWidth;
Expand All @@ -90,12 +107,17 @@ export default class AutoDragSortableView extends Component{
y: parseInt(top + 0.5),
});
newData.scaleValue = new Animated.Value(1);
newData.shouldRender = nextprops.enableInitialLoadOptimization ?
(prevState.dataSource[index] !== undefined ? prevState.dataSource[index].shouldRender : index < forLoadOptimizationInitialRenderCount) :
true;
return newData;
});
return {
dataSource: dataSource,
curPropsDataSource: nextprops.dataSource,
height: Math.ceil(dataSource.length / rowNum) * itemHeight,
columnCount: rowNum,
rowCount: Math.ceil(dataSource.length / rowNum),
itemWidth,
itemHeight,
}
Expand All @@ -110,6 +132,11 @@ export default class AutoDragSortableView extends Component{

componentDidUpdate() {
this.autoMeasureHeight()
if (this.state.columnCount !== prevState.columnCount || this.state.rowCount !== prevState.rowCount || this.state.dataSource !== prevState.dataSource) {
this.viewableItemsData.mostRecentStartItemIndex = undefined;
this.viewableItemsData.mostRecentEndItemIndex = undefined;
}
this.updateViewableItems();
}

// Compatible with different systems and paging loading
Expand Down Expand Up @@ -582,10 +609,68 @@ export default class AutoDragSortableView extends Component{
offsetY: nativeEvent.contentOffset.y,
hasScroll: true,
}
this.updateViewableItems();
if (nativeEvent.contentOffset.y !== 0) this.isHasMeasure = true;
if (this.props.onScrollListener) this.props.onScrollListener(event)
}

handleLayout = ({ nativeEvent}) => {
this.viewableItemsData.layoutDimensions = nativeEvent.layout;
this.updateViewableItems();
}

updateViewableItems = () => {
if (!this.props.enableInitialLoadOptimization) {
return;
}
const windowHeight = this.curScrollData !== undefined ? this.curScrollData.windowHeight : (this.viewableItemsData.layoutDimensions !== undefined ? this.viewableItemsData.layoutDimensions.height : 0);
const rowHeight = this.state.itemHeight;
const maxRowsVisibleOnScreen = (1 + Math.ceil(windowHeight / rowHeight));

const offsetY = this.curScrollData !== undefined ? this.curScrollData.offsetY : 0;

// In the following two calculations, think of maxRowsVisibleOnScreen as
// like "page size" or "batch size" (where a batch or page is measured in #
// of rows, not items)
const topRowIndex = Math.floor(Math.floor(offsetY / rowHeight) / maxRowsVisibleOnScreen) * maxRowsVisibleOnScreen;
// The `+ (maxRowsVisibleOnScreen*2)` means we render EVEN MORE than what
// is visible (like an additional "batch")
const bottomRowIndex = Math.ceil(Math.ceil((offsetY + windowHeight) / rowHeight) / maxRowsVisibleOnScreen) * maxRowsVisibleOnScreen + (maxRowsVisibleOnScreen*2);

const startItemIndex = Math.max(topRowIndex, 0) * this.state.columnCount;
const endItemIndex = Math.min(bottomRowIndex * this.state.columnCount + this.state.columnCount - 1, this.state.dataSource.length - 1);

// This is a small perf optimization to avoid going through the loop if we just did these rows
if (startItemIndex === this.viewableItemsData.mostRecentStartItemIndex && endItemIndex === this.viewableItemsData.mostRecentEndItemIndex) {
// No need to do any updates
return;
}

this.viewableItemsData.mostRecentStartItemIndex = startItemIndex;
this.viewableItemsData.mostRecentEndItemIndex = endItemIndex;

let changed = false;

for (let i = startItemIndex; i <= endItemIndex; i++) {
const item = this.state.dataSource[i];
if (item.shouldRender) {
continue;
}
item.shouldRender = true;
changed = true;
}

if (changed) {
// This looks like it doesn't do anything but actually it triggers a
// re-render since this component isn't a PureComponent. The reason we
// do this is because the update to `shouldRender` above won't by
// itself trigger a re-render because React has no idea that some
// object nested within another object that is part of state has
// changed.
this.setState({ dataSource: this.state.dataSource })
}
}

render() {
return (
<ScrollView
Expand All @@ -599,7 +684,9 @@ export default class AutoDragSortableView extends Component{
}}
scrollEnabled = {this.state.scrollEnabled}
onScroll={this.onScrollListener}
style={styles.container}>
style={styles.container}
onLayout={this.handleLayout}
>
{this.props.renderHeaderView ? this.props.renderHeaderView : null}
<View
//ref={(ref)=>this.sortParentRef=ref}
Expand Down Expand Up @@ -644,19 +731,21 @@ export default class AutoDragSortableView extends Component{
opacity: item.scaleValue.interpolate({inputRange,outputRange}),
transform: [transformObj]
}]}>
<TouchableOpacity
activeOpacity = {1}
delayLongPress={this.props.delayLongPress}
onPressOut={()=> this.onPressOut()}
onLongPress={()=>this.startTouch(index)}
onPress={()=>{
if (this.props.onClickItem) {
this.isHasMeasure = true
this.props.onClickItem(this.getOriginalData(),item.data,index)
}
}}>
{this.props.renderItem(item.data,index)}
</TouchableOpacity>
{(!this.props.enableInitialLoadOptimization || item.shouldRender) &&
<TouchableOpacity
activeOpacity = {1}
delayLongPress={this.props.delayLongPress}
onPressOut={()=> this.onPressOut()}
onLongPress={()=>this.startTouch(index)}
onPress={()=>{
if (this.props.onClickItem) {
this.isHasMeasure = true
this.props.onClickItem(this.getOriginalData(),item.data,index)
}
}}>
{this.props.renderItem(item.data,index)}
</TouchableOpacity>
}
</Animated.View>
)
})
Expand All @@ -682,6 +771,8 @@ AutoDragSortableView.propTypes = {

sortable: PropTypes.bool,

enableInitialLoadOptimization: PropTypes.bool,

onClickItem: PropTypes.func,
onDragStart: PropTypes.func,
onDragEnd: PropTypes.func,
Expand Down
2 changes: 2 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ interface IProps{

sortable?: boolean;

enableInitialLoadOptimization?: boolean;

onClickItem?: (data: any[],item: any,index: number) => void;
onDragStart?: (fromIndex: number) => void;
onDragEnd?: (fromIndex: number,toIndex: number) => void;
Expand Down