eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
DocsÉcosystème
Image
Edit this page
6 min read
Un bitmap dans un emplacement explicitement dimensionné. La taille est un argument du constructeur et non une indication, parce que le layout ne peut jamais inférer l'étendue d'une source qu'il n'a pas décodée, et un emplacement qui attend le réseau est une page qui se réagence sous les yeux de la lectrice.
1
Image("/images/hero.jpg", 640, 360, label: "The team at the 2026 offsite")
Écrit avec la factory, comme le reste du vocabulaire. Le même appel se réalise sur le web et sur Photon ; rien dedans ne nomme une cible.
L'emplacement
Paramètre
Type
Sens
source
string
Où se trouve l'illustration. Un chemin que la cible peut récupérer.
width
float
La largeur de l'emplacement, en pixels indépendants de la densité.
height
float
La hauteur de l'emplacement.
fit
ImageFit
Comment l'illustration remplit l'emplacement. Cover par défaut.
label
string
Ce qu'un lecteur d'écran dit. Vide signifie décoratif, ce qui est la règle HTML et tient sur les deux cibles.
Trois autres se posent en propriétés, parce qu'elles sont l'exception plutôt que la forme :
Propriété
Type
Sens
DarkSource
string?
L'illustration pour le mode SOMBRE, quand une seule image ne peut pas servir les deux.
CornerRadius
CornerRadii
Le rayon par coin ; l'illustration y est rognée.
Fit
ImageFit
Posable ici aussi, pour un arbre construit sans 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 = "", // décoratif : le nom à côté le dit déjà
}
Fit
ImageFit
Ce qu'il fait
Cover
Remplissage avec rognage centré. Le défaut, et ce que veut une photographie.
Contain
En boîte à lettres dans l'emplacement, la source entière visible.
Stretch
Remplissage déformant. Réservé à l'art qui le tolère.
L'illustration sombre est une PAIRE, pas un branchement
Un logotype dessiné à l'encre pour les surfaces claires et en blanc pour les sombres ne peut pas être un seul fichier. Les couleurs résolvent cela avec une paire de ColorToken ; les illustrations ne peuvent pas, donc la paire vit sur le nœud et le réalisateur la résout de la même façon : aucun branchement dans le code de l'application, et aucune question de mode posée au moment de peindre.
Sur le web les deux sources partent et le CSS en montre une, en suivant la préférence du système d'exploitation et le mode forcé par l'application elle-même, que le contrôleur de thème estampille en data-theme.
Ce que chaque cible dessine
Web : un <img> avec la largeur et la hauteur de l'emplacement, l'object-fit venu de Fit, le border-radius venu de CornerRadius, le src de Source et l'alt de Label.
Photon : le moteur dessine une boîte de placeholder SurfaceSubtle sous le même rayon jusqu'à ce que le chargement de la texture arrive : le motif de dégradation documenté, énoncé ici pour que la forme de la page soit la même sur les deux cibles même pendant que les pixels ne le sont pas.
Limites de la v1, partagées par les deux : le nine-slice, les états de chargement et d'erreur, et le fondu de décodage arrivent avec les systèmes d'assets et d'animation plutôt que sur ce nœud.
Optimisation d'images côté serveur
eQuantic.UI.Images ajoute un endpoint : un service autonome qui redimensionne, réencode et met en cache des images à la demande.
Lisez ceci d'abord, parce que la forme de la fonctionnalité n'est pas celle qu'elle paraît. L'endpoint n'est pas câblé au nœud Image. Rien dans le vocabulaire ne lui demande une variante redimensionnée, et le nœud n'a ni propriété de qualité, ni de srcset, ni d'opt-in, et la page que vous lisez disait le contraire, en décrivant un élément uniquement web qui précédait le vocabulaire. Ce qui est livré, c'est le service ; pointer quelque chose vers lui est aujourd'hui l'affaire de l'appelant.
L'activer
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, 75 par défaut
opts.Formats = ["image/webp"]; // formats de sortie préférés
opts.CacheTtlSeconds = 14400; // 4 heures par défaut
opts.MaxSourceSize = 10 * 1024 * 1024; // 10 Mo par défaut
});
});
app.UseStaticFiles();
app.MapUI(); // mappe /_equantic/image
Le contrat
1
GET /_equantic/image?url=<path>&w=<width>&q=<quality>
Paramètre
Règle
url
Obligatoire. Doit commencer par / et ne doit pas contenir .., il ne peut donc pas quitter le content root.
w
Obligatoire. Doit être l'une des AllowedWidths, sinon la réponse est 400 en nommant les valeurs permises.
q
Facultatif, de 1 à 100. Retombe sur DefaultQuality.
Le format de la réponse est négocié depuis l'en-tête Accept de la requête face à Formats, avec un repli que le navigateur peut toujours lire. Un défaut de cache redimensionne, convertit et écrit le résultat dans CacheDirectory ; un succès sert les octets en cache avec Cache-Control: public, max-age=<CacheTtlSeconds>. Une source plus grande que MaxSourceSize est refusée plutôt que décodée, qui que ce soit qui la remette : l'optimiseur et le générateur de placeholders cessent de lire au budget, donc un flux qu'un appelant ouvre y est tenu comme un fichier sous wwwroot.
Ce qu'il lit, et ce qui revient
Une source est lue quand ses octets sont du JPEG, du PNG, du GIF, du WebP ou du BMP, quoi que dise son nom. Un .tiff est refusé avant d'être ouvert, et un fichier que l'optimiseur ne peut pas lire, ou dont l'en-tête demande plus de 16 383 × 16 383 pixels, reçoit un 400 sans qu'un pixel soit décodé. L'optimiseur tourne sur SkiaSharp, et ses natifs pour Linux, macOS et Windows viennent avec le paquet, il n'y a donc rien à installer et aucune clé de licence à poser.
•
Orientation. L'orientation qu'un appareil a enregistrée est appliquée aux pixels, donc w est la largeur telle qu'affichée, et ImageOptimizer.GetDimensionsAsync répond la taille telle qu'affichée.
•
Animation. Un GIF ou un WebP animé est servi tel quel, avec son propre content type, comme l'optimiseur de Next.js en sert un : aucun encodeur ici n'écrit d'images successives.
•
La réponse. C'est l'un des formats qu'un encodeur ici écrit, donc Formats accepte image/webp, image/png et image/jpeg, et tout autre, image/avif compris, arrête l'application au démarrage. Les pixels sont en sRGB et ne portent aucun profil de couleur, ce qu'un navigateur lit comme du sRGB.
Options
Option
Défaut
Sens
DefaultQuality
75
La qualité quand la requête ne la dit pas.
DeviceSizes
640, 750, 828, 1080, 1200, 1920, 2048, 3840
Largeurs pour les images pleine largeur.
ImageSizes
32, 48, 64, 96, 128, 256, 384
Largeurs pour les images plus petites que le viewport.
AllowedWidths
dérivé
DeviceSizes et ImageSizes, dédupliquées et triées. La liste de validation.
Formats
image/webp
Formats de sortie, le meilleur d'abord : image/webp, image/png, image/jpeg.
CacheTtlSeconds
14400
Ce que la réponse dit au navigateur.
CacheDirectory
obj/eQuantic/image-cache
Où vivent les résultats encodés.
MaxSourceSize
10 Mo
Au-delà, une source est refusée, qu'elle soit lue par l'endpoint ou remise par un appelant.
Placeholders flous
BlurPlaceholderGenerator transforme un flux ou un fichier en une minuscule data URL en base64. Résolu depuis les services qu'UseImageOptimization enregistre, ou injecté, il ne lit aucune source au-delà du MaxSourceSize de l'application :
1
2
var placeholders = app.Services.GetRequiredService<BlurPlaceholderGenerator>();
var placeholder = await placeholders.GenerateFromFileAsync("wwwroot/images/hero.jpg");
C'est une aide autonome pour la même raison que l'endpoint l'est : le vocabulaire n'a aucune propriété de placeholder à qui remettre le résultat.
Voir aussi
•
Composants : le reste du vocabulaire
•
Design System : les tokens, dont la paire de ColorToken que DarkSource reflète
•
Styles : l'HtmlElement et l'échappatoire uniquement web