Skip to content

Instantly share code, notes, and snippets.

@asgvard
Created November 10, 2017 20:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save asgvard/1951f0c1f81d47b5b286228e01d34271 to your computer and use it in GitHub Desktop.
Save asgvard/1951f0c1f81d47b5b286228e01d34271 to your computer and use it in GitHub Desktop.
Horizontal Paging Swiper based on FlatList
/**
* TODO Finish this for optimized Android swiping
* Inspired by react-native-swiper but based on cross-platform and optimized FlatList
*/
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
StyleSheet,
FlatList,
View
} from 'react-native';
import {DEVICE_HEIGHT, DEVICE_WIDTH} from '../../utils/device';
import AppColors from '../../styles/styles';
import {dummy} from '../../utils/generic';
import {isAndroid} from '../../utils/platform';
const styles = StyleSheet.create({
container: {
backgroundColor: AppColors.transparent,
flex: 1
}
});
class FlatSwiper extends Component {
constructor(props) {
super(props);
this.state = {
width: DEVICE_WIDTH,
height: DEVICE_HEIGHT,
index: 0
};
this.listRef = null;
this.onLayout = this.onLayout.bind(this);
this.onTouchStart = this.onTouchStart.bind(this);
this.onTouchEnd = this.onTouchEnd.bind(this);
this.onScrollEnd = this.onScrollEnd.bind(this);
this.getItemLayout = this.getItemLayout.bind(this);
this.onRefReady = this.onRefReady.bind(this);
this.renderItem = this.renderItem.bind(this);
this.scrollToIndex = this.scrollToIndex.bind(this);
}
onLayout({nativeEvent: {layout: {width, height}}}) {
this.setState({
width,
height
});
}
onTouchStart() {
console.log('onTouchStart');
this.props.onTouchStart();
}
onTouchEnd() {
console.log('onTouchEnd');
this.props.onTouchEnd();
}
onScrollEnd({nativeEvent: {contentOffset: {x}}}) {
console.log('onScrollEnd');
const newIndex = this.state.width > 0 ? Math.ceil(x / this.state.width) : 0;
if (newIndex !== this.state.index) {
this.setState({
index: newIndex
}, () => {
this.props.onIndexChange(newIndex);
});
}
}
onRefReady(reference) {
reference && (this.listRef = reference);
}
getItemLayout(data, index) {
return {
length: this.state.width,
offset: this.state.width * index,
index
};
}
scrollToIndex(index) {
console.log('scrollToIndex');
this.listRef && this.listRef.scrollToIndex({index});
/**
* Android hack to manually trigger onScrollEnd
* It doesn't trigger when manually scrolling
* <3 Android
*/
isAndroid() && this.onScrollEnd({
nativeEvent: {
contentOffset: {
x: index * this.state.width
}
}
});
}
renderItem({item, index}) {
const {state: {width, height}} = this;
const itemWrapperStyle = {
width,
height
};
return (<View style={itemWrapperStyle}>
{this.props.renderItem(item, index)}
</View>);
}
render() {
return (<FlatList
style={styles.container}
onLayout={this.onLayout}
data={this.props.data}
extraData={this.props.extraData}
pagingEnabled
horizontal
showsHorizontalScrollIndicator={false}
onTouchStart={this.onTouchStart}
onTouchEnd={this.onTouchEnd}
onMomentumScrollEnd={this.onScrollEnd}
onScrollBeginDrag={() => console.log('onScrollBeginDrag')}
onScrollEndDrag={() => console.log('onScrollEndDrag')}
onMomentumScrollBegin={() => console.log('onMomentumScrollBegin')}
onScrollShouldSetResponder={() => console.log('onScrollShouldSetResponder')}
onStartShouldSetResponder={() => console.log('onStartShouldSetResponder')}
onStartShouldSetResponderCapture={() => console.log('onStartShouldSetResponderCapture')}
onResponderGrant={() => console.log('onResponderGrant')}
onResponderReject={() => console.log('onResponderReject')}
onResponderRelease={() => console.log('onResponderRelease')}
onResponderTerminate={() => console.log('onResponderTerminate')}
onResponderTerminationRequest={() => console.log('onResponderTerminationRequest')}
onScroll={() => console.log('onScroll')}
keyExtractor={this.props.itemKeyExtractor}
getItemLayout={this.getItemLayout}
ref={this.onRefReady}
renderItem={this.renderItem}
/>);
}
}
FlatSwiper.propTypes = {
data: PropTypes.array.isRequired,
extraData: PropTypes.any,
onTouchStart: PropTypes.func,
onTouchEnd: PropTypes.func,
onIndexChange: PropTypes.func,
itemKeyExtractor: PropTypes.func.isRequired,
renderItem: PropTypes.func.isRequired
};
FlatSwiper.defaultProps = {
extraData: null,
onTouchStart: dummy,
onTouchEnd: dummy,
onIndexChange: dummy
};
export default FlatSwiper;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment