Saltar al contenido
zabloo

Image

El nodo que enseña una imagen. Su fichero viaja dentro del envelope, así que el juego no descarga nada, y su tamaño natural es el del propio original.

primitivedesde v1sin focus

Un Image dibuja una imagen dentro de un rectángulo. Usas uno para iconos, retratos y banners: la moneda que va al lado de un precio, el arte de un objeto en un hueco del inventario, la cabecera del panel de una tienda. El fichero no se descarga en tiempo de ejecución: zabloo export lo lee del disco y mete sus bytes en el envelope, así que lo que recibe el juego es una imagen que ya tiene. El nodo no acepta hijos, porque es un rect con píxeles dentro y todo lo demás es layout.

image-fit.viewIR v1
The same square star icon in four boxes of equal size: contain shows it whole and letterboxed, cover fills the box and crops it, stretch fills it and distorts it, and the fourth is the contained star tinted gold through style.color.
ESTADO
  • hover — inactivo
  • pressed — inactivo
  • focused — inactivo
  • selected — inactivo
  • disabled — inactivo

Se lee del último frame dibujado

Parado: pulsa Ejecutar para dibujarlo en la GPU.

import { Column, Image, Row, Text } from "@zabloo/react";
import type { ReactNode } from "react";


const BOX = { width: 132, height: 84 } as const;

const FRAME = {
  background: "{color.slot}",
  radius: "{radius.md}",
  borderWidth: "{border.hairline}",
  borderColor: "{color.line}",
} as const;

function Sample({ caption, children }: { caption: string; children: ReactNode }) {
  return (
    <Column layout={{ gap: "{space.2}", align: "center" }}>
      {children}
      <Text style={{ color: "{color.faint}", fontSize: "{text.xs}" }}>{caption}</Text>
    </Column>
  );
}

export default function ImageFit() {
  return (
    <Column
      layout={{ grow: 1, justify: "center", align: "center", padding: "{space.6}" }}
      style={{ background: "{color.bg}" }}
    >
      <Row layout={{ gap: "{space.3}", align: "start" }}>
        <Sample caption="contain">
          {/* The whole image, undistorted, letterboxed in the box. */}
          <Image src="icons/star.png" fit="contain" layout={BOX} style={FRAME} />
        </Sample>

        <Sample caption="cover">
          {/* Fills the rect, undistorted, cropping the overflowing axis. */}
          <Image src="icons/star.png" fit="cover" layout={BOX} style={FRAME} />
        </Sample>

        <Sample caption="stretch">
          {/* Fills it exactly, aspect ratio and all. */}
          <Image src="icons/star.png" fit="stretch" layout={BOX} style={FRAME} />
        </Sample>

        <Sample caption="style.color">
          {/* The tint multiplies per channel — absent = white = the pixels as
              they are, so this needs no second image. */}
          <Image
            src="icons/star.png"
            fit="contain"
            layout={BOX}
            style={{ ...FRAME, color: "{color.gold}" }}
          />
        </Sample>
      </Row>
    </Column>
  );
}
Un mismo original cuadrado en cuatro cajas del mismo tamaño. Fíjate en a qué renuncia cada una: contain conserva la imagen entera y deja franjas, cover llena la caja y pierde los bordes, stretch la llena y deforma, y la cuarta es el mismo fichero teñido con style.color. Fíjate también en que no se sale nada — todos los modos pintan dentro de su propio rect.

Las dos tablas de abajo son el caso más claro de que las dos capas son cosas de verdad distintas. La prop que escribes es un path dentro de tu proyecto; la prop que se publica es una referencia al envelope, reescrita por zabloo export cuando reunió los bytes. El mismo nombre, dos significados, y solo uno de ellos llega nunca a la máquina de un jugador.

Props de autoría

Lo que escribes en @zabloo/react.

PropTipoPor defectoDescripción
srcstringPath dentro del src/assets/ del proyecto — "logo.png", "icons/coin.png".
fit"contain" | "cover" | "stretch""contain"Cómo llena el original el rect del layout.

Además de estas, todo componente acepta las props base del nodo —id, visible, disabled, layout, style, states, transition, autofocus, clip— más variant, que el theme resuelve al exportar y que nunca aparece en la IR.

Props de IR

Lo que llega al juego, cuando ya no queda capa de autoría.

PropTipoPor defectoDescripción
srcAssetRef"asset:<id>" — una entrada del manifest del envelope. Nunca un binding.
fit"contain" | "cover" | "stretch""contain"Cómo llena el original el rect del layout.
src es estático a propósito

Una referencia a un asset se recoge al exportar, cuando los bytes se reúnen dentro del envelope — así que en tiempo de ejecución no hay nada a lo que un binding pueda apuntar. Un icono por fila elegido por los datos no se puede expresar en la IR v1; una vista que lo necesite autora sus huecos de forma estática, o el juego cambia la vista entera.

El fit

Todos los modos pintan dentro del rect. cover recorta el original a través de sus UVs en vez de desbordarse, así que el invariante que hace honesto el hit-testing sobre los rects del layout se cumple sin ninguna maquinaria de recorte.

ModoResultado
"contain"La imagen entera, sin deformar, centrada — con franjas.
"cover"Llena el rect, sin deformar, recortando por igual el eje que se sale.
"stretch"Llena el rect exactamente, deformando la proporción.

El pintado

No hay props de style específicas de Image. Todo es el conjunto de estilos de siempre, que es lo que hace que un icono sea tematizable y distinto por estado sin coste ninguno:

  • style.color tiñe la imagen, multiplicando canal a canal. Ausente = blanco = los píxeles tal cual. Es el mismo “color del contenido de este nodo” que colorea los glifos, así que states.*.style.color tiñe por estado sin nada nuevo.
  • style.radius redondea la imagen pintada, a juego con el fondo del propio nodo.
  • style.background y borderWidth son el placeholder. Una imagen no pinta nada hasta que sus bytes están decodificados, y el layout ya ha reservado el hueco a partir del width/height del manifest.

No hay estado loading: el placeholder se escribe, no es un estado de ejecución en el que entre el SDK.

Comportamiento

Estados

Solo disabled, heredado — un icono se apaga con el control al que pertenece. Dentro de un Button, los estados del propio botón pueden teñirlo: el estilo es del botón, la imagen es su contenido, así que un hover ilumina los dos sin que la imagen declare nada.

Focusable

No. Una imagen no acepta input y nada de lo que haga el jugador le llega, así que no se le aplica nada más allá de disabled.

Tamaño

Intrínseco, a partir del width/height del manifest: es una hoja con tamaño natural, como Text. Dale layout.width/height para dimensionarla tú, y fit para elegir cómo llena la imagen esa caja.

Actions

Ninguna, así que el juego no oye nada de un Image. Una imagen que reacciona a una pulsación es un Image dentro de un Button, y la pulsación es del botón.

Degradación

En un SDK más antiguo

En un SDK más antiguo la caja conserva su tamaño exacto y el fondo que le hayas puesto — la imagen no llega a aparecer dentro.

Como un Container vacío: es una hoja, así que lo que sobrevive es su caja, su fondo y su borde — el placeholder que escribiste, para siempre. El layout no se mueve nada, porque el hueco estaba reservado desde el manifest antes de decodificar ningún byte.

Composición

Un icono delante de una etiqueta es un Row; un botón que es solo un icono es un Button cuyo único hijo es un Image. El nodo no opina de ninguna de las dos cosas.

  • Tamaño intrínseco — cuando el arte se dibujó al tamaño al que debe verse, que es el caso normal de los iconos.
  • Dimensionada y recortada — usa fit="cover" con una caja explícita para banners y retratos, donde la forma del hueco importa más que la imagen entera.
  • Un original monocromo teñido — cuando la misma forma necesite varios colores: un fichero en el envelope, todos los colores que tenga el theme, y un cambio de estado no cuesta un segundo asset.
// Intrinsic size, straight from the manifest.
<Image src="icons/coin.png" />

// Sized and cropped by you.
<Image src="banners/shop.png" fit="cover" layout={{ width: 320, height: 120 }} style={{ radius: 8 }} />

// Tinted per state: the same "color of this node's content" that colors glyphs.
<Image
src="icons/star.png"
layout={{ width: 20, height: 20 }}
style={{ color: "{color.faint}" }}
states={{ hover: { style: { color: "{color.gold}" } } }}
/>

Relacionado