Final fixes for photo overlay position and rotation
This commit is contained in:
@@ -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' },
|
||||||
|
|||||||
@@ -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,34 +130,34 @@ 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"
|
||||||
>
|
/>
|
||||||
|
|
||||||
|
{/* Capa de UI sobre la cámara */}
|
||||||
<View style={styles.overlay}>
|
<View style={styles.overlay}>
|
||||||
{/* Header */}
|
{/* Header con botón cerrar */}
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.closeBtn}>
|
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.closeBtn}>
|
||||||
<Text style={styles.closeTxt}>✕</Text>
|
<Text style={styles.closeTxt}>✕</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Footer Preview con rotación */}
|
{/* Preview del sello (posicionado en su esquina con rotación) */}
|
||||||
{config && meta && (
|
{config && meta && (
|
||||||
<View style={[
|
<View style={styles.fullOverlay} pointerEvents="none">
|
||||||
styles.footerWrapper,
|
|
||||||
{ transform: [{ rotate: `${rotation}deg` }] }
|
|
||||||
]}>
|
|
||||||
<FooterOverlay
|
<FooterOverlay
|
||||||
config={config}
|
config={config}
|
||||||
meta={meta}
|
meta={meta}
|
||||||
imageWidth={winW > winH ? winH : winW}
|
imageWidth={Math.min(winW, winH)}
|
||||||
|
rotation={rotation}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Controls */}
|
{/* Botón de disparo abajo al centro */}
|
||||||
<View style={styles.controls}>
|
<View style={styles.footerControls}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.captureBtn, taking && styles.disabled]}
|
style={[styles.captureBtn, taking && styles.disabled]}
|
||||||
onPress={() => void capture()}
|
onPress={() => void capture()}
|
||||||
@@ -165,43 +167,34 @@ export function CameraScreen({ route, navigation }: Props) {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</CameraView>
|
|
||||||
</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',
|
||||||
|
|||||||
Reference in New Issue
Block a user