In my Expo app, on a particular screen you can take an image or select from camera roll using expo APIs and then preview.
(在我的Expo应用程序中,您可以在特定屏幕上拍摄图像,或使用expo API从相机胶卷中进行选择,然后进行预览。)
In the preview, I have an overlay where the user can touch a point on the image a dot/marker is displayed over the corresponding X, Y-axis.(在预览中,我有一个叠加层,用户可以在其中触摸图像上的点,在相应的X,Y轴上显示点/标记。)
I want to be able to zoom into the image and place the marker with more accuracy.
(我希望能够放大图像并更精确地放置标记。)
So far I have tried:
(到目前为止,我已经尝试过:)
Scrollview - this kinda works, but after saving the image and viewing it again the marker is no longer in the correct position.
(滚动视图-可以,但是保存图像并再次查看后,标记不再位于正确的位置。)
https://kmagiera.github.io/react-native-gesture-handler/docs/handler-pan.html : This zooms much better and maintains accuracy but once zoomed, I am unable to pan around the zoomed image.
(https://kmagiera.github.io/react-native-gesture-handler/docs/handler-pan.html :这样可以更好地缩放并保持准确性,但是一旦缩放,我将无法平移缩放后的图像。)
3. https://github.com/ascoders/react-native-image-zoom : This appears very glitchy when you try to pan and also lose the marker position.
(3. https://github.com/ascoders/react-native-image-zoom :当您尝试平移并失去标记位置时,这似乎非常不正常。)
Ideally, I am looking at working with option 2 but making changes so I can pan the image.
(理想情况下,我正在考虑使用选项2,但要进行更改以便平移图像。)
Does anyone have a similar working example or suggestions on how I can move forward?
(有没有人有类似的工作示例或关于如何前进的建议?)
import React from "react";
import {
Container,
Text,
Button,
View,
Icon,
StyleProvider,
} from "native-base";
import {
ImageBackground,
Image,
TouchableOpacity,
TextInput,
Animated,
StyleSheet,
Dimensions,
} from "react-native";
import {
PinchGestureHandler,
PanGestureHandler,
ScrollView,
} from "react-native-gesture-handler";
import styles from "./ShotLoggerStyles";
import texts from "../../Style/texts";
import getTheme from "../../native-base-theme/components";
import marksmenOne from "../../native-base-theme/variables/marksmenOne";
import HeaderComponent from "../../Components/Header/Header";
import FooterComponent from "../../Components/Footer/FooterComponent";
import ChangeWeapon from "../../Components/ChangeWeapon";
import Loader from "../../Components/Loader/Loader";
const mainBG = require("../../assets/Backgrounds/metal.jpg");
const contentBG = require("../../assets/Backgrounds/training.jpg");
const windowWidth = Dimensions.get("window").width;
const ShotLoggerView = props => {
const {
leftPress,
log,
addShot,
removeShot,
updateShots,
updateScore,
changeWeaponHandler,
_takeImageHandler,
saveLog,
weaponModalVisible,
toggleWeaponModal,
loading,
displayHeight,
pinchRef,
rotationRef,
_onPinchGestureEvent,
_onPinchHandlerStateChange,
_scale,
panRef,
_onTiltGestureEvent,
_onTiltGestureStateChange,
minDist,
minPointers,
maxPointers,
avgTouches,
_tiltStr,
} = props;
return (
<Container>
<StyleProvider style={getTheme(marksmenOne)}>
<ImageBackground source={mainBG} style={styles.mainBG}>
<Loader visible={loading} />
<HeaderComponent
headerTitle="Logging Shots"
leftPress={leftPress}
leftAction="close"
/>
<View style={styles.content}>
{!log.image ? (
<>
<ChangeWeapon
weaponModalVisible={weaponModalVisible}
onChangeWeapon={changeWeaponHandler}
toggleWeaponModal={toggleWeaponModal}
/>
<ImageBackground style={styles.noImage} source={contentBG}>
<Text style={texts.headerTitle}>
Take a picture of your target
</Text>
<Button
icon
style={styles.buttonPrimary}
onPress={() => _takeImageHandler()}
>
<Icon style={styles.buttonIcon} name="camera" />
</Button>
</ImageBackground>
</>
) : (
<>
<View style={styles.details}>
<View style={styles.detailsItem}>
<Text style={texts.bodyWhite}>Shots</Text>
<TextInput
style={styles.input}
defaultValue={log.shots.toString()}
editable={false}
/>
</View>
<View style={styles.detailsItem}>
<Text style={texts.bodyWhite}>Score</Text>
<TextInput
style={styles.input}
onChangeText={value => updateScore(value)}
clearTextOnFocus
keyboardType="number-pad"
returnKeyType="done"
/>
</View>
</View>
<ScrollView>
<PanGestureHandler
ref={panRef}
onGestureEvent={_onTiltGestureEvent}
onHandlerStateChange={_onTiltGestureStateChange}
// onGestureEvent={this._onDragGestureEvent}
// onHandlerStateChange={this._onDragHandlerStateChange}
minDist={1}
minPointers={2}
maxPointers={2}
avgTouches
>
<Animated.View
style={[
styles.style2,
{
transform: [
{ perspective: 200 },
{ scale: _scale },
{ rotateX: _tiltStr },
],
},
]}
collapsable={false}
>
<PinchGestureHandler
ref={pinchRef}
simultaneousHandlers={rotationRef}
onGestureEvent={_onPinchGestureEvent}
onHandlerStateChange={_onPinchHandlerStateChange}
>
<Animated.View
style={[
styles.style2,
{
transform: [
{ perspective: 200 },
{ scale: _scale },
{ rotateX: _tiltStr },
],
},
]}
collapsable={false}
>
<TouchableOpacity
activeOpacity={1}
style={styles.pointer}
onPress={evt => addShot(evt.nativeEvent)}
>
<View
style={[
styles.instructions,
log.heatmap.length > 0
? { zIndex: -5 }
: { zIndex: 20 },
]}
>
<Text style={texts.headerBody}>
Tap the picture to add & remove shots.
</Text>
</View>
<Animated.Image
style={{
width: "100%",
minHeight: props.displayHeight,
height: "auto",
}}
source={{
uri: `data:image/jpg;base64,${log.image}`,
}}
/>
{log.heatmap.length > 0 &&
log.heatmap.map((shot, index) => {
return (
<TouchableOpacity
key={index}
onPress={() => removeShot(index)}
style={[
styles.shot,
{ left: shot.x - 10, top: shot.y - 10 },
]}
/>
);
})}
</TouchableOpacity>
</Animated.View>
</PinchGestureHandler>
</Animated.View>
</PanGestureHandler>
</ScrollView>
</>
)}
</View>
<FooterComponent
rightAction={saveLog}
rightActionText="Save shots"
rightActionDisable={!log.image}
/>
</ImageBackground>
</StyleProvider>
</Container>
);
};
export default ShotLoggerView;
const styles2 = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
backgroundColor: "black",
overflow: "hidden",
alignItems: "center",
flex: 1,
justifyContent: "center",
},
pinchableImage: {
width: 250,
height: 250,
},
wrapper: {
flex: 1,
},
});
and
(和)
/* eslint-disable no-underscore-dangle */
import React, { Component } from "react";
import { Alert, Dimensions, Animated, PanResponder } from "react-native";
import * as ImagePicker from "expo-image-picker";
import Constants from "expo-constants";
import { State } from "react-native-gesture-handler";
import * as Permissions from "expo-permissions";
import * as ImageManipulator from "expo-image-manipulator";
import * as Segment from "expo-analytics-segment";
import ShotLoggerView from "../ShotLoggerView";
import ENV from "../../../env";
import { USE_NATIVE_DRIVER } from "../../../config";
const windowWidth = Dimensions.get("window").width;
const circleRadius = 30;
Segment.initialize({
androidWriteKey: ENV.SEGMENT_ANDROID_KEY