react-native-img-browser
Version:
此组件基于react-native-photo-browser@0.4.0进行修改,主要修复其大量图片时,滑动动画不流畅;图片多时打开大图加载卡顿或加载不出来,将react-native-photo-browser中的FullScreenContainer文件中的ListView组件替换成FlatList组件
281 lines (242 loc) • 7.38 kB
JavaScript
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
Dimensions,
Image,
StyleSheet,
View,
TouchableWithoutFeedback,
ActivityIndicator,
Platform,
} from 'react-native';
import * as Progress from 'react-native-progress-cus';
export default class Photo extends Component {
static propTypes = {
/*
* image uri or opaque type that is passed as source object to image component
*/
uri: PropTypes.oneOfType([
// assets or http url
PropTypes.string,
// Opaque type returned by require('./image.jpg')
PropTypes.number,
]).isRequired,
/*
* displays a check button above the image
*/
displaySelectionButtons: PropTypes.bool,
/*
* image resizeMode
*/
resizeMode: PropTypes.string,
/*
* these values are set to image and it's container
* screen width and height are used if those are not defined
*/
width: PropTypes.number,
height: PropTypes.number,
/*
* when lazyLoad is true,
* image is not loaded until 'load' method is manually executed
*/
lazyLoad: PropTypes.bool,
/*
* displays selected or unselected icon based on this prop
*/
selected: PropTypes.bool,
/*
* size of selection images are decided based on this
*/
thumbnail: PropTypes.bool,
/*
* executed when user selects/unselects the photo
*/
onSelection: PropTypes.func,
/*
* image tag generated using require(asset_path)
*/
progressImage: PropTypes.number,
/*
* displays Progress.Circle instead of default Progress.Bar
* it's ignored when progressImage is also passed.
* iOS only
*/
useCircleProgress: PropTypes.bool,
};
static defaultProps = {
resizeMode: 'contain',
thumbnail: false,
lazyLoad: false,
selected: false,
};
constructor(props) {
super(props);
this._onProgress = this._onProgress.bind(this);
this._onError = this._onError.bind(this);
this._onLoad = this._onLoad.bind(this);
this._toggleSelection = this._toggleSelection.bind(this);
const { lazyLoad, uri } = props;
this.state = {
uri: lazyLoad ? null : uri,
progress: 0,
error: false,
};
}
load() {
if (!this.state.uri) {
this.setState({
uri: this.props.uri,
});
}
}
_onProgress(event) {
const progress = event.nativeEvent.loaded / event.nativeEvent.total;
if (!this.props.thumbnail && progress !== this.state.progress) {
this.setState({
progress,
});
}
}
_onError() {
this.setState({
error: true,
progress: 1,
});
}
_onLoad() {
this.setState({
progress: 1,
});
}
_toggleSelection() {
// onSelection is resolved in index.js
// and refreshes the dataSource with new media object
this.props.onSelection(!this.props.selected);
}
_renderProgressIndicator() {
const { progressImage, useCircleProgress } = this.props;
const { progress } = this.state;
// return null;
if (progress < 1) {
if (progressImage) {
return (
<Image
source={progressImage}
/>
);
}
if (Platform.OS === 'android') {
return <ActivityIndicator animating={ true }/>;
}
const ProgressElement = useCircleProgress ? Progress.Circle : Progress.Bar;
return (
<ProgressElement
progress={progress}
thickness={20}
color={'white'}
/>
);
}
return null;
}
_renderErrorIcon() {
return (
<Image
source={require('../../Assets/image-error.png')}
/>
);
}
_renderSelectionButton() {
const { progress } = this.state;
const { displaySelectionButtons, selected, thumbnail } = this.props;
// do not display selection before image is loaded
if (!displaySelectionButtons || progress < 1) {
return null;
}
let buttonImage;
if (thumbnail) {
let icon = require('../../Assets/small-selected-off.png');
if (selected) {
icon = require('../../Assets/small-selected-on.png');
}
buttonImage = (
<Image
source={icon}
style={styles.thumbnailSelectionIcon}
/>
);
} else {
let icon = require('../../Assets/selected-off.png');
if (selected) {
icon = require('../../Assets/selected-on.png');
}
buttonImage = (
<Image
style={styles.fullScreenSelectionIcon}
source={icon}
/>
);
}
return (
<TouchableWithoutFeedback onPress={this._toggleSelection}>
{buttonImage}
</TouchableWithoutFeedback>
);
}
render() {
const { resizeMode, width, height } = this.props;
const screen = Dimensions.get('window');
const { uri, error } = this.state;
let source;
if (uri) {
// create source objects for http/asset strings
// or directly pass uri number for local files
source = typeof uri === 'string' ? { uri } : uri;
}
// i had to get window size and set photo size here
// to be able to respond device orientation changes in full screen mode
// FIX_ME: when you have a better option
const sizeStyle = {
width: width || screen.width,
height: height || screen.height,
};
return (
<View style={[styles.container, sizeStyle]}>
{error ? this._renderErrorIcon() : this._renderProgressIndicator()}
<Image
{...this.props}
style={[styles.image, sizeStyle]}
source={source}
onProgress={this._onProgress}
onError={this._onError}
onLoad={this._onLoad}
resizeMode={resizeMode}
/>
{this._renderSelectionButton()}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
alignItems: 'center',
justifyContent: 'center',
},
image: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
thumbnailSelectionIcon: {
position: 'absolute',
top: 8,
right: 8,
},
fullScreenSelectionIcon: {
position: 'absolute',
top: 60,
right: 16,
},
});