Final fixes for photo overlay position and rotation

This commit is contained in:
2026-07-29 09:39:12 +02:00
parent a9ba966176
commit f2b359e515
2 changed files with 62 additions and 62 deletions
+15 -8
View File
@@ -11,20 +11,21 @@ interface Props {
config: FooterConfig; config: FooterConfig;
meta: StampMeta; meta: StampMeta;
imageWidth: number; imageWidth: number;
rotation?: number; // Rotación extra para la preview de cámara
} }
export function FooterOverlay({ config, meta, imageWidth }: Props) { export function FooterOverlay({ config, meta, imageWidth, rotation = 0 }: Props) {
if (!config.enabled) return null; if (!config.enabled) return null;
const enabled = config.fields.filter((f) => f.enabled); const enabled = config.fields.filter((f) => f.enabled);
if (enabled.length === 0 && !config.logoUri) return null; if (enabled.length === 0 && !config.logoUri) return null;
const fontSize = Math.max(10, Math.round(imageWidth * 0.018)); const fontSize = Math.max(10, Math.round(imageWidth * 0.022));
const logoDim = Math.max(40, Math.round(imageWidth * 0.08)); const logoDim = Math.max(40, Math.round(imageWidth * 0.10));
const pad = Math.max(8, Math.round(imageWidth * 0.015)); const pad = Math.max(8, Math.round(imageWidth * 0.02));
const lineH = Math.round(fontSize * 1.3); const lineH = Math.round(fontSize * 1.3);
// Determinar posición // Determinar posición física
const posStyle: ViewStyle = {}; const posStyle: ViewStyle = {};
if (config.overlayPosition.includes('top')) posStyle.top = 20; if (config.overlayPosition.includes('top')) posStyle.top = 20;
else posStyle.bottom = 20; else posStyle.bottom = 20;
@@ -32,6 +33,11 @@ export function FooterOverlay({ config, meta, imageWidth }: Props) {
if (config.overlayPosition.includes('left')) posStyle.left = 20; if (config.overlayPosition.includes('left')) posStyle.left = 20;
else posStyle.right = 20; else posStyle.right = 20;
// Aplicar rotación si se indica (para la cámara)
if (rotation !== 0) {
posStyle.transform = [{ rotate: `${rotation}deg` }];
}
return ( return (
<View style={[styles.container, posStyle, { padding: pad }]}> <View style={[styles.container, posStyle, { padding: pad }]}>
{/* Logo */} {/* Logo */}
@@ -39,7 +45,7 @@ export function FooterOverlay({ config, meta, imageWidth }: Props) {
<View <View
style={[ style={[
styles.logoWrap, styles.logoWrap,
{ width: logoDim, height: logoDim, borderRadius: Math.round(logoDim * 0.08), marginBottom: 6 }, { width: logoDim, height: logoDim, borderRadius: Math.round(logoDim * 0.1), marginBottom: 4 },
]} ]}
> >
<Image source={{ uri: config.logoUri }} style={styles.logoImg} resizeMode="contain" /> <Image source={{ uri: config.logoUri }} style={styles.logoImg} resizeMode="contain" />
@@ -75,8 +81,9 @@ const styles = StyleSheet.create({
position: 'absolute', position: 'absolute',
backgroundColor: 'rgba(0,0,0,0.65)', backgroundColor: 'rgba(0,0,0,0.65)',
borderRadius: 8, borderRadius: 8,
maxWidth: '50%', // Para no ocupar toda la pantalla maxWidth: '70%', // Un poco más ancho para evitar cortes agresivos
alignItems: 'flex-start', alignItems: 'flex-start',
zIndex: 1000,
}, },
logoWrap: { logoWrap: {
backgroundColor: '#fff', backgroundColor: '#fff',
@@ -84,7 +91,7 @@ const styles = StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
}, },
logoImg: { width: '80%', height: '80%' }, logoImg: { width: '85%', height: '85%' },
textBlock: { width: '100%' }, textBlock: { width: '100%' },
line: { color: '#fff' }, line: { color: '#fff' },
bold: { fontWeight: '700' }, bold: { fontWeight: '700' },
+47 -54
View File
@@ -38,6 +38,8 @@ export function CameraScreen({ route, navigation }: Props) {
}; };
const handleOrientation = (o: ScreenOrientation.Orientation) => { const handleOrientation = (o: ScreenOrientation.Orientation) => {
// Nota: LANDSCAPE_LEFT es rotar el móvil a la derecha (home button a la derecha)
// Queremos contrarrestar el giro para que el sello esté derecho.
if (o === ScreenOrientation.Orientation.LANDSCAPE_LEFT) setRotation(90); if (o === ScreenOrientation.Orientation.LANDSCAPE_LEFT) setRotation(90);
else if (o === ScreenOrientation.Orientation.LANDSCAPE_RIGHT) setRotation(-90); else if (o === ScreenOrientation.Orientation.LANDSCAPE_RIGHT) setRotation(-90);
else if (o === ScreenOrientation.Orientation.PORTRAIT_UPSIDE_DOWN) setRotation(180); else if (o === ScreenOrientation.Orientation.PORTRAIT_UPSIDE_DOWN) setRotation(180);
@@ -128,80 +130,71 @@ export function CameraScreen({ route, navigation }: Props) {
<View style={styles.container}> <View style={styles.container}>
<CameraView <CameraView
ref={cameraRef} ref={cameraRef}
style={styles.camera} style={StyleSheet.absoluteFill}
facing="back" facing="back"
autofocus="on" autofocus="on"
> />
<View style={styles.overlay}>
{/* Header */}
<View style={styles.header}>
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.closeBtn}>
<Text style={styles.closeTxt}></Text>
</TouchableOpacity>
</View>
{/* Footer Preview con rotación */} {/* Capa de UI sobre la cámara */}
{config && meta && ( <View style={styles.overlay}>
<View style={[ {/* Header con botón cerrar */}
styles.footerWrapper, <View style={styles.header}>
{ transform: [{ rotate: `${rotation}deg` }] } <TouchableOpacity onPress={() => navigation.goBack()} style={styles.closeBtn}>
]}> <Text style={styles.closeTxt}></Text>
<FooterOverlay </TouchableOpacity>
config={config}
meta={meta}
imageWidth={winW > winH ? winH : winW}
/>
</View>
)}
{/* Controls */}
<View style={styles.controls}>
<TouchableOpacity
style={[styles.captureBtn, taking && styles.disabled]}
onPress={() => void capture()}
disabled={taking}
>
<View style={styles.captureInner} />
</TouchableOpacity>
</View>
</View> </View>
</CameraView>
{/* Preview del sello (posicionado en su esquina con rotación) */}
{config && meta && (
<View style={styles.fullOverlay} pointerEvents="none">
<FooterOverlay
config={config}
meta={meta}
imageWidth={Math.min(winW, winH)}
rotation={rotation}
/>
</View>
)}
{/* Botón de disparo abajo al centro */}
<View style={styles.footerControls}>
<TouchableOpacity
style={[styles.captureBtn, taking && styles.disabled]}
onPress={() => void capture()}
disabled={taking}
>
<View style={styles.captureInner} />
</TouchableOpacity>
</View>
</View>
</View> </View>
); );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#000' }, container: { flex: 1, backgroundColor: '#000' },
camera: { flex: 1 }, overlay: { flex: 1, justifyContent: 'space-between', zIndex: 10 },
overlay: { flex: 1, justifyContent: 'space-between' }, fullOverlay: {
...StyleSheet.absoluteFillObject,
// No centramos aquí, dejamos que FooterOverlay use sus offsets top/bottom/left/right
},
center: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#000' }, center: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#000' },
message: { color: '#fff', textAlign: 'center', marginBottom: 20 }, message: { color: '#fff', textAlign: 'center', marginBottom: 20 },
btn: { backgroundColor: COLORS.primary, padding: 12, borderRadius: 8 }, btn: { backgroundColor: COLORS.primary, padding: 12, borderRadius: 8 },
btnText: { color: '#fff', fontWeight: 'bold' }, btnText: { color: '#fff', fontWeight: 'bold' },
header: { padding: 20, paddingTop: 50 }, header: { padding: 20, paddingTop: 50 },
closeBtn: { width: 40, height: 40, justifyContent: 'center', alignItems: 'center' }, closeBtn: { width: 40, height: 40, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.3)', borderRadius: 20 },
closeTxt: { color: '#fff', fontSize: 24 }, closeTxt: { color: '#fff', fontSize: 24 },
footerWrapper: { footerControls: {
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, paddingBottom: 40,
alignItems: 'center', alignItems: 'center',
width: '100%',
}, },
captureBtn: { captureBtn: {
width: 70, width: 76,
height: 70, height: 70, // Un poco ovalado para que se note
borderRadius: 35, borderRadius: 38,
borderWidth: 4, borderWidth: 5,
borderColor: '#fff', borderColor: '#fff',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',