针对该插件无法缓存管理,进行改造,使其兼容react-native-fetch-blob插件,改造点:
export interface Progress {
received: number
total: number
}
/**
* 图片信息(被缓存则来自于本地)
*/
cachedImageUrls?: Array<ImageInfo>
/**
* 下载进度内容
*/
progresses?: Array<Progress>
// 给 imageSizes 塞入空数组
const imageSizes: Array<typings.ImageSize> = [];
const progresses: Array<typings.Progress> = [];
nextProps.imageUrls.forEach(imageUrl => {
imageSizes.push({
width: imageUrl.width || 0,
height: imageUrl.height || 0,
status: 'loading'
});
progresses.push({
received: 0,
total: 0
});
})
this.setState({
currentShowIndex: nextProps.index,
imageSizes,
progresses
}
//生成随机ID:GUID
GUID(){
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
}).toUpperCase();
}
//存储文件缓存信息
saveImageCachePath(url: string, localPath: string,callback?: (error?:Error) => void) {
AsyncStorage.getItem('imageCache').then((result: string)=>{
if (null == result) {
result = '{}';
}
//添加图片缓存
let imgCache = JSON.parse(result);
imgCache[url] = localPath;
//保存
AsyncStorage.setItem('imageCache', JSON.stringify(imgCache), callback);
});
}
//读取全部缓存配置信息
loadImageCache(callback?: (result: any) => void) {
AsyncStorage.getItem('imageCache').then((result: any)=>{
if (null != result) {
result = '{}';
//添加图片缓存
let imgCache = JSON.parse(result);
callback(imgCache);
} else {
callback(null);
}
}).catch(()=>{
callback(null);
});
}
private cachedImageUrls = new Array();
componentWillReceiveProps(nextProps: typings.PropsDefine) {
if (nextProps.index !== this.state.currentShowIndex) {
this.setState({
currentShowIndex: nextProps.index
}, () => {
//进行加载图片操作之前先要对传入的url进行核实,如果已经缓存的话则不再进行网络加载操作
this.cachedImageUrls = this.props.imageUrls.slice();
this.loadImageCache((cache) => {
if (null != cache) {
//对缓存进行检测
this.cachedImageUrls.forEach(imageUrl => {
let {url, originUrl} = imageUrl;
if (cache[url]) imageUrl.url = cache[url];
if (cache[originUrl]) imageUrl.originUrl = cache[originUrl];
});
}
// 立刻预加载要看的图
this.loadImage(nextProps.index)
this.jumpToCurrentImage()
// 显示动画
Animated.timing(this.fadeAnim, {
toValue: 1,
duration: 200
}).start()
this.setState({ cachedImageUrls: this.cachedImageUrls });
})
});
}
}
this.setState({
currentShowIndex: nextProps.index,
imageSizes,
progresses
}, () => {
//进行加载图片操作之前先要对传入的url进行核实,如果已经缓存的话则不再进行网络加载操作
this.cachedImageUrls = this.props.imageUrls.slice();
this.loadImageCache((cache) => {
if (null != cache) {
//对缓存进行检测
this.cachedImageUrls.forEach(imageUrl => {
let {url, originUrl} = imageUrl;
if (cache[url]) imageUrl.url = cache[url];
if (cache[originUrl]) imageUrl.originUrl = cache[originUrl];
});
}
// 立刻预加载要看的图
this.loadImage(nextProps.index)
this.jumpToCurrentImage()
// 显示动画
Animated.timing(this.fadeAnim, {
toValue: 1,
duration: 200
}).start()
this.setState({ cachedImageUrls: this.cachedImageUrls });
})
})
if (Platform.OS !== 'web' as any) {
const prefetchImagePromise = Image.prefetch(image.url)
if (image.url.match(/^(http|https):\/\//)) {
prefetchImagePromise.then(() => {
imageLoaded = true;
if (sizeLoaded) {
imageStatus.status = 'success';
saveImageSize();
}
}, () => {
imageStatus.status = 'fail';
saveImageSize();
});
} else {
// 本地图片
imageLoaded = true;
prefetchImagePromise.then(() => { }).catch(() => { });
if (sizeLoaded) {
imageStatus.status = 'success';
saveImageSize();
}
}
if (Platform.OS !== 'web' as any) {
//const prefetchImagePromise = Image.prefetch(image.url)
if (image.url.match(/^(http|https):\/\//)) {
let dirs = RNFetchBlob.fs.dirs;
RNFetchBlob.config({
path: `${dirs.DocumentDir}/ImageCache/${this.GUID()}`
})
.fetch('GET',image.url)
.progress((received:number, total:number) => {
//重新设置进度
let progress = Object.assign({}, this.state.progresses[index]);
progress.received = received;
progress.total = total;
let progresses = this.state.progresses.slice();
progresses[index] = progress;
this.setState({ progresses: progresses });
}).then((res: any) => {
//将图片缓存路径写入配置
this.saveImageCachePath(image.url, res.path, (error) => {
if (null != error) {
//配置保存完之后对缓存进行更新
this.cachedImageUrls[index].url = res.path();
}
});
image.url = res.path();
Image.prefetch(image.url).then(() => { }).catch(() => { });
imageLoaded = true;
if (sizeLoaded) {
imageStatus.status = 'success';
saveImageSize();
}
}).catch(() => {
imageStatus.status = 'fail';
saveImageSize();
});
} else {
// 本地图片
imageLoaded = true;
Image.prefetch(image.url).then(() => { }).catch(() => { });
if (sizeLoaded) {
imageStatus.status = 'success';
saveImageSize();
}
}
/**
* 获得整体内容
*/
getContent() {
// 获得屏幕宽高
const screenWidth = this.width
const screenHeight = this.height
const ImageElements = this.cachedImageUrls.map((image, index) => {
let width = this.state.imageSizes[index] && this.state.imageSizes[index].width
let height = this.state.imageSizes[index] && this.state.imageSizes[index].height
const imageInfo = this.state.imageSizes[index]
// 如果宽大于屏幕宽度,整体缩放到宽度是屏幕宽度
if (width > screenWidth) {
const widthPixel = screenWidth / width
width *= widthPixel
height *= widthPixel
}
// 如果此时高度还大于屏幕高度,整体缩放到高度是屏幕高度
if (height > screenHeight) {
const HeightPixel = screenHeight / height
width *= HeightPixel
height *= HeightPixel
}
if (imageInfo.status === 'success' && this.props.enableImageZoom) {
return (
<ImageZoom key={index}
style={this.styles.modalContainer}
cropWidth={this.width}
cropHeight={this.height}
imageWidth={width}
imageHeight={height}
maxOverflow={this.props.maxOverflow}
horizontalOuterRangeOffset={this.handleHorizontalOuterRangeOffset.bind(this)}
responderRelease={this.handleResponderRelease.bind(this)}
onLongPress={this.handleLongPress.bind(this, image)}
onClick={this.handleClick.bind(this)}
onDoubleClick={this.handleDoubleClick.bind(this)}>
<Image style={Object.assign({}, this.styles.imageStyle, { width: width, height: height })}
source={{ uri: image.url }} />
</ImageZoom>
)
} else {
switch (imageInfo.status) {
case 'loading':
return (
<TouchableHighlight key={index}
onPress={this.handleClick.bind(this)}
style={this.styles.loadingTouchable}>
<View style={this.styles.loadingContainer}>
{this.props.loadingRender()}
</View>
</TouchableHighlight>
)
case 'success':
return (
<Image key={index}
style={Object.assign({}, this.styles.imageStyle, { width: width, height: height })}
source={{ uri: image.url }} />
)
case 'fail':
return (
<ImageZoom key={index}
style={this.styles.modalContainer}
cropWidth={this.width}
cropHeight={this.height}
imageWidth={width}
imageHeight={height}
maxOverflow={this.props.maxOverflow}
horizontalOuterRangeOffset={this.handleHorizontalOuterRangeOffset.bind(this)}
responderRelease={this.handleResponderRelease.bind(this)}
onLongPress={this.handleLongPress.bind(this, image)}
onClick={this.handleClick.bind(this)}
onDoubleClick={this.handleDoubleClick.bind(this)}>
<TouchableOpacity key={index}
style={this.styles.failContainer}>
<Image source={this.props.failImageSource}
style={this.styles.failImage} />
</TouchableOpacity>
</ImageZoom>
)
}
}
})
return (
<Animated.View style={Object.assign({}, this.styles.container, { opacity: this.fadeAnim })}>
{this.props.renderHeader(this.state.currentShowIndex)}
<View style={this.styles.arrowLeftContainer}>
<TouchableWithoutFeedback onPress={this.goBack.bind(this)}>
<View>
{this.props.renderArrowLeft()}
</View>
</TouchableWithoutFeedback>
</View>
<View style={this.styles.arrowRightContainer}>
<TouchableWithoutFeedback onPress={this.goNext.bind(this)}>
<View>
{this.props.renderArrowRight()}
</View>
</TouchableWithoutFeedback>
</View>
<Animated.View style={Object.assign({}, this.styles.moveBox, { transform: [{ translateX: this.positionX }] }, { width: this.width * this.props.imageUrls.length })}>
{ImageElements}
</Animated.View>
{this.props.imageUrls.length > 1 &&
this.props.renderIndicator(this.state.currentShowIndex + 1, this.props.imageUrls.length)
}
{this.cachedImageUrls[this.state.currentShowIndex].originSizeKb && this.cachedImageUrls[this.state.currentShowIndex].originUrl &&
<View style={this.styles.watchOrigin}>
<TouchableOpacity style={this.styles.watchOriginTouchable}>
<Text style={this.styles.watchOriginText}>查看原图(2M)</Text>
</TouchableOpacity>
</View>
}
{this.props.renderFooter(this.state.currentShowIndex)}
</Animated.View>
)
}
注:对于显示原图的功能插件作者似乎只是显示了按钮并没有完成它,日后如果有需要的话会对插件进行进一步处理以满足需求
/**
* 保存当前图片到本地相册
*/
saveToLocal() {
if (!this.props.onSave) {
CameraRoll.saveToCameraRoll(this.cachedImageUrls[this.state.currentShowIndex].url)
this.props.onSaveToCamera(this.state.currentShowIndex)
} else {
this.props.onSave(this.cachedImageUrls[this.state.currentShowIndex].url)
}
this.setState({
isShowMenu: false
})
}
【编译内容】 改完之后需要编译内容以生效,终端cd到插件目录《项目路径/node_modules/react-native-image-zoom-viewer》并执行yarn进行编译操作,编译完成后重启react-native才可以生效
Depend on react-native-image-pan-zoom
: https://github.com/ascoders/react-native-image-zoom