Fix photo overlay positioning and rotation detection
This commit is contained in:
@@ -1,33 +1,55 @@
|
||||
import { CameraView, useCameraPermissions } from 'expo-camera';
|
||||
import * as Location from 'expo-location';
|
||||
import * as ScreenOrientation from 'expo-screen-orientation';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Dimensions,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
ActivityIndicator,
|
||||
useWindowDimensions,
|
||||
} from 'react-native';
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import { RootStackParamList } from '../navigation/types';
|
||||
import { FooterConfig, loadFooterConfig, resolveFieldValue, StampMeta } from '../photo/footerConfig';
|
||||
import { FooterConfig, loadFooterConfig, StampMeta } from '../photo/footerConfig';
|
||||
import { getActiveProjectId, getProject } from '../db/repositories';
|
||||
import { COLORS } from '../ui/components';
|
||||
import { FooterOverlay } from '../photo/FooterOverlay';
|
||||
|
||||
type Props = NativeStackScreenProps<RootStackParamList, 'Camera'>;
|
||||
|
||||
const { width: SCREEN_W, height: SCREEN_H } = Dimensions.get('window');
|
||||
|
||||
export function CameraScreen({ route, navigation }: Props) {
|
||||
const { onCapture } = route.params;
|
||||
const [permission, requestPermission] = useCameraPermissions();
|
||||
const cameraRef = useRef<CameraView>(null);
|
||||
const { width: winW, height: winH } = useWindowDimensions();
|
||||
|
||||
const [config, setConfig] = useState<FooterConfig | null>(null);
|
||||
const [meta, setMeta] = useState<StampMeta | null>(null);
|
||||
const [taking, setTaking] = useState(false);
|
||||
const [rotation, setRotation] = useState(0);
|
||||
|
||||
// Detectar orientación física para rotar el sello
|
||||
useEffect(() => {
|
||||
const updateOrientation = async () => {
|
||||
const info = await ScreenOrientation.getOrientationAsync();
|
||||
handleOrientation(info);
|
||||
};
|
||||
|
||||
const handleOrientation = (o: ScreenOrientation.Orientation) => {
|
||||
if (o === ScreenOrientation.Orientation.LANDSCAPE_LEFT) setRotation(90);
|
||||
else if (o === ScreenOrientation.Orientation.LANDSCAPE_RIGHT) setRotation(-90);
|
||||
else if (o === ScreenOrientation.Orientation.PORTRAIT_UPSIDE_DOWN) setRotation(180);
|
||||
else setRotation(0);
|
||||
};
|
||||
|
||||
void updateOrientation();
|
||||
const sub = ScreenOrientation.addOrientationChangeListener((evt) => {
|
||||
handleOrientation(evt.orientationInfo.orientation);
|
||||
});
|
||||
return () => ScreenOrientation.removeOrientationChangeListener(sub);
|
||||
}, []);
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
const [cfg, projectId] = await Promise.all([
|
||||
@@ -118,10 +140,17 @@ export function CameraScreen({ route, navigation }: Props) {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Footer Preview */}
|
||||
{/* Footer Preview con rotación */}
|
||||
{config && meta && (
|
||||
<View style={styles.footerWrapper}>
|
||||
<FooterOverlay config={config} meta={meta} imageWidth={SCREEN_W} />
|
||||
<View style={[
|
||||
styles.footerWrapper,
|
||||
{ transform: [{ rotate: `${rotation}deg` }] }
|
||||
]}>
|
||||
<FooterOverlay
|
||||
config={config}
|
||||
meta={meta}
|
||||
imageWidth={winW > winH ? winH : winW}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
@@ -153,8 +182,16 @@ const styles = StyleSheet.create({
|
||||
closeBtn: { width: 40, height: 40, justifyContent: 'center', alignItems: 'center' },
|
||||
closeTxt: { color: '#fff', fontSize: 24 },
|
||||
footerWrapper: {
|
||||
height: 100, // Espacio para el footer
|
||||
justifyContent: 'flex-end',
|
||||
position: 'absolute',
|
||||
// Ocupamos toda la pantalla para que el transform rote sobre el centro
|
||||
// pero el FooterOverlay se posicionará internamente en su esquina.
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
controls: {
|
||||
paddingBottom: 40,
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
FooterConfig,
|
||||
FooterField,
|
||||
loadFooterConfig,
|
||||
OverlayPosition,
|
||||
pickAndSaveLogo,
|
||||
PhotoResolution,
|
||||
saveFooterConfig,
|
||||
@@ -36,6 +37,7 @@ const BUILT_IN_LABELS: Record<string, string> = {
|
||||
|
||||
const RESOLUTION_OPTIONS: PhotoResolution[] = ['low', 'medium', 'high'];
|
||||
const QUALITY_OPTIONS = ['0.5', '0.7', '0.85', '1.0'];
|
||||
const POSITION_OPTIONS: OverlayPosition[] = ['bottom-left', 'bottom-right', 'top-left', 'top-right'];
|
||||
|
||||
export function PhotoSettingsScreen() {
|
||||
const [config, setConfig] = useState<FooterConfig | null>(null);
|
||||
@@ -182,6 +184,18 @@ export function PhotoSettingsScreen() {
|
||||
<Text style={styles.hint}>
|
||||
Valores bajos reducen el tamaño del archivo pero pueden verse peor.
|
||||
</Text>
|
||||
|
||||
<View style={{ height: 12 }} />
|
||||
|
||||
<ChipSelect
|
||||
label="Posición del sello"
|
||||
value={config.overlayPosition}
|
||||
options={POSITION_OPTIONS}
|
||||
onChange={(v) => void save({ ...config, overlayPosition: v })}
|
||||
/>
|
||||
<Text style={styles.hint}>
|
||||
Esquina de la imagen donde aparecerán los datos.
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* ── Campos ── */}
|
||||
|
||||
Reference in New Issue
Block a user