ピンチで画像をズームイン/ズームアウトし、その画像をある区域の周りに移動させるコンポーネントを作成しようとしています。最終目標は地図の画像を表示し、それを探索することです。マップは常に画像になります。ビュー内で画像を移動する - リアクションネイティブ
イメージは、私がpanResponderを設定したビューに含まれています。 panResponderは、ピンチとタッチイベントを区別し、適切な関数を呼び出します。
私は「ピンチとズーム」機能と「動き回り」機能を作っていましたが、後者は私にとっては不思議です。私は計算が少しずれていると思うが、それは私の強い訴訟ではない。
これを実装するにはどうすればよいでしょうか?ここで
は、全体のコードです:
import React, {
Component,
} from 'react';
import {
Dimensions,
AppRegistry,
StyleSheet,
Text,
View,
Image,
TouchableOpacity,
PanResponder,
} from 'react-native';
const screenWidth = Dimensions.get('window').width;
const screenHeight = Dimensions.get('window').height;
const img = require('image!cars');
const scaleStep = 0.1;
const DIR_IN = 'IN';
const DIR_OUT = 'OUT';
class reactzoom extends Component {
constructor(props) {
super(props);
this.state = {
zoom: 1,
x: 0,
y: 0,
distance: 0,
isZooming: false,
isMoving: false,
};
}
getDimensionsToFitArea(image, areaDimensions) {
const verticalFactor = areaDimensions.height/image.height;
const horizontalFactor = areaDimensions.width/image.width;
const imageFactor = Math.min(verticalFactor, horizontalFactor);
return {
width: image.width * imageFactor,
height: image.height * imageFactor,
};
}
setZoom(zoom) {
this.setState({
zoom: zoom,
});
}
zoomIn() {
const newZoom = this.state.zoom + scaleStep;
this.setZoom(newZoom);
}
zoomOut() {
let newZoom = this.state.zoom - (scaleStep * 1.5);
if (newZoom < 1) {
newZoom = 1;
this.resetCenter();
}
this.setZoom(newZoom);
}
cancelZoom() {
this.setZoom(1);
this.resetCenter();
}
resetCenter() {
this.setCenter(0, 0);
}
setCenter(x, y) {
let newX = 0;
let newY = 0;
if (this.state.zoom > 1) {
const imgAreaDimensions = this.getImageAreaDimensions();
if (x != 0 && y != 0) {
newX = (imgAreaDimensions.width/2) - x;
newY = (imgAreaDimensions.height/2) - y;
}
}
const newState = {
x: newX,
y: newY,
};
this.setState(newState);
}
getImageAreaDimensions() {
return {
width: screenWidth,
height: screenHeight/2
};
}
processTouch(x, y) {
if (!this.state.isMoving) {
this.setState({
isMoving: true,
initialX: x,
initialY: y,
pathDoneX: 0,
pathDoneY: 0,
});
} else {
const path = calcPath(this.state.initialX, this.state.initialY, x, y);
const newX = this.state.initialX - path.x;
const newY = this.state.initialY - path.y;
this.setCenter(newX, newY);
this.setState({
pathDoneX: this.state.pathDoneX + path.x,
pathDoneY: this.state.pathDoneY + path.y,
});
}
}
processPinch(x1, y1, x2, y2) {
const distance = calcDistance(x1, y1, x2, y2);
const center = calcCenter(x1, y1, x2, y2);
const direction = (distance > this.state.distance) ? DIR_IN : DIR_OUT;
if (!this.state.isZooming) {
if (direction === DIR_IN) {
this.setCenter(center.x, center.y);
}
}
else {
(direction === DIR_IN) ? this.zoomIn() : this.zoomOut();
}
this.setState({
distance: distance,
isZooming: true,
});
}
componentWillMount() {
this._panResponder = PanResponder.create({
oneStartShouldSetPanResponderCapture:() => true,
oneMoveShouldSetPanResponder:() => true,
oneMoveShouldSetPanResponderCapture:() => 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:() => false,
onPanResponderRelease:() => {
this.setState({
isZooming: false,
isMoving: false,
});
},
onPanResponderTerminate:() => { },
onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
return (Math.abs(gestureState.dx) > 2) || (Math.abs(gestureState.dy) > 2)
},
});
}
render() {
const imgAreaDimensions = this.getImageAreaDimensions();
const imgDimensions = this.getDimensionsToFitArea(img, {
width: imgAreaDimensions.width,
height: imgAreaDimensions.height,
});
return (
<View style={styles.container}>
<View
style={{
borderWidth: 1,
borderColor: '#000000',
width: imgAreaDimensions.width,
height: imgAreaDimensions.height,
}}
{...this._panResponder.panHandlers}
>
<Image
style={{
width: imgDimensions.width,
height: imgDimensions.height,
transform: [
{ translateX: this.state.x },
{ translateY: this.state.y },
{ scaleX: this.state.zoom },
{ scaleY: this.state.zoom }
]
}}
source={img}
/>
</View>
<TouchableOpacity
style={styles.button}
onPress={() => this.cancelZoom() }
>
<Text>~</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
button: {
borderColor: '#000000',
borderWidth: 1,
padding: 25,
paddingBottom: 5,
paddingTop: 5,
alignSelf: 'stretch',
alignItems: 'center',
margin: 1,
height: 50,
}
});
AppRegistry.registerComponent('reactzoom',() => reactzoom);
function calcPath(x1, y1, x2, y2) {
return {
x: x2 - x1,
y: y2 - y1,
};
}
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),
};
}
これはまったく役に立ちませんが、私はこのチュートリアルを見ても画像を見てみると役に立つかもしれませんhttp://mindthecode.com/getting-started-with-the-panresponder -in-react-native/ – cjmling