UI personalizado (Scanning Screen) o Pantalla de escaneo personalizada – Agrega un extra a tus aplicaciones de realidad aumentada usando MindAR

En este tutorial quiero enseñarte como realizar una modificación al UI que MindAR trae por defecto en su Scanning Screen.
¿Quieres saber de qué estoy hablando? Te invito a ingresar a la documentación de MindAR: Custom UI | mind-ar-js
La idea de este tutorial se dio gracias a un usuario de la comunidad de WhatSapp (ingresa aquĆ y haz parte de la comunidad). Mas especĆficamente a Samuel Diaz Ramirez.
Samuel nos compartió sus archivos CSS y JS en la comunidad y puedes descargarlos desde aquĆ.
La idea de Samuel me pareció perfecta para lo que siempre he soƱado con este blog, que es crear una gran comunidad donde podamos compartir nuestro aprendizaje, conocimiento, ideas y proyectos de realidad aumentada usando librerĆas de código abierto para todos los que queremos aprender sobre esta tecnologĆa.
Cuando nació Realidad Aumentada Empezando Desde Cero por allÔ por el año 2009, el blog era un subdominio de una web llamada inkframe y por el año 2010 decidà crear lo que hoy se conoce como una de las webs de enseñanza de realidad aumentada con mÔs años online.
Si lo sĆ©, la web estuvo unos aƱos con muy poca actividad, pero volvĆ para quedarme y volver a compartir todo lo relacionado al aprendizaje y creación de aplicaciones de realidad aumentada enfocada a la web que es donde creo que esta el futuro de esta tecnologĆa.
con esta introducción super épica (Risas) comenzamos el tutorial.
Mira aquĆ el resultado de la app que vamos a crear.
El secreto de esta app se basa en el desarrollo del CSS. Apenas dos o tres cosas en nuestro HTML de realidad aumentada se deben de agregar. te muestro:
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>MindAR App</title> <!-- Incluimos A-Frame y MindAR A-Frame --> <script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/mind-ar@1.2.5/dist/mindar-image-aframe.prod.js"></script> <style> /* AQUI VA EL CSS QUE TE ENSEĆARE MAS ADELANTE DE ESTE TUTORIAL*/ </style> </head> <body> <div id="ui-custom-tracking-overlay" class="ui-custom-tracking-overlay"> <div class="ui-tracking-square"> <img src="target.png" alt="Marker Target" class="scanning-image"> <div class="scanning-message">Escanea tu marcador</div> <div class="ui-scan-line"></div> <span></span> </div> </div> <a-scene mindar-image="imageTargetSrc: ./targets.mind; uiScanning: #ui-custom-tracking-overlay; filterMinCF:0.0001; filterBeta:500; warmupTolerance:2; missTolerance:5;" color-space="sRGB" renderer="colorManagement: true, physicallyCorrectLights: true" vr-mode-ui="enabled: false" device-orientation-permission-ui="enabled: false"> <a-assets> <a-asset-item id="WonderWoman" src="./WonderWoman2.glb"></a-asset-item> </a-assets> <a-camera position="0 0 0" look-controls="enabled: false"></a-camera> <a-entity mindar-image-target="targetIndex: 0"> <a-gltf-model rotation="0 0 0" position="0 0 0" scale="0.5 0.5 0.5" gltf-model="#WonderWoman"> </a-gltf-model> </a-entity> </a-scene> <script> //AQUI VA EL JAVASCRIPT </script> </body> </html>
Como puedes ver, es un código bÔsico HTML para crear una aplicación de realidad aumentada usando MindAR.
Te recomiendo este tutorial por si consideras que este post estĆ” muy avanzado: Aprende Realidad Aumentada Desde Cero: Guia para empezar |
¿Dónde sucede la magia de la personalización del UI?
Desde la lĆnea 17 hasta la 28 del código, mĆ”s especĆficamente en:
- <div id=»ui-custom-tracking-overlay» class=»ui-custom-tracking-overlay»>
- <div class=»ui-tracking-square»>
- <div class=»scanning-message»>Escanea tu marcador</div>
- <div class=»ui-scan-line»></div>
Una explicación simple con un ejemplo: el atributo class=Ā»ui-scan-lineĀ» crea un vĆnculo. Esto le dice al navegador que este <div> en particular debe tener el estilo de todas las reglas CSS que se aplican al selector de clase .ui-scan-line.
En resumen, el HTML crea el elemento y la clase lo etiqueta, para que el CSS pueda encontrarlo y aplicarle el estilo correspondiente.
Examinemos el CSS
<style>
/* Definimos las variables CSS */
:root {
--primary-colorUI: #0066ff;
--secondary-colorUI: #0080ff;
--corner-size: 35px;
--square-size: 60vmin;
--scan-line-height: 4px;
--border-thickness: 3px;
}
/* Ocultamos los elementos de UI predeterminados de MindAR para evitar duplicados */
.mindar-ui-overlay, .mindar-ui-target, .a-enter-vr-button {
display: none !important;
visibility: hidden !important;
}
/* Contenedor principal de nuestra superposición de escaneo personalizada */
.ui-custom-tracking-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none; /* Permite clics a travƩs del overlay */
z-index: 9;
background-color: rgba(0, 0, 0, 0.8);
opacity: 1;
transition: opacity 0.5s ease;
}
/* Clase para ocultar la superposición cuando el marcador es detectado */
.ui-custom-tracking-overlay.hidden {
opacity: 0;
}
/* El cuadro de escaneo principal */
.ui-tracking-square {
position: absolute;
width: var(--square-size);
height: var(--square-size);
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/* Usamos flexbox para centrar y ordenar el contenido interno */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
/* Estilo para las esquinas del cuadro de escaneo */
.ui-tracking-square::before,
.ui-tracking-square::after {
content: '';
position: absolute;
width: var(--corner-size);
height: var(--corner-size);
border: var(--border-thickness) solid var(--primary-colorUI);
filter: drop-shadow(0 0 8px var(--primary-colorUI)) drop-shadow(0 0 16px rgba(0, 102, 255, 0.4)) drop-shadow(0 0 24px rgba(0, 102, 255, 0.2));
animation: ui-corner-glow 3s infinite ease-in-out;
}
/* Esquina superior izquierda */
.ui-tracking-square::before {
top: -2px;
left: -2px;
border-right: none;
border-bottom: none;
border-top-left-radius: 8px;
}
/* Esquina inferior derecha */
.ui-tracking-square::after {
bottom: -2px;
right: -2px;
border-left: none;
border-top: none;
border-bottom-right-radius: 8px;
}
/* Contenedor adicional para las otras dos esquinas */
.ui-tracking-square span::before,
.ui-tracking-square span::after {
content: '';
position: absolute;
width: var(--corner-size);
height: var(--corner-size);
border: var(--border-thickness) solid var(--primary-colorUI);
filter: drop-shadow(0 0 8px var(--primary-colorUI)) drop-shadow(0 0 16px rgba(0, 102, 255, 0.4)) drop-shadow(0 0 24px rgba(0, 102, 255, 0.2));
animation: ui-corner-glow 3s infinite ease-in-out;
}
/* Esquina superior derecha */
.ui-tracking-square span::before {
top: -2px;
right: -2px;
border-left: none;
border-bottom: none;
border-top-right-radius: 8px;
}
/* Esquina inferior izquierda */
.ui-tracking-square span::after {
bottom: -2px;
left: -2px;
border-right: none;
border-top: none;
border-bottom-left-radius: 8px;
}
/* Estilo para la imagen dentro del recuadro de escaneo */
.scanning-image {
width: 80%;
height: 80%;
object-fit: contain; /* Asegura que la imagen se ajuste sin distorsionarse */
opacity: 0.7; /* Hacemos la imagen un poco transparente */
margin-bottom: 10px; /* Espacio entre la imagen y el texto */
}
/* Estilo para el mensaje de escaneo */
.scanning-message {
color: var(--primary-colorUI);
font-size: clamp(1rem, 4vw, 1.5rem);
font-family: sans-serif;
text-shadow: 0 0 5px var(--primary-colorUI), 0 0 10px rgba(0, 102, 255, 0.6);
text-align: center;
}
/* Estilo para la barra de escaneo - ahora una lĆnea recta */
.ui-scan-line {
position: absolute;
left: 0;
width: 100%;
height: var(--scan-line-height);
background: var(--primary-colorUI); /* LĆnea recta */
box-shadow: 0 0 5px var(--primary-colorUI), 0 0 10px rgba(0, 102, 255, 0.6);
animation: ui-scan-animation 3s infinite ease-in-out;
opacity: 0.8;
}
/* Animación del movimiento de la barra de escaneo */
@keyframes ui-scan-animation {
0%, 100% { top: 0; opacity: 0.7; }
50% { top: 100%; opacity: 1; }
}
/* Animación para el brillo de las esquinas */
@keyframes ui-corner-glow {
0% {
filter: drop-shadow(0 0 8px var(--primary-colorUI)) drop-shadow(0 0 16px rgba(0, 102, 255, 0.4));
border-color: var(--primary-colorUI);
}
100% {
filter: drop-shadow(0 0 12px var(--primary-colorUI)) drop-shadow(0 0 24px rgba(0, 102, 255, 0.6)) drop-shadow(0 0 32px rgba(0, 102, 255, 0.2));
border-color: var(--secondary-colorUI);
}
}
</style>
Si quieres aprender mĆ”s sobre HTML y CSS te recomiendo este super curso: Aprende HTML y CSS – Curso Desde Cero
Empecemos con la Explicación del CSS
Imagina que el HTML es el lienzo donde pondrƔs todos los objetos (texto, imƔgenes, videos), mientras que el CSS es la caja de herramientas con la que les darƔs forma, color y movimiento.
El código que tienes es como una lista detallada de instrucciones para que el navegador Ā«ponga un marco de neónĀ» alrededor de un Ć”rea especĆfica.
Analizando el Código
Dividamos el CSS en partes para entenderlo mejor:
1 – Variables Globales (:root)
:root { …} es como la sección donde el artista prepara sus colores base. Define variables personalizadas para que, si quiere cambiar el color del neón o el tamaƱo del cuadro, solo tenga que hacerlo una vez en un solo lugar.
–primary-colorUI: #0066ff;: Guarda el color azul neón principal.
–corner-size: 35px;: Define el tamaƱo de las esquinas.
–scan-line-height: 4px;: Guarda el grosor de la lĆnea de escaneo.
Si quieres usar un verde neón en lugar de azul, solo tienes que cambiar el valor de –primary-colorUI y todo el diseƱo se actualizarĆ” automĆ”ticamente.
2 – Ocultando la Interfaz por Defecto
Esta sección oculta la interfaz de usuario que MindAR trae por defecto, para que no interfiera con nuestro diseño personalizado.
.mindar-ui-overlay, .mindar-ui-target, .a-enter-vr-button: Los selectores (.) señalan a los elementos de la pÔgina. El display: none !important; le dice al navegador: «”Quita estos elementos de la vista por completo y no dejes que nada los muestre!». La palabra !important es como un comando de emergencia para asegurar que esto se cumpla.
3 – El Contenedor de la Superposición
El contenedor .ui-custom-tracking-overlay es como una lƔmina semitransparente que cubre toda la pantalla.
position: fixed;: Lo fija en su lugar para que no se mueva al hacer scroll.
width: 100%; height: 100%;: Hace que ocupe todo el espacio de la pantalla.
background-color: rgba(0, 0, 0, 0.8);: Lo hace negro y un 80% transparente, creando el efecto de «dimmer».
pointer-events: none;: Permite que los clics del mouse o los toques del dedo pasen a travƩs de Ʃl. Es esencial para que el usuario pueda interactuar con lo que estƔ debajo.
transition: opacity 0.5s ease;: Permite que la superposición desaparezca de forma suave, en medio segundo, cuando el marcador sea detectado.
4 – El Cuadro de Escaneo Principal
.ui-tracking-square es el recuadro que se ve en el centro de la pantalla.
position: absolute;: Le permite posicionarse con precisión dentro de su contenedor (.ui-custom-tracking-overlay).
top: 50%; left: 50%;: Lo coloca en el centro de la pantalla.
transform: translate(-50%, -50%);: Mueve el cuadro hacia atrÔs la mitad de su propio tamaño para que el centro del cuadro coincida perfectamente con el centro de la pantalla. Esto es un truco muy útil para centrar elementos.
display: flex;: Lo convierte en un contenedor flexible, lo que facilita alinear la imagen y el texto en su interior.
5 – Las Esquinas
Las esquinas del cuadro no son elementos HTML reales, sino que se crean con pseudo-elementos ::before y ::after. Piensa en ellos como pequeños «dibujos» que el navegador crea y adjunta al elemento .ui-tracking-square.
content: Ā»;: Les dice a los pseudo-elementos que son invisibles por sĆ mismos.
position: absolute;: Les permite ser colocados con precisión en las esquinas
border: var(–border-thickness) solid var(–primary-colorUI);: Les da el borde de neón.
filter: drop-shadow(…): Este es el truco del neón. Crea una sombra brillante alrededor del borde, dĆ”ndole el efecto de resplandor.
animation: ui-corner-glow 3s infinite ease-in-out;: Aplica una animación que hace que las esquinas «parpadeen» suavemente cada 3 segundos.
Cada una de las cuatro esquinas se posiciona cortando los bordes que no queremos ver (por ejemplo, border-right: none;) y dÔndole un radio de borde en la esquina que sà queremos (border-top-left-radius: 8px;).
6 – El Contenido Interno
.scanning-image: La imagen dentro del recuadro. width: 80%; y height: 80%; hacen que ocupe el 80% del espacio, y object-fit: contain; asegura que no se distorsione.
.scanning-message: El texto «Escanea tu marcador». text-shadow es el truco para darle un resplandor de neón al texto, igual que con las esquinas.
7 – La LĆnea de Escaneo
.ui-scan-line es un simple div rectangular que se mueve de arriba abajo.
position: absolute;: Le permite moverse libremente dentro del cuadro de escaneo.
background: var(–primary-colorUI);: Le da un color sólido, como una lĆnea recta.
box-shadow(…): Le da un resplandor sutil para que se vea como si estuviera Ā«escaneandoĀ».
animation: ui-scan-animation 3s infinite ease-in-out;: Le aplica una animación que lo hace subir y bajar cada 3 segundos.
8 – Las Animaciones (@keyframes)
@keyframes. Le dice al navegador cómo se deben mover o cambiar los elementos con el tiempo.
@keyframes ui-scan-animation: Define el movimiento de la lĆnea de escaneo. En el 0% de la animación estĆ” arriba, en el 50% estĆ” abajo y en el 100% vuelve a estar arriba.
@keyframes ui-corner-glow: Define el efecto de brillo de las esquinas. Le dice que empiece con un brillo y termine con otro, y que el navegador lo repita infinitamente.
Terminamos de explicar el CSS. Espero te sea de gran ayuda la explicación.
Pasemos ahora al JavaScript que no es muy largo para este tipo de personalizacion.
<script>
// Obtenemos el elemento de la superposición de escaneo por su ID
const scanningOverlay = document.getElementById('ui-custom-tracking-overlay');
// Obtenemos la escena A-Frame
const sceneEl = document.querySelector('a-scene');
// Escuchamos el evento 'targetFound' que MindAR emite cuando detecta un marcador
sceneEl.addEventListener('targetFound', function () {
// Cuando se encuentra el marcador, agregamos la clase 'hidden' para ocultarlo
scanningOverlay.classList.add('hidden');
});
// Escuchamos el evento 'targetLost' que MindAR emite cuando se pierde el marcador
sceneEl.addEventListener('targetLost', function () {
// Cuando se pierde el marcador, quitamos la clase 'hidden' para volver a mostrarlo
scanningOverlay.classList.remove('hidden');
});
</script>
Si quieres tener todo este código en un solo archivo html te recomiendo seguir la comunidad de Realidad Aumentada Empezando Desde Cero en WhatsApp para que no te pierdas ni un solo tutorial y descargas de código. Al ingresar a la comunidad pide este código.
Desafortunadamente Whatsapp no muestra el historial de mensajes de los chats para este tipo de comunidad entonces para que no te pierdas de nada allĆ” te espero.