eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
DocsEcosistema
Image
Edit this page
6 min read
Un bitmap en un slot de tamaño explícito. El tamaño es un argumento del constructor y no una pista, porque el layout nunca puede inferir la extensión de una fuente que no ha decodificado, y un slot que espera a la red es una página que se reacomoda delante de quien lee.
1
Image("/images/hero.jpg", 640, 360, label: "The team at the 2026 offsite")
Escrito con la factory, como el resto del vocabulario. La misma llamada se realiza en la web y en Photon; nada en ella nombra un destino.
El slot
Parámetro
Tipo
Significado
source
string
Dónde está la ilustración. Una ruta que el destino pueda descargar.
width
float
El ancho del slot, en píxeles independientes de la densidad.
height
float
El alto del slot.
fit
ImageFit
Cómo la ilustración llena el slot. Cover por defecto.
label
string
Lo que dice un lector de pantalla. Vacío significa decorativo, que es la regla de HTML y se sostiene en los dos destinos.
Tres más se fijan como propiedades, porque son la excepción y no la forma:
Propiedad
Tipo
Significado
DarkSource
string?
La ilustración para el modo OSCURO, cuando una sola imagen no puede servir a los dos.
CornerRadius
CornerRadii
El radio por esquina; la ilustración se recorta a él.
Fit
ImageFit
También se puede fijar aquí, para un árbol construido sin la factory.
1
2
3
4
5
6
new Image("/brand/wordmark.svg", 180, 40)
{
DarkSource = "/brand/wordmark-white.svg",
CornerRadius = new CornerRadii(8),
Label = "", // decorativo: el nombre que está al lado ya lo dice
}
Fit
ImageFit
Qué hace
Cover
Llenado con recorte centrado. El valor por defecto, y lo que quiere una fotografía.
Contain
Con bandas dentro del slot, la fuente entera visible.
Stretch
Llenado con distorsión. Reservado para arte que lo tolera.
La ilustración oscura es un PAR, no un desvío
Un wordmark dibujado en tinta para superficies claras y en blanco para las oscuras no puede ser un solo archivo. Los colores resuelven esto con un par de ColorToken; las ilustraciones no pueden, así que el par vive en el nodo y el realizador lo resuelve del mismo modo: sin desvío en el código de la aplicación, y sin ninguna pregunta de modo en el momento de pintar.
En la web viajan las dos fuentes y el CSS muestra una, siguiendo la preferencia del sistema operativo y el modo forzado por la propia aplicación, que el controlador de tema estampa como data-theme.
Lo que dibuja cada destino
Web: un <img> con el ancho y el alto del slot, el object-fit que viene de Fit, el border-radius que viene de CornerRadius, el src de Source y el alt de Label.
Photon: el motor dibuja una caja de placeholder SurfaceSubtle bajo el mismo radio hasta que la subida de la textura aterriza: el patrón de degradación documentado, declarado aquí para que la forma de la página sea la misma en los dos destinos incluso mientras los píxeles no lo son.
Vallas de la v1, compartidas por los dos: el nine-slice, los estados de carga y de error, y el crossfade de decodificación se suman junto con los sistemas de assets y de animación, en lugar de llegar a este nodo.
Optimización de imágenes en el servidor
eQuantic.UI.Images añade un endpoint: un servicio independiente que redimensiona, recodifica y cachea imágenes a demanda.
Lee esto primero, porque la forma de la funcionalidad no es lo que parece. El endpoint no está conectado al nodo Image. Nada del vocabulario le pide una variante redimensionada, y el nodo no tiene propiedad de calidad, de srcset ni de opt-in, y la página que estás leyendo decía lo contrario, describiendo un elemento solo de web que precedía al vocabulario. Lo que se entrega es el servicio; apuntar algo hacia él es hoy la jugada de quien llama.
Habilitarlo
1
2
3
4
5
6
7
8
9
10
11
12
13
builder.Services.AddUI(options =>
{
options.UseImageOptimization(opts =>
{
opts.DefaultQuality = 80; // 1-100, por defecto 75
opts.Formats = ["image/webp"]; // formatos de salida preferidos
opts.CacheTtlSeconds = 14400; // por defecto 4 horas
opts.MaxSourceSize = 10 * 1024 * 1024; // por defecto 10 MB
});
});
app.UseStaticFiles();
app.MapUI(); // mapea /_equantic/image
El contrato
1
GET /_equantic/image?url=<path>&w=<width>&q=<quality>
Parámetro
Regla
url
Obligatorio. Tiene que empezar por / y no puede contener .., así que no puede salir del content root.
w
Obligatorio. Tiene que ser uno de AllowedWidths, o la respuesta es 400 nombrando los valores permitidos.
q
Opcional, de 1 a 100. Cae a DefaultQuality.
El formato de la respuesta se negocia desde la cabecera Accept de la petición contra Formats, con una alternativa que el navegador siempre puede leer. Un fallo de caché redimensiona, convierte y escribe el resultado en CacheDirectory; un acierto sirve los bytes cacheados con Cache-Control: public, max-age=<CacheTtlSeconds>. Una fuente más grande que MaxSourceSize se rechaza en lugar de decodificarse, quien sea quien la entregue: el optimizador y el generador de placeholders dejan de leer en el presupuesto, así que un stream que abre quien llama queda sujeto a él igual que un archivo bajo wwwroot.
Qué lee, y qué vuelve
Una fuente se lee cuando sus bytes son un JPEG, un PNG, un GIF, un WebP o un BMP, diga lo que diga su nombre. Un .tiff se rechaza antes de abrirse, y un archivo que el optimizador no puede leer, o uno cuya cabecera pide más de 16.383 × 16.383 píxeles, se responde con 400 sin decodificar un solo píxel. El optimizador corre sobre SkiaSharp, y sus nativos para Linux, macOS y Windows vienen con el paquete, así que no hay nada que instalar ni clave de licencia que fijar.
•
Orientación. La orientación que grabó una cámara se aplica a los píxeles, así que w es el ancho tal como se muestra, y ImageOptimizer.GetDimensionsAsync responde el tamaño tal como se muestra.
•
Animación. Un GIF o un WebP animado se sirve tal cual, con su propio content type, igual que sirve uno el optimizador de Next.js: ningún codificador de aquí escribe fotogramas.
•
La respuesta. Es uno de los formatos que escribe un codificador de aquí, así que Formats acepta image/webp, image/png e image/jpeg, y cualquier otro, image/avif incluido, detiene la aplicación al arrancar. Los píxeles son sRGB y no llevan perfil de color, que un navegador lee como sRGB.
Opciones
Opción
Por defecto
Significado
DefaultQuality
75
La calidad cuando la petición no lo dice.
DeviceSizes
640, 750, 828, 1080, 1200, 1920, 2048, 3840
Anchos para imágenes a todo lo ancho.
ImageSizes
32, 48, 64, 96, 128, 256, 384
Anchos para imágenes más pequeñas que el viewport.
AllowedWidths
derivado
DeviceSizes e ImageSizes, deduplicados y ordenados. La lista de validación.
Formats
image/webp
Formatos de salida, el mejor primero: image/webp, image/png, image/jpeg.
CacheTtlSeconds
14400
Lo que la respuesta le dice al navegador.
CacheDirectory
obj/eQuantic/image-cache
Donde viven los resultados codificados.
MaxSourceSize
10 MB
Por encima de esto una fuente se rechaza, la lea el endpoint o la entregue quien llama.
Placeholders con desenfoque
BlurPlaceholderGenerator convierte un stream o un archivo en una data URL diminuta en base64. Resuelto desde los servicios que registra UseImageOptimization, o inyectado, no lee ninguna fuente más allá del MaxSourceSize de la aplicación:
1
2
var placeholders = app.Services.GetRequiredService<BlurPlaceholderGenerator>();
var placeholder = await placeholders.GenerateFromFileAsync("wwwroot/images/hero.jpg");
Es un ayudante independiente por la misma razón que lo es el endpoint: el vocabulario no tiene ninguna propiedad de placeholder a la que entregarle el resultado.
Ver también
•
Componentes: el resto del vocabulario
•
Design System: los tokens, incluido el par de ColorToken que DarkSource refleja
•
Estilos: el HtmlElement y la vía de escape solo de web