React Native 的图片点击放大效果的组件使用 react-native-zoom-image

1.在index.android.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, ( wh=> {
        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), ( xywhpxpy=> {
      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 : ( evtgestureState=> this.props.startCapture,
      onMoveShouldSetPanResponder : this._onMoveShouldSetPanResponder. bind(this),
       onMoveShouldSetPanResponderCapture : ( evtgestureState=> this.props.moveCapture,
       onPanResponderTerminationRequest : ( evtgestureState=>  true,
      onPanResponderGrant : this._handlePanResponderGrant. bind(this),
      onPanResponderMove : this._handlePanResponderMove. bind(this),
      onPanResponderRelease : this._handlePanResponderEnd. bind(this),
      onPanResponderTerminate : this._handlePanResponderEnd. bind(this),
       onShouldBlockNativeResponder : ( evtgestureState=>  true
    });
  }
   _onStartShouldSetPanResponder ( evtgestureState) {
     // set responder for tapping when the drawer is open
     // TODO: tap close
     if (this._inAnimation)  return;
     return  false;
  }
   _onMoveShouldSetPanResponder ( evtgestureState) {
     // custom pan responder condition function
     if (this._inAnimation)  return;
     if (this.props.responderNegotiate  && this.props. responderNegotiate(evt, gestureState)  ===  falsereturn  false;
     if (this. _touchPositionCheck(gestureState)) {
       return  true;
    }
     return  false;
  }
   _handlePanResponderGrant( evtgestureState) {
  }
   _handlePanResponderMove ( evtgestureState) {
     const { dy= gestureState;
    this. _updateNativeStyles(dy);
  }
   _handlePanResponderEnd ( evtgestureState) {
     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 { dxdy= gestureState;
     if ( Math. abs(dy)  <=  Math. abs(dx)) {
       return  false;
    }
     return  true;
  }
   _closeModal( isDown) {
     const { easingFunconClose= 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 { durationeasingFunc= 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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值