Scrollable image with pinch-to-zoom in react-native

前端 未结 6 1815
野的像风
野的像风 2020-12-23 12:33

I\'m trying to display an image in my React Native app (Android) and I want to give users an ability to zoom that image in and out. This also requires the image to be scroll

相关标签:
6条回答
  • 2020-12-23 12:45

    Don't go deep if you are working with react-native because things will go more and more complex as deep you go.

    Give it a try...

    npm i react-native-image-zoom-viewer --save
    

    or

    yarn add react-native-image-zoom-viewer
    

    copy this code and put it in app.js and hit Run button.

    import React from 'react';
    import {View} from 'react-native';
    import ImageViewer from 'react-native-image-zoom-viewer';
    const image = [
      {
        url:
          'https://static8.depositphotos.com/1020341/896/i/950/depositphotos_8969502-stock-photo-human-face-with-cracked-texture.jpg',
      },
    ];
    
    const App = () => {
      return (
        <View style={{flex: 1}}>
          <ImageViewer imageUrls={image} />
        </View>
      );
    };
    export default App;
    
    0 讨论(0)
  • 2020-12-23 12:51
    npm i react-native-photo-view-ex
    
    import PhotoView from 'react-native-photo-view-ex';
    
    <PhotoView
        style={{ flex: 1, width: '100%', height: '100%' }}
        source={{ uri: this.state.filePath }} // you can supply any URL as well
        minimumZoomScale={1} // max value can be 1
        maximumZoomScale={2} // max value can be 3
    />
    
    0 讨论(0)
  • 2020-12-23 12:55

    I ended up rolling my own ZoomableImage component. So far it's been working out pretty well, here is the code:

    import React, { Component } from "react";
    import { View, PanResponder, Image } from "react-native";
    import PropTypes from "prop-types";
    
    function calcDistance(x1, y1, x2, y2) {
      const dx = Math.abs(x1 - x2);
      const dy = Math.abs(y1 - y2);
      return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
    }
    
    function calcCenter(x1, y1, x2, y2) {
      function middle(p1, p2) {
    return p1 > p2 ? p1 - (p1 - p2) / 2 : p2 - (p2 - p1) / 2;
      }
    
      return {
    x: middle(x1, x2),
    y: middle(y1, y2)
      };
    }
    
    function maxOffset(offset, windowDimension, imageDimension) {
      const max = windowDimension - imageDimension;
      if (max >= 0) {
    return 0;
      }
      return offset < max ? max : offset;
    }
    
    function calcOffsetByZoom(width, height, imageWidth, imageHeight, zoom) {
      const xDiff = imageWidth * zoom - width;
      const yDiff = imageHeight * zoom - height;
      return {
    left: -xDiff / 2,
    top: -yDiff / 2
      };
    }
    
    class ZoomableImage extends Component {
      constructor(props) {
    super(props);
    
    this._onLayout = this._onLayout.bind(this);
    
    this.state = {
      zoom: null,
      minZoom: null,
      layoutKnown: false,
      isZooming: false,
      isMoving: false,
      initialDistance: null,
      initialX: null,
      initalY: null,
      offsetTop: 0,
      offsetLeft: 0,
      initialTop: 0,
      initialLeft: 0,
      initialTopWithoutZoom: 0,
      initialLeftWithoutZoom: 0,
      initialZoom: 1,
      top: 0,
      left: 0
    };
      }
    
      processPinch(x1, y1, x2, y2) {
    const distance = calcDistance(x1, y1, x2, y2);
    const center = calcCenter(x1, y1, x2, y2);
    
    if (!this.state.isZooming) {
      const offsetByZoom = calcOffsetByZoom(
        this.state.width,
        this.state.height,
        this.props.imageWidth,
        this.props.imageHeight,
        this.state.zoom
      );
      this.setState({
        isZooming: true,
        initialDistance: distance,
        initialX: center.x,
        initialY: center.y,
        initialTop: this.state.top,
        initialLeft: this.state.left,
        initialZoom: this.state.zoom,
        initialTopWithoutZoom: this.state.top - offsetByZoom.top,
        initialLeftWithoutZoom: this.state.left - offsetByZoom.left
      });
    } else {
      const touchZoom = distance / this.state.initialDistance;
      const zoom =
        touchZoom * this.state.initialZoom > this.state.minZoom
          ? touchZoom * this.state.initialZoom
          : this.state.minZoom;
    
      const offsetByZoom = calcOffsetByZoom(
        this.state.width,
        this.state.height,
        this.props.imageWidth,
        this.props.imageHeight,
        zoom
      );
      const left =
        this.state.initialLeftWithoutZoom * touchZoom + offsetByZoom.left;
      const top =
        this.state.initialTopWithoutZoom * touchZoom + offsetByZoom.top;
    
      this.setState({
        zoom,
        left:
          left > 0
            ? 0
            : maxOffset(left, this.state.width, this.props.imageWidth * zoom),
        top:
          top > 0
            ? 0
            : maxOffset(top, this.state.height, this.props.imageHeight * zoom)
      });
    }
      }
    
      processTouch(x, y) {
    if (!this.state.isMoving) {
      this.setState({
        isMoving: true,
        initialX: x,
        initialY: y,
        initialTop: this.state.top,
        initialLeft: this.state.left
      });
    } else {
      const left = this.state.initialLeft + x - this.state.initialX;
      const top = this.state.initialTop + y - this.state.initialY;
    
      this.setState({
        left:
          left > 0
            ? 0
            : maxOffset(
                left,
                this.state.width,
                this.props.imageWidth * this.state.zoom
              ),
        top:
          top > 0
            ? 0
            : maxOffset(
                top,
                this.state.height,
                this.props.imageHeight * this.state.zoom
              )
      });
    }
      }
    
      _onLayout(event) {
    const layout = event.nativeEvent.layout;
    
    if (
      layout.width === this.state.width &&
      layout.height === this.state.height
    ) {
      return;
    }
    
    const zoom = layout.width / this.props.imageWidth;
    
    const offsetTop =
      layout.height > this.props.imageHeight * zoom
        ? (layout.height - this.props.imageHeight * zoom) / 2
        : 0;
    
    this.setState({
      layoutKnown: true,
      width: layout.width,
      height: layout.height,
      zoom,
      offsetTop,
      minZoom: zoom
    });
      }
    
      componentWillMount() {
    this._panResponder = PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onStartShouldSetPanResponderCapture: () => true,
      onMoveShouldSetPanResponder: () => true,
      onMoveShouldSetPanResponderCapture: () => true,
      onPanResponderGrant: () => {},
      onPanResponderMove: evt => {
        const touches = evt.nativeEvent.touches;
        if (touches.length === 2) {
          this.processPinch(
            touches[0].pageX,
            touches[0].pageY,
            touches[1].pageX,
            touches[1].pageY
          );
        } else if (touches.length === 1 && !this.state.isZooming) {
          this.processTouch(touches[0].pageX, touches[0].pageY);
        }
      },
    
      onPanResponderTerminationRequest: () => true,
      onPanResponderRelease: () => {
        this.setState({
          isZooming: false,
          isMoving: false
        });
      },
      onPanResponderTerminate: () => {},
      onShouldBlockNativeResponder: () => true
    });
      }
    
      render() {
    return (
      <View
        style={this.props.style}
        {...this._panResponder.panHandlers}
        onLayout={this._onLayout}
      >
        <Image
          style={{
            position: "absolute",
            top: this.state.offsetTop + this.state.top,
            left: this.state.offsetLeft + this.state.left,
            width: this.props.imageWidth * this.state.zoom,
            height: this.props.imageHeight * this.state.zoom
          }}
          source={this.props.source}
        />
      </View>
    );
      }
    }
    
    ZoomableImage.propTypes = {
      imageWidth: PropTypes.number.isRequired,
      imageHeight: PropTypes.number.isRequired,
      source: PropTypes.object.isRequired
    };
    export default ZoomableImage;

    0 讨论(0)
  • 2020-12-23 12:55

    You can simply use the react-native-image-zoom-viewer or react-native-image-pan-zoom library for that. Using this libraries you don't have to code manually.

    0 讨论(0)
  • 2020-12-23 12:57

    There's a much easier way now. Just make a ScollView with minimumZoomScale and maximumZoomScale:

    import React, { Component } from 'react';
    import { AppRegistry, ScrollView, Text } from 'react-native';
    
    export default class IScrolledDownAndWhatHappenedNextShockedMe extends Component {
      render() {
          return (
            <ScrollView minimumZoomScale={1} maximumZoomScale={5} >
              <Text style={{fontSize:96}}>Scroll me plz</Text>
              <Text style={{fontSize:96}}>If you like</Text>
              <Text style={{fontSize:96}}>Scrolling down</Text>
              <Text style={{fontSize:96}}>What's the best</Text>
              <Text style={{fontSize:96}}>Framework around?</Text>
              <Text style={{fontSize:80}}>React Native</Text>
            </ScrollView>
        );
      }
    }
    
    // skip these lines if using Create React Native App
    AppRegistry.registerComponent(
      'AwesomeProject',
      () => IScrolledDownAndWhatHappenedNextShockedMe);
    
    0 讨论(0)
  • 2020-12-23 13:02

    In my case I have to add images inside Viewpager with Zoom functionality.

    So I have used these two library.

    import ViewPager from '@react-native-community/viewpager'
    import PhotoView from 'react-native-photo-view-ex';
    

    which you can install from.

    npm i @react-native-community/viewpager
    npm i react-native-photo-view-ex
    

    So I have used this code.

    class ResumeView extends React.Component {
    
        render() {
            preivewArray = this.props.showPreview.previewArray
            var pageViews = [];
            for (i = 0; i < preivewArray.length; i++) {
                pageViews.push(<View style={style.page}>
    
                    <PhotoView
                        source={{ uri: preivewArray[i].filePath }}
                        minimumZoomScale={1}
                        maximumZoomScale={3}
                        // resizeMode='stretch'
                        style={{ width: a4_width, height: a4_height, alignSelf: 'center' }} />
    
                </View>);
            }
    
            return (
                <ViewPager
                    onPageScroll={this.pageScroll}
                    style={{ width: '100%', height: a4_height }}>
                    {pageViews}
                </ViewPager>
            )
        }
    
        pageScroll = (event) => {
            console.log("onPageScroll")
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题