博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React Native 的图片点击放大效果的组件使用 react-native-zoom-image
阅读量:4085 次
发布时间:2019-05-25

本文共 11125 字,大约阅读时间需要 37 分钟。

1.在index..js下书写

/**
 * Created by TinySymphony on 2017-03-23.
 */
import React, {Component} 
from 
'react';
import {
  StyleSheet,
  AppRegistry,
  View,
  Text,
  Easing,
  ScrollView
from 
'react-native';
import ZoomImage 
from 
'./ZoomImage';
export default 
class 
App 
extends 
Component {
  
constructor(
props) {
    super(props);
    this.text 
= 
'';
  }
  
render() {
    
return (
      
<ScrollView
>
        
<View style
={styles.content}
>
          
<
Text style
={styles.txt}
>Zoom Image Examples
! Try to click them
~</
Text
>
          
<View style
={styles.imgItem}
>
            
<ZoomImage
              source
={
{uri
: 
'https://avatars2.githubusercontent.com/u/7685233?v=3&s=460'}}
              imgStyle
={
{width
: 
220, height
: 
220}}
              style
={styles.img}
              
/>
          
</View
>
          
<View style
={styles.imgItem}
>
            
<ZoomImage
              source
={
{uri
: 
'https://ooo.0o0.ooo/2017/03/31/58de0e9b287f6.jpg'}}
              imgStyle
={
{width
: 
250, height
: 
230}}
              style
={styles.img}
              enableScaling
={
true}
              
/>
          
</View
>
          
<View style
={styles.imgItem}
>
            
<ZoomImage
              source
={
{uri
: 
'https://ooo.0o0.ooo/2017/03/31/58de0e9b28328.jpg'}}
              imgStyle
={
{width
: 
250, height
: 
240}}
              style
={styles.img}
              easingFunc
={Easing.bounce}
              
/>
          
</View
>
        
</View
>
      
</ScrollView
>
    );
  }
}
const 
styles 
= 
StyleSheet.
create({
  content
: {
    justifyContent
: 
'center',
    alignItems
: 
'center'
  },
  txt
: {
    fontSize
: 
16,
    marginTop
: 
50,
    color
: 
'#333'
  },
  img
: {
    borderWidth
: 
3,
    borderColor
: 
'#45b7d5'
  },
  imgItem
: {
    justifyContent
: 
'center',
    alignItems
: 
'center',
    margin
: 
20
  }
});
AppRegistry.
registerComponent(
'App', () 
=> App);
// export default App;
// import { AppRegistry } from 'react-native';
// import App from './App';
// AppRegistry.registerComponent('Example', (): App => App);

2.ZoomImage.js文件

import React, {PropTypes, Component} 
from 
'react';
import {
  View,
  Text,
  Image,
  Modal,
  Easing,
  StyleSheet,
  PanResponder,
  NativeModules,
  findNodeHandle,
  Dimensions,
  TouchableWithoutFeedback
from 
'react-native';
import Animation 
from 
'./Animation';
const 
winWidth 
= Dimensions.
get(
'window').width;
const 
winHeight 
= Dimensions.
get(
'window').height;
const 
winRatio 
= winWidth 
/ winHeight;
const 
RCTUIManager 
= NativeModules.UIManager;
class 
ZoomImage 
extends 
Component {
  
static propTypes 
= {
    startCapture
: PropTypes.bool,
    moveCapture
: PropTypes.bool,
    responderNegotiate
: PropTypes.func,
    easingFunc
: PropTypes.func,
    duration
: PropTypes.number,
    enableScaling
: PropTypes.bool
  }
  
static defaultProps 
= {
    startCapture
: 
false,
    moveCapture
: 
false,
    duration
: 
800,
    easingFunc
: Easing.ease,
    enableScaling
: 
false
  }
  
constructor(
props) {
    super(props);
    this.state 
= {
      maxSize
: {
        width
: 
0,
        height
: 
0
      },
      isModalVisible
: 
false
    };
    this.enableModal 
= 
false;
    this.closeModal 
= this.closeModal.
bind(this);
    this.openModal 
= this.openModal.
bind(this);
    this.getMaxSizeByRatio 
= this.getMaxSizeByRatio.
bind(this);
  }
  
getMaxSizeByRatio (
ratio) {
    
return {
      width
: ratio 
>= winRatio 
? winWidth 
: winWidth 
/ ratio,
      height
: ratio 
>= winRatio 
? winWidth 
/ ratio 
: winHeight
    };
  }
  
componentDidMount () {
    
if (this.props.source.uri) {
      Image.
getSize(this.props.source.uri, (
w
h
=> {
        this.
setState((
state
=> {
          state.maxSize 
= this.
getMaxSizeByRatio(w 
/ h);
          this.enableModal 
= 
true;
        });
      });
    } 
else {
      this.
setState((
state
=> {
        state.maxSize 
= this.
getMaxSizeByRatio(this.props.imgStyle.width 
/ this.props.imgStyle.height);
        this.enableModal 
= 
true;
      });
    }
  }
  
openModal () {
    
if (
!this.refs.view 
|| 
!this.enableModal) 
return;
    RCTUIManager.
measure(
findNodeHandle(this.refs.view), (
x
y
w
h
px
py
=> {
      this.originPosition 
= {x, y, w, h, px, py};
    });
    this.
setState({
      isModalVisible
: 
true
    });
  }
  
closeModal () {
    this.
setState({
      isModalVisible
: 
false
    });
  }
  
render () {
    
return (
      
<TouchableWithoutFeedback style
={this.props.imgStyle}
        onPress
={this.openModal}
        ref
=
"view"
>
        
<View style
={this.props.style}
>
          
<Image
            source
={this.props.source}
            resizeMode
={this.props.resizeMode}
            style
={this.props.imgStyle}
/>
          
<ImageModal
            visible
={this.state.isModalVisible}
            onClose
={this.closeModal}
            originPosition
={this.originPosition}
            size
={this.state.maxSize}
            minAlpha
={this.props.minAlpha}
            source
={this.props.source}
            duration
={this.props.duration}
            easingFunc
={this.props.easingFunc}
            enableScaling
={this.props.enableScaling}
/>
        
</View
>
      
</TouchableWithoutFeedback
>
    );
  }
}
class 
ImageModal 
extends 
Component {
  
constructor(
props) {
    super(props);
    this._initModalStyle 
= {
      style
: {
        backgroundColor
: 
'rgba(0, 0, 0, 1)'
      }
    };
    this._modalStyle 
= 
JSON.
parse(
JSON.
stringify(this._initModalStyle));
    this._initContentStyle 
= {
      style
: {
        top
: 
0,
        bottom
: 
0,
        left
: 
0,
        right
: 
0
      }
    };
    this._contentStyle 
= 
JSON.
parse(
JSON.
stringify(this._initContentStyle));
    this._initImgSize 
= {
      style
: this.props.size
    };
    this._imgSize 
= 
JSON.
parse(
JSON.
stringify(this._initImgSize));
    this._inAnimation 
= 
false;
    this._setNativeProps 
= this._setNativeProps.
bind(this);
    this._closeModalByTap 
= this._closeModalByTap.
bind(this);
    this._closeModal 
= this._closeModal.
bind(this);
    this._rebounce 
= this._rebounce.
bind(this);
    this._touchPositionCheck 
= this._touchPositionCheck.
bind(this);
    this._updateNativeStyles 
= this._updateNativeStyles.
bind(this);
    this._pan 
= PanResponder.
create({
      onStartShouldSetPanResponder
: this._onStartShouldSetPanResponder.
bind(this),
      
onStartShouldSetPanResponderCapture
: (
evt
gestureState
=> this.props.startCapture,
      onMoveShouldSetPanResponder
: this._onMoveShouldSetPanResponder.
bind(this),
      
onMoveShouldSetPanResponderCapture
: (
evt
gestureState
=> this.props.moveCapture,
      
onPanResponderTerminationRequest
: (
evt
gestureState
=> 
true,
      onPanResponderGrant
: this._handlePanResponderGrant.
bind(this),
      onPanResponderMove
: this._handlePanResponderMove.
bind(this),
      onPanResponderRelease
: this._handlePanResponderEnd.
bind(this),
      onPanResponderTerminate
: this._handlePanResponderEnd.
bind(this),
      
onShouldBlockNativeResponder
: (
evt
gestureState
=> 
true
    });
  }
  
_onStartShouldSetPanResponder (
evt
gestureState) {
    
// set responder for tapping when the drawer is open
    
// TODO: tap close
    
if (this._inAnimation) 
return;
    
return 
false;
  }
  
_onMoveShouldSetPanResponder (
evt
gestureState) {
    
// custom pan responder condition function
    
if (this._inAnimation) 
return;
    
if (this.props.responderNegotiate 
&& this.props.
responderNegotiate(evt, gestureState) 
=== 
false
return 
false;
    
if (this.
_touchPositionCheck(gestureState)) {
      
return 
true;
    }
    
return 
false;
  }
  
_handlePanResponderGrant(
evt
gestureState) {
  }
  
_handlePanResponderMove (
evt
gestureState) {
    
const {
dy
= gestureState;
    this.
_updateNativeStyles(dy);
  }
  
_handlePanResponderEnd (
evt
gestureState) {
    
const {
dy
= gestureState;
    
if (dy 
> 
0.4 
* winHeight) {
      this.
_closeModal(
true);
    } 
else 
if (
-dy 
> 
0.4 
* winHeight) {
      this.
_closeModal(
false);
    } 
else {
      this.
_rebounce();
    }
  }
  
_touchPositionCheck(
gestureState) {
    
const {
dx
dy
= gestureState;
    
if (
Math.
abs(dy) 
<= 
Math.
abs(dx)) {
      
return 
false;
    }
    
return 
true;
  }
  
_closeModal(
isDown) {
    
const {
easingFunc
onClose
= this.props;
    
let current 
= this._contentStyle.style.top;
    this._inAnimation 
= 
true;
    
new 
Animation({
      start
: current,
      end
: isDown 
? winHeight 
: 
-winHeight,
      duration
: 
140,
      easingFunc,
      
onAnimationFrame
: (
val
=> {
        this.
_updateNativeStyles(val);
      },
      
onAnimationEnd
: () 
=> {
        this._inAnimation 
= 
false;
        
onClose();
        this.
_setNativeProps(
true);
      }
    }).start();
  }
  
_closeModalByTap() {
    
if (this._inAnimation) {
      
return 
false;
    }
    this.
_closeModal(
true);
  }
  
_rebounce(
isDown) {
    
const {
duration
easingFunc
= this.props;
    
let current 
= this._contentStyle.style.top;
    this._inAnimation 
= 
true;
    
new 
Animation({
      start
: current,
      end
: 
0,
      duration
: 
Math.
abs(current 
/ winHeight) 
* duration,
      easingFunc,
      
onAnimationFrame
: (
val
=> {
        this.
_updateNativeStyles(val);
      },
      
onAnimationEnd
: () 
=> {
        this._inAnimation 
= 
false;
      }
    }).start();
  }
  
_updateNativeStyles(
dy) {
    
const {
      
width,
      
height
    } 
= this.props.size;
    
// this._contentStyle.style.left = dx;
    
// this._contentStyle.style.right = -dx;
    this._contentStyle.style.top 
= dy;
    this._contentStyle.style.bottom 
= 
-dy;
    this._modalStyle.style.backgroundColor 
= 
`rgba(0, 0, 0, ${
1
 
-
 
Math
.
abs
(dy) 
/
 winHeight 
*
 
0.9
})`;
    
if (this.props.enableScaling) {
      this._imgSize.style.width 
= width 
* (
1 
- 
Math.
abs(dy 
/ winHeight) 
* 
0.6);
      this._imgSize.style.height 
= height 
* (
1 
- 
Math.
abs(dy 
/ winHeight) 
* 
0.6);
    } 
else {
      this._imgSize.style.width 
= width;
      this._imgSize.style.height 
= height;
    }
    this.
_setNativeProps();
  }
  
_setNativeProps(
isReset) {
    
if (isReset) {
      this._contentStyle 
= 
JSON.
parse(
JSON.
stringify(this._initContentStyle));
      this._modalStyle 
= 
JSON.
parse(
JSON.
stringify(this._initModalStyle));
      this._imgSize 
= 
JSON.
parse(
JSON.
stringify(this._initImgSize));
    }
    this.content 
&& this.content.
setNativeProps(this._contentStyle);
    this.mask 
&& this.mask.
setNativeProps(this._modalStyle);
    this.img 
&& this.img.
setNativeProps(this._imgSize);
  }
  
componentDidUpdate () {
    
new 
Animation({
      start
: 
0,
      end
: 
1,
      duration
: 
100,
      easingFunc
: Easing.ease,
      
onAnimationFrame
: (
val
=> {
        this.mask 
&& this.mask.
setNativeProps({style
: {
          opacity
: val
        }});
      },
      
onAnimationEnd
: () 
=> {
        this._inAnimation 
= 
false;
      }
    }).start();
  }
  
render () {
    
const {
      
visible,
      
onClose,
      
source,
      
size  
// origin size of the image
    } 
= this.props;
    
if (visible) { this._inAnimation 
= 
true; }
    this._initImgSize.style 
= size;
    
return (
      
<Modal
        visible
={visible}
        transparent
={
true}
        onRequestClose
={onClose}
>
        
<View style
={styles.mask} ref
={
mask 
=> {this.mask 
= mask;}} {
...this._pan.panHandlers}
>
          
<TouchableWithoutFeedback
            ref
={
ref 
=> {this.imgContainer 
= ref;}}
            onPress
={this._closeModalByTap}
>
            
<View
              ref
={
ref 
=> {this.content 
= ref;}}
              style
={styles.content}
>
              
<Image ref
={
img 
=> {this.img 
= img;}} source
={source} style
={[size, styles.img]}
/>
            
</View
>
          
</TouchableWithoutFeedback
>
        
</View
>
      
</Modal
>
    );
  }
}
const 
styles 
= 
StyleSheet.
create({
  mask
: {
    position
: 
'absolute',
    right
: 
0,
    left
: 
0,
    top
: 
0,
    bottom
: 
0,
    backgroundColor
: 
'rgba(0, 0, 0, 1)',
    opacity
: 
0
  },
  content
: {
    position
: 
'absolute',
    right
: 
0,
    left
: 
0,
    top
: 
0,
    bottom
: 
0,
    justifyContent
: 
'center',
    alignItems
: 
'center',
    backgroundColor
: 
'transparent'
  },
  toucharea
: {
    flex
: 
1,
    justifyContent
: 
'center',
    alignItems
: 
'center',
    alignSelf
: 
'stretch'
  },
  modalText
: {
    color
: 
'#fff'
  },
  img
: {
  }
});
ZoomImage.ImageModal 
= ImageModal;
export default ZoomImage;

3.Animation.js

export default 
function 
Animation(
option) {
  this.animate 
= this.animate.
bind(this);
  this.start 
= this.start.
bind(this);
  this.option 
= option;
}
Animation.prototype.
animate 
= 
function (
now) {
  
const {
    
start,
    
end,
    
duration,
    
onAnimationFrame,
    
onAnimationEnd 
= () 
=> {},
    
easingFunc 
= 
t 
=> t
  } 
= this.option;
  
var currentDuration 
= now 
- this.startTime;
  
if (currentDuration 
>= duration) {
    
onAnimationFrame(end);
    
onAnimationEnd();
    
return;
  }
  
let value;
  
if (start 
> end) {
    value 
= start 
- (start 
- end) 
* 
easingFunc(currentDuration 
/ duration);
  } 
else {
    value 
= (end 
- start) 
* 
easingFunc(currentDuration 
/ duration) 
+ start;
  }
  
onAnimationFrame(value);
  
requestAnimationFrame(this.animate);
};
Animation.prototype.
start 
= 
function () {
  this.startTime 
= 
new 
Date();
  this.
animate(this.startTime);
};

Examples效果如下

项目原地址https://github.com/Tinysymphony/react-native-zoom-image

转载地址:http://mzrni.baihongyu.com/

你可能感兴趣的文章
【LintCode 简单】646. 第一个独特字符位置
查看>>
【LintCode 简单】172. 删除元素
查看>>
【LintCode 简单】100. 删除排序数组中的重复数字
查看>>
【LintCode 简单】101. 删除排序数组中的重复数字 II
查看>>
【LintCode 简单】777. 完全平方数
查看>>
【LintCode 简单】111. 爬楼梯
查看>>
【LintCode 简单】173. 链表插入排序
查看>>
达观数据比赛 第一天任务
查看>>
达观数据比赛 第二天任务
查看>>
达观数据比赛 第三天任务
查看>>
达观数据比赛 第四天任务
查看>>
达观数据比赛 第五天任务
查看>>
达观数据比赛 第六天任务
查看>>
openpyxl 基础用法举例
查看>>
html页面段首空两格问题
查看>>
jquery validate不能验证多个相同的Name 只验证第一个的方案
查看>>
Java工具类实现校验公民身份证的有效性
查看>>
Java常用数据结构总结
查看>>
SpringMVC常见面试题总结
查看>>
scala(1)----windows环境下安装scala以及idea开发环境下配置scala
查看>>