
En el desarrollo web moderno, es fundamental crear una experiencia de usuario consistente e intuitiva que cubra todos los dispositivos disponibles. Los usuarios esperan que las funcionalidades operen sin problemas, ya sea desde una computadora de escritorio con un mouse o desde un celular con pantalla táctil.
Recibimos una solicitud de la comunidad para la implementación de una funcionalidad que resaltaba un desafío clave en este aspecto: acortar la brecha entre las interacciones de escritorio y las móviles dentro de nuestro Google Maps Add-On. La solicitud, que puede verse en GitHub, señalaba que, si bien hacer clic derecho sobre un marcador es una acción muy común en computadoras, no existía una alternativa equivalente para dispositivos móviles. Aceptamos el desafío y, luego de investigar y realizar varias pruebas, encontramos la solución. A continuación, te mostramos cómo lo logramos.
El Desafío: Llevar la Funcionalidad de Escritorio a Dispositivos Móviles
En una computadora de escritorio, el clic derecho es una acción poderosa y comprendida universalmente. No sirve únicamente para abrir menús; es un evento particular que los desarrolladores pueden utilizar para disparar funcionalidades personalizadas y contextuales. En nuestro Google Maps Add-On, los desarrolladores pueden vincularse fácilmente a este evento para crear interacciones sofisticadas.
Por ejemplo, podrías tener un marcador donde un clic derecho capture las coordenadas exactas para realizar un cálculo, o que verifique si la tecla Shift estaba presionada para así ejecutar una acción secundaria.
GoogleMapMarker officeMarker = googleMap.addMarker("Our Office",
new LatLon(-31.620173186615883, -60.67964404821396), false, Markers.PURPLE);
officeMarker.addRightClickListener(e -> {
String message = "Right-click at lat: " + e.getLatitude();
if (e.isShiftKey()) {
message += " (Shift was held down!)";
}
Notification.show(message);
});
Esto funciona a la perfección en una computadora, brindando a los desarrolladores acceso a datos enriquecidos del evento. Sin embargo, en un dispositivo móvil, esta funcionalidad antes era inaccesible. Al no existir un clic derecho físico, los usuarios móviles se quedaban sin poder utilizar estas características avanzadas basadas en eventos.
La Solución: Simular un Clic Derecho con Touch-and-Hold
A partir de la versión 2.3.0, el add-on detecta automáticamente el gesto de “mantener presionado” (touch-and-hold) sobre un marcador y lo traduce en un evento de clic derecho.
Esto significa que exactamente el mismo código Java ahora funciona a la perfección en dispositivos móviles sin necesidad de realizar ningún cambio. El usuario simplemente presiona y mantiene su dedo sobre un marcador por un instante y el RightClickListener se dispara, ofreciendo la experiencia consistente esperada.
Cómo Funciona: El Detrás de Escena
Para hacer esto posible, implementamos la lógica directamente en el web component de Google Maps. El componente escucha un evento mousedown, el cual funciona como un disparador universal tanto para un clic con el mouse como para un toque en la pantalla.
Cuando este evento se dispara, se inicia un breve temporizador. Si el usuario mantiene presionado por la duración del temporizador (alrededor de 800 milisegundos), el componente emite el evento de clic derecho personalizado. Si el usuario levanta el dedo o comienza a arrastrar el mapa, el temporizador se cancela. Esto asegura que el gesto sea verdaderamente intencional y ofrece una experiencia fluida e intuitiva.
A continuación, te mostramos el método principal que se introdujo para lograr esta funcionalidad:
/**
* Sets up touch-and-hold gesture detection to simulate a right-click on mobile devices.
*
* The implementation attaches the necessary event listeners to the marker to detect a long press (touch and hold).
* When a long press is detected, it fires a 'google-map-marker-rightclick' custom event (which the server-side API can listen for).
* It also handles the cancellation of the gesture if the user moves their finger, releases it too early, or starts dragging the marker.
* Finally, it prevents a standard 'click' event from firing after a successful long press to avoid duplicate actions.
*/
_setupTouchAndHold() {
// Only enable when clickEvents are on and device is touch/coarse pointer
const isTouch =
(typeof navigator !== "undefined" && navigator.maxTouchPoints > 0) ||
(typeof matchMedia === "function" &&
matchMedia("(pointer: coarse)").matches);
if (!this.clickEvents || !isTouch) {
return; // Skip setup in non-touch environments
}
// Avoid double-binding listeners
if (this._touchHoldInstalled) {
return;
}
// Duration in milliseconds to consider a press a "long press".
// This value balances being quick enough to feel responsive while being long
// enough to prevent accidental triggers. 800ms is a common choice.
const LONG_PRESS_DURATION = 800;
// Internal state variables
this._touchTimer = null; // A timer to track the duration of the press
this._suppressNextClick = this._suppressNextClick || false; // Flag to suppress the next click event if it follows a long press
// Listen for 'mousedown', which fires on both desktop clicks and mobile touch-starts
google.maps.event.addListener(this.marker, "mousedown", (e) => {
// Respect runtime toggling of clickEvents
if (!this.clickEvents) {
return;
}
// Ignore secondary button (desktop right-click)
if (e && e.domEvent && typeof e.domEvent.button === 'number' && e.domEvent.button === 2) {
return;
}
if (this._touchTimer) clearTimeout(this._touchTimer);
// Start the timer. If it completes, a long press has occurred.
this._touchTimer = setTimeout(() => {
// ensure the subsequent synthetic click is swallowed
this._suppressNextClick = true;
// Fire the custom event that simulates a right-click
this.fire("google-map-marker-rightclick", e);
// timer consumed
this._touchTimer = null;
}, LONG_PRESS_DURATION);
});
// Helper function to cancel timer
const clearTimer = () => {
if (this._touchTimer) {
clearTimeout(this._touchTimer);
this._touchTimer = null;
}
};
// Cancel the timer if the user releases, drags, or moves off the marker
google.maps.event.addListener(this.marker, "mouseup", clearTimer);
google.maps.event.addListener(this.marker, "dragstart", clearTimer);
google.maps.event.addListener(this.marker, "mouseout", clearTimer);
this._touchHoldInstalled = true;
},
});
Si querés profundizar un poco más, puedes ver la implementación completa aquí.
Verlo en Acción: Un Ejemplo Práctico
La mejor manera de experimentar esta nueva funcionalidad es probándola. Puedes usar nuestra demo online para verla en acción.
Pruébala en vivo aquí: Demo de Add Markers de Google Maps.
Cómo reproducir el comportamiento:
- Navega a la página de la demo Add Markers.
- En las opciones debajo del mapa, marca la casilla “Right Click”.
- Haz clic en el botón “Add Marker”. Aparecerá un nuevo marcador en el centro del mapa.
- Ponlo a prueba:
- En escritorio: Haz clic derecho sobre el nuevo marcador.
- En un dispositivo móvil: Mantén presionado el dedo sobre el marcador por un instante.
En ambos casos, aparecerá una notificación mostrando los datos enriquecidos del evento capturados por el listener, incluyendo las coordenadas y cualquier tecla modificadora utilizada.
El código detrás de la demo
Esta funcionalidad se logra con apenas unas líneas de código en la demo. Como puedes ver en el fragmento a continuación, simplemente agregamos un RightClickListener al marcador. El add-on ahora se encarga de toda la lógica del lado del cliente para garantizar que este único listener funcione tanto en escritorio como con gestos en dispositivos móviles.
// From AddMarkersDemo.java
// ... inside the "Add Marker" button's click listener ...
if (withRightClick.getValue()) {
marker.addRightClickListener(e -> {
Div text = new Div(new Text("Alt key: " + e.isAltKey()), new HtmlComponent("br"),
new Text("Shift key: " + e.isShiftKey()), new HtmlComponent("br"),
new Text("Ctrol key: " + e.isCtrlKey()), new HtmlComponent("br"),
new Text("Click counts: " + e.getClickCount()), new HtmlComponent("br"),
new Text("Latitude: " + e.getLatitude()), new HtmlComponent("br"),
new Text("Longitude: " + e.getLongitude()));
Notification notification = new Notification();
Button closeButton = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
closeButton.addClickListener(event -> {
notification.close();
});
HorizontalLayout layout = new HorizontalLayout(text, closeButton);
layout.setAlignItems(Alignment.CENTER);
notification.add(layout);
notification.open();
});
}
Conclusión
Acortar la brecha de interacción entre escritorio y móvil es un paso fundamental para crear aplicaciones verdaderamente universales. Esta actualización simplifica ese desafío al traducir automáticamente los gestos táctiles en los eventos que los desarrolladores ya utilizan. Al encargarnos de esta complejidad, facilitamos la creación de funcionalidades de mapas ricas e interactivas que ofrecen una experiencia consistente y confiable para cada usuario, en cualquier dispositivo.
Esperamos que esta funcionalidad resulte útil. Asegúrate de explorar todo lo demás que el Google Maps Add-On tiene para ofrecer en nuestra aplicación de demos centralizada. Si tienes ideas para mejorar esta o cualquier otra parte del add-on, háznoslo saber abriendo un issue en el repositorio de GitHub.
Puedes encontrar más información sobre las versiones existentes y las versiones de Vaadin soportadas en el Directorio de Vaadin. Actualmente, el add-on está disponible para las versiones de Vaadin: 14, 23, 24 y 25.
¡Gracias por leer y let’s keep the code flowing!
¡Únete a la conversación!