可缩放图像与缩放缩放

时间:2016-04-02 03:43:01

标签: react-native

我试图在我的React Native应用程序(Android)中显示图像,我想让用户能够放大和缩小图像。 这也要求图像一旦放大就可以滚动。

我该怎么做呢?

我尝试使用ScrollView在里面显示更大的图像,但在Android上它可以垂直或水平滚动,而不是两种方式。 即使这有效,也存在使pinch-to-zoom工作的问题。

据我了解,我需要在自定义视图上使用PanResponder来缩放图像并相应地对其进行定位。有更简单的方法吗?

6 个答案:

答案 0 :(得分:38)

我最终滚动了自己的ZoomableImage组件。到目前为止,它已经做得很好,这是代码:



import React, {Component, PropTypes} from 'react';
import { Text, View, PanResponder, Image } from 'react-native';

function calcDistance(x1, y1, x2, y2) {
    let dx = Math.abs(x1 - x2)
    let 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) {
    let max = windowDimension - imageDimension;
    if (max >= 0) {
        return 0;
    }
    return offset < max ? max : offset;
}

function calcOffsetByZoom(width, height, imageWidth, imageHeight, zoom) {
    let xDiff = imageWidth * zoom - width;
    let 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) {
        let distance = calcDistance(x1, y1, x2, y2);
        let center = calcCenter(x1, y1, x2, y2);

        if (!this.state.isZooming) {
            let 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 {
            let touchZoom = distance / this.state.initialDistance;
            let zoom = touchZoom * this.state.initialZoom > this.state.minZoom
                ? touchZoom * this.state.initialZoom : this.state.minZoom;

            let offsetByZoom = calcOffsetByZoom(this.state.width, this.state.height,
                this.props.imageWidth, this.props.imageHeight, zoom);
            let left = (this.state.initialLeftWithoutZoom * touchZoom) + offsetByZoom.left;
            let top = (this.state.initialTopWithoutZoom * touchZoom) + offsetByZoom.top;

            this.setState({
                zoom: zoom,
                left: 0,
                top: 0,
                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 {
            let left = this.state.initialLeft + x - this.state.initialX;
            let 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) {
        let layout = event.nativeEvent.layout;

        if (layout.width === this.state.width
            && layout.height === this.state.height) {
            return;
        }

        let zoom = layout.width / this.props.imageWidth;

        let 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: zoom,
            offsetTop: offsetTop,
            minZoom: zoom
        });
    }

    componentWillMount() {
        this._panResponder = PanResponder.create({
            onStartShouldSetPanResponder: (evt, gestureState) => true,
            onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
            onMoveShouldSetPanResponder: (evt, gestureState) => true,
            onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
            onPanResponderGrant: (evt, gestureState) => {},
            onPanResponderMove: (evt, gestureState) => {
                let touches = evt.nativeEvent.touches;
                if (touches.length == 2) {
                    let touch1 = touches[0];
                    let touch2 = touches[1];

                    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: (evt, gestureState) => true,
            onPanResponderRelease: (evt, gestureState) => {
                this.setState({
                    isZooming: false,
                    isMoving: false
                });
            },
            onPanResponderTerminate: (evt, gestureState) => {},
            onShouldBlockNativeResponder: (evt, gestureState) => 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;
&#13;
&#13;
&#13;

答案 1 :(得分:3)

您可以简单地使用react-native-image-zoom-viewerreact-native-image-pan-zoom库。使用此库,您无需手动编码。

答案 2 :(得分:1)

现在有一个更简单的方法。 只需使用minimumZoomScalemaximumZoomScale创建一个ScollView:

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);

答案 3 :(得分:0)

enter image description here enter image description here

就我而言,我必须在具有缩放功能的Viewpager中添加图像。

所以我用了这两个库。

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

可以从中安装。

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

所以我用了这段代码。

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")
    }

}

答案 4 :(得分:0)

如果您使用react-native,请不要太深,因为随着您的深入,事情会越来越复杂。

尝试一下...

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

yarn add react-native-image-zoom-viewer

复制此代码并将其放入app.js中,然后单击“运行”按钮。

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;

答案 5 :(得分:0)

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
/>