> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/creator/content-creator-es/scenes-sdk7/ui-2d/ui_button_events.md).

# Eventos de Button de UI

Gestiona eventos de Button en entidades de UI.

Para crear un Button en tu UI, crea un `Button` elemento de UI con las siguientes propiedades:

* `value`: Una cadena con el texto que se mostrará en el botón.
* `onMouseDown`: Una función callback que se ejecuta cada vez que el usuario pulsa el botón del puntero sobre la Entity.
* `uiTransform`: Propiedades de posicionamiento del elemento de UI.

El siguiente ejemplo muestra cómo crear un Button de UI clicable.

***Archivo ui.tsx:***

```tsx
import { Button } from '@dcl/sdk/react-ecs'

export const uiMenu = () => (
	<Button
		value="Haz clic en mí"
		uiTransform={{ width: 100, height: 100 }}
		onMouseDown={() => {
			console.log('Se hizo clic en la UI')
		}}
	/>
)
```

***Archivo index.ts:***

```ts
import { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
import { uiMenu } from './ui'

export function main() {
    ReactEcsRenderer.setUiRenderer(uiMenu)
}
```

{% hint style="warning" %}
**📔 Nota**: Todos los siguientes fragmentos de esta página asumen que tienes un `.ts` similar al anterior, ejecutando la `ReactEcsRenderer.setUiRenderer()` función.
{% endhint %}

También puedes escribir la función que se ejecuta al hacer clic fuera de la definición de la UI, y referenciarla por nombre. Esto ayuda a que el código de la UI sea más legible, y también es útil si varias entidades de UI clicables necesitan llamar a la misma función.

```tsx
import { Button } from '@dcl/sdk/react-ecs'

function handleClick() {
	// Hacer algo en onClick
	console.log('Se hizo clic en la UI')
}
export const uiMenu = () => (
	<Button
		value="Haz clic en mí"
		uiTransform={{ width: 100 }}
		onMouseDown={handleClick}
	/>
)
```

Los siguientes campos se pueden agregar a un `Button` elemento de UI:

* `onMouseDown`: Una función callback que se ejecuta cada vez que el usuario pulsa el botón del puntero sobre la Entity.
* `onMouseUp`: Una función callback que se ejecuta cada vez que se suelta el botón del puntero mientras se apunta a la Entity.
* `onMouseEnter`: Una función callback que se ejecuta cada vez que el puntero comienza a pasar sobre el botón.
* `onMouseLeave`: Una función callback que se ejecuta cada vez que el puntero deja de pasar sobre el botón.
* `color`: Color del texto del botón.
* `font`: Fuente del texto del botón.
* `textAlign`: Alineación del texto dentro del botón
* `uiTransform`: Propiedades de posicionamiento del elemento de UI.
* `uiBackground`: Establece el color o la textura del elemento de UI.
* `variant`: Usa esta propiedad para establecer el estilo del botón como una de las opciones predeterminadas. `primary` y `secondary` están disponibles.
* `disabled`: Booleano para deshabilitar un botón. Cuando `disabled` se establece en *true*, las `onMouseDown` y `onMouseUp` acciones dejan de llamarse, y el botón deja de anunciar cualquier interacción con el puntero. El botón también se dibuja "atenuado": tanto el texto como el fondo se renderizan con la mitad de su `alpha` value. Este es solo un cambio visual. Los valores de Color4 que tu escena pasa nunca se modifican, así que puedes reutilizar con seguridad un objeto de paleta compartido entre muchos elementos. `Color4` estilo de Button

## Estilo de Button

Establece el variant en `primary` o `secondary` para aprovechar las opciones de estilo predeterminadas de los Button. `primary` hace que tu botón sea rojo con texto blanco, `secondary` hace que tu botón sea blanco con texto rojo.

```tsx
import { UiEntity, Button, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
	<UiEntity
		uiTransform={{
			width: 500,
			height: 230,
			margin: '16px 0 8px 270px',
			padding: 4,
			alignSelf: 'center',
		}}
		uiBackground={{ color: Color4.Gray() }}
	>
		<Button
			value="Haz clic en mí"
			variant="primary"
			uiTransform={{ width: 80, height: 20, margin: 4 }}
			onMouseDown={() => {
				console.log('Se hizo clic en la UI')
			}}
		/>
		<Button
			value="Haz clic en mí"
			variant="secondary"
			uiTransform={{ width: 80, height: 20, margin: 4 }}
			onMouseDown={() => {
				console.log('Se hizo clic en la UI')
			}}
		/>
	</UiEntity>
)
```

También eres libre de usar todas las propiedades del fondo libremente. También puedes establecer un variant y luego sobrescribir algunas de sus propiedades. Este ejemplo usa el `primary` variant, pero sobrescribe el color para que sea verde:

```tsx
import { Button } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
	<Button
		value="¡Mi Button!"
		variant="primary"
		uiTransform={{ width: 100, height: 100 }}
		onMouseDown={() => {
			console.log('¡Se hizo clic en Mi Button!')
		}}
		uiBackground={{
			color: Color4.Green(),
		}}
	/>
)
```

## Botones conmutables

Un caso de uso común es hacer que un botón cambie entre dos estados, como un interruptor. El siguiente ejemplo cambia entre dos colores cada vez que se presiona el botón:

```tsx
import { Button } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

let buttonEnabled = false

export const uiMenu = () => (
	<Button
		value="Mi Button"
		variant="primary"
		uiTransform={{ width: 100, height: 100 }}
		onMouseDown={() => {
			console.log('¡Se hizo clic en Mi Button!')
			buttonEnabled = !buttonEnabled
			if (buttonEnabled) {
				// hacer algo
			} else {
				// hacer otra cosa
			}
		}}
		uiBackground={{
			color: buttonEnabled ? Color4.Green() : Color4.Red(),
		}}
	/>
)
```

Ten en cuenta que en el ejemplo anterior, el color depende de una `variable buttonEnabled` . Cada vez que el valor de esta variable cambia, afecta inmediatamente al color de fondo.

## Feedback al pasar el cursor

Otro caso de uso común es mostrar algún tipo de indicación visual al pasar el cursor sobre un botón, para dejar claro que es interactuable, o incluso mostrar un hint al pasar el cursor que explique qué hace este botón. Usa los `onMouseEnter` y `onMouseLeave` callbacks para detectar cuándo el cursor del jugador está sobre el botón y reaccionar en consecuencia.

```tsx
import { Button } from '@dcl/sdk/react-ecs'

let buttonEnabled = false

export const uiMenu = () => (
	<Button
		value="Mi Button"
		uiTransform={{ width: 100, height: 100 }}
		onMouseDown={() => {
			// función del botón
		}}
		onMouseEnter={() => {
			// mostrar hint
		}}
		onMouseLeave={() => {
			// ocultar hint
		}}
	/>
)
```

## Hacer clicables otros elementos

Cualquier elemento de la UI se puede hacer clicable agregándole un `onMouseDown` propiedad, funciona de forma idéntica a un botón. El siguiente ejemplo agrega `onMouseDown` propiedades a las imágenes de fondo y al texto.

```tsx
import { UiEntity, Label, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'
import { engine, Transform } from '@dcl/sdk/ecs'

export const uiMenu = () => (
	<UiEntity
		onMouseDown={() => {
			console.log('¡Se hizo clic en el fondo!')
		}}
		uiTransform={{
			width: 400,
			height: 230,
		}}
		uiBackground={{ color: Color4.create(0.5, 0.8, 0.1, 0.6) }}
	>
		<Label
			onMouseDown={() => {
				console.log('¡Se hizo clic en la etiqueta!')
			}}
			value={`Jugador: ${getPlayerPosition()}`}

			fontSize={18}
			uiTransform={{ width: '100%', height: 30 }}
		/>
	</UiEntity>
)

function getPlayerPosition() {
	const playerPosition = Transform.getOrNull(engine.PlayerEntity)
	if (!playerPosition) return 'desconocido'
	const { x, y, z } = playerPosition.position
	return `{x: ${x.toFixed(2)}, y: ${y.toFixed(2)}, z: ${z.toFixed(2)} }`
}
```

## Bloqueo del puntero

Todas las entidades de UI no bloquean el puntero de forma predeterminada, lo que significa que los clics de los jugadores las atravesarán e interactuarán con objetos en el espacio del mundo 3D que hay detrás de ellas. Si una entity tiene un `onMouseDown` callback, entonces se vuelve bloqueante para el puntero, así que los clics del jugador no afectan a lo que hay detrás de esa entity de UI.

Puedes cambiar este comportamiento predeterminado cambiando el valor de `pointerFilter` la propiedad del `uiTransform` component en cualquier entity de UI. Por ejemplo, para establecer una entity que no tiene ningún `onMouseDown` como bloqueante para el puntero.

Los valores admitidos para `pointerFilter` son:

* `block`: El elemento de UI bloquea el puntero; los jugadores no pueden hacer clic en nada detrás de este elemento de UI.
* `none`: El elemento de UI no bloquea el puntero. El elemento no es clicable y se puede hacer clic en cualquier cosa detrás de él.

A continuación hay una UI sencilla que no tiene un `onMouseDown`, pero que anula el comportamiento predeterminado de no bloquear el puntero estableciendo `pointerFilter` en `block`.

```tsx
import { UiEntity, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

// dibuja la UI
export const uiMenu = () => (
	<UiEntity
		uiTransform={{
			width: '100%',
			height: '100px',
			pointerFilter: `block`,
		}}
		uiText={{ value: `Este elemento bloquea el puntero`, fontSize: 40 }}
		uiBackground={{ color: Color4.create(0.5, 0.8, 0.1, 0.6) }}
	/>
)
```

### El bloqueo sigue la caja de diseño, no los píxeles visibles

Un elemento bloqueante captura los clics a lo largo de todo su **rectángulo completo**, haya o no algo dibujado allí. Un fondo completamente transparente no hace ninguna diferencia.

{% hint style="danger" %}
**Advertencia:** Nunca pongas un controlador de puntero o `pointerFilter: 'block'` en un contenedor de pantalla completa dimensionado `100%` por `100%`.

Su rectángulo es toda la pantalla, así que un único `onMouseDown` en la raíz de tu diseño hace que todos los demás elementos de UI y todo lo que hay en el mundo 3D no se puedan hacer clic. La UI sigue viéndose perfectamente correcta, porque el panel que puedes ver solo cubre una pequeña parte de la pantalla, lo que hace que esto sea muy difícil de detectar.
{% endhint %}

Adjunta los controladores al elemento más pequeño que los necesite: el panel, el botón, la fila. Los contenedores de diseño se quedan sin controladores.

Hay dos casos en los que un elemento bloqueante de pantalla completa es lo correcto, y ambos son deliberados:

* Un **fondo modal**, que está pensado para absorber los clics, y que solo renderizas mientras el modal está abierto.
* Un **capturador de liberación de arrastre**, que solo existe mientras hay un arrastre en progreso. Consulta [Interacciones de arrastre](#drag-interactions) más abajo.

Si los clics dejan de funcionar en cualquier parte de tu escena, esto es lo primero que debes comprobar.

## Interacciones de arrastre

Los controladores de puntero de la UI (`onMouseDown`, `onMouseUp`, `onMouseEnter`, `onMouseLeave`) no toman parámetros. Se activan como simples `() => void` callbacks sin datos de posición ni coordenadas. No existe ningún `onMouseDrag` o `onMouseMove` controlador en el sistema de UI.

Para construir UI basada en arrastre (sliders, barras de arrastre, asas de arrastre), usa `PrimaryPointerInfo.screenDelta` de `@dcl/sdk/ecs`. Esto proporciona el movimiento del mouse en píxeles desde el último frame, actualizado en cada frame sin importar sobre qué esté el cursor.

El patrón funciona así:

1. `onMouseDown` en el objetivo de arrastre inicia el arrastre y registra el valor inicial.
2. Un sistema lee `screenDelta` en cada frame y lo acumula en el valor mientras el arrastre está activo.
3. Una superposición invisible de pantalla completa con `pointerFilter: 'block'` captura la liberación del mouse, de modo que soltar fuera del objetivo estrecho todavía termina el arrastre.

```ts
import { engine, PrimaryPointerInfo, UiCanvasInformation } from '@dcl/sdk/ecs'

let dragging = false
let sliderValue = 0.5

// Llama a este sistema en cada frame para acumular el movimiento de arrastre
engine.addSystem(() => {
	if (!dragging) return

	const delta = PrimaryPointerInfo.getOrNull(engine.RootEntity)?.screenDelta
	if (!delta || delta.x === 0) return

	// Convierte los píxeles de pantalla a un rango de 0–1
	// Divide por el factor de escala de la UI para que la velocidad del arrastre coincida con el cursor
	const canvas = UiCanvasInformation.getOrNull(engine.RootEntity)
	const scale = canvas ? Math.min(canvas.width / 1920, canvas.height / 1080) : 1
	const trackWidth = 200 // ancho virtual en px de la pista del slider

	sliderValue = Math.max(0, Math.min(1, sliderValue + delta.x / scale / trackWidth))
})
```

{% hint style="warning" %}
**Nota:** En dispositivos móviles, `screenDelta` siempre informa 0 (no hay cursor de movimiento libre). Para sliders compatibles con móviles, agrega botones stepper (`-` / `+`) junto a la pista de arrastre. Usa [`isMobile()`](/creator/content-creator-es/desarrollar-para-movil/desarrollar/detect-platform.md) de `de @dcl/sdk/platform` para dividir tu UI en ramas.
{% endhint %}

{% hint style="info" %}
**Consejo:** Divide siempre `screenDelta` por el factor de escala de la UI, o el arrastre se pasará o se quedará corto en pantallas cuya resolución difiera de tu tamaño virtual.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.decentraland.org/creator/content-creator-es/scenes-sdk7/ui-2d/ui_button_events.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
