react-native-img-browser
Version:
此组件基于react-native-photo-browser@0.4.0进行修改,主要修复其大量图片时,滑动动画不流畅;图片多时打开大图加载卡顿或加载不出来,将react-native-photo-browser中的FullScreenContainer文件中的ListView组件替换成FlatList组件
334 lines (288 loc) • 9.95 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import {
Animated,
Dimensions,
ListView,
View,
StyleSheet,
ViewPropTypes
} from 'react-native';
import Constants from './constants';
import { TopBar } from './bar';
import GridContainer from './GridContainer';
import FullScreenContainer from './FullScreenContainer';
const TOOLBAR_HEIGHT = Constants.TOOLBAR_HEIGHT;
export default class PhotoBrowser extends React.Component {
static propTypes = {
style: ViewPropTypes.style,
mediaList: PropTypes.array.isRequired,
/*
* if thumbnails should have same height and width
*/
square: PropTypes.bool,
/*
* offsets the width of the grid
*/
gridOffset: PropTypes.number,
/*
* set the current visible photo before displaying
*/
initialIndex: PropTypes.number,
/*
* Allows to control whether the bars and controls are always visible
* or whether they fade away to show the photo full
*/
alwaysShowControls: PropTypes.bool,
/*
* Show action button to allow sharing, copying, etc
*/
displayActionButton: PropTypes.bool,
/*
* Whether to display left and right nav arrows on bottom toolbar
*/
displayNavArrows: PropTypes.bool,
/*
* Whether to keeep status bar visible even when controls are hidden in full screen mode
*/
alwaysDisplayStatusBar: PropTypes.bool,
/*
* Whether to allow the viewing of all the photo thumbnails on a grid
*/
enableGrid: PropTypes.bool,
/*
* Whether to start on the grid of thumbnails instead of the first photo
*/
startOnGrid: PropTypes.bool,
/*
* Whether selection buttons are shown on each image
*/
displaySelectionButtons: PropTypes.bool,
/*
* Called when a media item is selected or unselected
*/
onSelectionChanged: PropTypes.func,
/*
* Called when action button is pressed for a media
* If you don't provide this props, ActionSheetIOS will be opened as default
*/
onActionButton: PropTypes.func,
/*
* displays Progress.Circle instead of default Progress.Bar for full screen photos
* iOS only
*/
useCircleProgress: PropTypes.bool,
/*
* Called when done or back button is tapped.
* Back button will not be displayed if this is null.
*/
onBack: PropTypes.func,
/*
* Sets images amount in grid row, default - 3 (defined in GridContainer)
*/
itemPerRow: PropTypes.number,
/*
* Display top bar
*/
displayTopBar: PropTypes.bool,
/*
* Applied on Photo components' parent TouchableOpacity
*/
onPhotoLongPress: PropTypes.func,
delayPhotoLongPress: PropTypes.number,
};
static defaultProps = {
mediaList: [],
initialIndex: 0,
square: false,
alwaysShowControls: false,
displayActionButton: false,
displayNavArrows: false,
alwaysDisplayStatusBar: false,
enableGrid: true,
startOnGrid: false,
displaySelectionButtons: false,
useCircleProgress: false,
onSelectionChanged: () => {},
displayTopBar: true,
onPhotoLongPress: () => {},
delayPhotoLongPress: 1000,
gridOffset: 0,
};
constructor(props, context) {
super(props, context);
this._onGridPhotoTap = this._onGridPhotoTap.bind(this);
this._onGridButtonTap = this._onGridButtonTap.bind(this);
this._onMediaSelection = this._onMediaSelection.bind(this);
this._updateTitle = this._updateTitle.bind(this);
this._toggleTopBar = this._toggleTopBar.bind(this);
const { mediaList, startOnGrid, initialIndex } = props;
this.state = {
dataSource: this._createDataSource(mediaList),
mediaList,
isFullScreen: !startOnGrid,
fullScreenAnim: new Animated.Value(startOnGrid ? 0 : 1),
currentIndex: initialIndex,
displayTopBar: props.displayTopBar,
};
}
componentWillReceiveProps(nextProps) {
const mediaList = nextProps.mediaList;
this.setState({
dataSource: this._createDataSource(mediaList),
mediaList,
});
}
_createDataSource(list) {
const dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
});
return dataSource.cloneWithRows(list);
}
_onGridPhotoTap(index) {
// console.info("index",index)
this.refs.fullScreenContainer.openPage(index, false);
this._toggleFullScreen(true);
}
_onGridButtonTap() {
this._toggleFullScreen(false);
}
_onMediaSelection(index, isSelected) {
const {
mediaList: oldMediaList,
dataSource,
} = this.state;
const newMediaList = oldMediaList.slice();
const selectedMedia = {
...oldMediaList[index],
selected: isSelected,
};
newMediaList[index] = selectedMedia;
this.setState({
dataSource: dataSource.cloneWithRows(newMediaList),
mediaList: newMediaList,
});
this.props.onSelectionChanged(selectedMedia, index, isSelected);
}
_updateTitle(title) {
this.setState({ title });
}
_toggleTopBar(displayed: boolean) {
if (this.props.displayTopBar) {
this.setState({
displayTopBar: displayed,
});
}
}
_toggleFullScreen(display: boolean) {
this.setState({
isFullScreen: display,
});
Animated.timing(
this.state.fullScreenAnim,
{
toValue: display ? 1 : 0,
duration: 300,
}
).start();
}
render() {
const {
alwaysShowControls,
displayNavArrows,
alwaysDisplayStatusBar,
displaySelectionButtons,
displayActionButton,
enableGrid,
useCircleProgress,
onActionButton,
onBack,
itemPerRow,
style,
square,
gridOffset,
} = this.props;
const {
dataSource,
mediaList,
isFullScreen,
fullScreenAnim,
currentIndex,
title,
displayTopBar,
} = this.state;
const screenHeight = Dimensions.get('window').height;
let gridContainer;
let fullScreenContainer;
if (mediaList.length > 0) {
if (enableGrid) {
gridContainer = (
<Animated.View
style={{
height: screenHeight,
marginTop: fullScreenAnim.interpolate({
inputRange: [0, 1],
outputRange: [0, screenHeight * -1 - TOOLBAR_HEIGHT],
}),
}}
>
<GridContainer
square={square}
offset={gridOffset}
dataSource={dataSource}
displaySelectionButtons={displaySelectionButtons}
onPhotoTap={this._onGridPhotoTap}
onMediaSelection={this._onMediaSelection}
itemPerRow={itemPerRow}
/>
</Animated.View>
);
}
fullScreenContainer = (
<FullScreenContainer
ref="fullScreenContainer"
dataSource={dataSource}
mediaList={mediaList}
initialIndex={currentIndex}
alwaysShowControls={alwaysShowControls}
displayNavArrows={displayNavArrows}
alwaysDisplayStatusBar={alwaysDisplayStatusBar}
displaySelectionButtons={displaySelectionButtons}
displayActionButton={displayActionButton}
enableGrid={enableGrid}
useCircleProgress={useCircleProgress}
onActionButton={onActionButton}
onMediaSelection={this._onMediaSelection}
onGridButtonTap={this._onGridButtonTap}
updateTitle={this._updateTitle}
toggleTopBar={this._toggleTopBar}
bottomBarComponent={this.props.bottomBarComponent}
onPhotoLongPress={this.props.onPhotoLongPress}
delayLongPress={this.props.delayPhotoLongPress}
/>
);
}
const TopBarComponent = this.props.topBarComponent || TopBar;
return (
<View style={[styles.container, {
paddingTop: gridContainer ? TOOLBAR_HEIGHT : 0,
}, style]}>
{gridContainer}
{fullScreenContainer}
{/* this is here for bigger z-index purpose */}
<TopBarComponent
height={TOOLBAR_HEIGHT}
displayed={displayTopBar}
title={isFullScreen ? title : `${mediaList.length} 照片`}
onBack={onBack}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'black',
},
});