我试图在我的React Native应用程序(Android)中显示图像,我想让用户能够放大和缩小图像。 这也要求图像一旦放大就可以滚动。
我该怎么做呢?
我尝试使用ScrollView
在里面显示更大的图像,但在Android上它可以垂直或水平滚动,而不是两种方式。
即使这有效,也存在使pinch-to-zoom
工作的问题。
据我了解,我需要在自定义视图上使用PanResponder
来缩放图像并相应地对其进行定位。有更简单的方法吗?
答案 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;
答案 1 :(得分:3)
您可以简单地使用react-native-image-zoom-viewer或react-native-image-pan-zoom库。使用此库,您无需手动编码。
答案 2 :(得分:1)
现在有一个更简单的方法。
只需使用minimumZoomScale
和maximumZoomScale
创建一个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)
就我而言,我必须在具有缩放功能的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
/>