Images and backgrounds
The app decides what an image is
dioxus-compose never fetches anything. An <img src> or a CSS
url() is recorded as written, and an image resolver you supply says how big it
is and which asset draws it. Gradients need no resolver, because they are computed from the
CSS alone.
The image resolver
An app may already have its images in memory, may want to fetch them itself, or may want to refuse some. So loading is the app's decision, made through one trait:
pub trait ImageResolver {
/// The asset that draws the image at `url`, or None to draw nothing.
/// Asked while a plan is built, never during layout.
fn resolve(&mut self, url: &str) -> Option<AssetId>;
/// The natural size, when the app knows it without fetching anything.
/// Asked during layout. None, the default, leaves the image without one.
fn size(&mut self, _url: &str) -> Option<ImageSize> {
None
}
}
AssetId is your own number for an image you registered with the renderer; it is
handed back in the plan and never looked into. ImageSize is in CSS pixels, so an
image made for a screen of two device pixels per CSS pixel is half its pixel size here.
The resolver goes in HtmlConfig::images, or later through
HtmlDom::set_image_resolver. Without one, an <img> takes only
the size its attributes and CSS give it, and no image draws anything.
impl ImageResolver for Images {
fn resolve(&mut self, url: &str) -> Option<AssetId> {
self.find(url).map(|(_, _, asset)| *asset)
}
fn size(&mut self, url: &str) -> Option<ImageSize> {
self.find(url).map(|(_, size, _)| *size)
}
}
URLs
HtmlConfig::base_url is what relative src attributes and CSS
url() values resolve against. Without one, or with one that nothing can be
resolved against, such as a data: URL, a relative URL reaches your resolver
exactly as it was written: photos/tram.jpg stays photos/tram.jpg.
The img element
An <img> with no size of its own is laid out at the size the resolver gave.
object-fit decides where inside its box the picture is drawn. In
samples/gallery the 48 by 48 avatar keeps its natural size, the photos
cover their tiles, and the route map is shown whole with contain.
.avatar { border-radius: 50%; }
.tile {
margin: 0;
border-radius: 8px;
overflow: hidden;
}
.tile img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.tile img.whole { object-fit: contain; }
Background images
background-image layers keep their CSS order: the first is drawn on top, and all
of them over the background colour. background-size (cover,
contain or lengths), background-position and
background-repeat are resolved into rectangles, so the renderer draws an image
into a box rather than interpreting CSS.
.postcard {
background-image: url(textures/postcard.png);
background-size: cover;
background-position: center;
}
Gradients
linear-gradient(), radial-gradient() and their repeating forms are
resolved against the tile they fill. Keywords such as to right become the angle
they mean for that tile's size, and every stop gets its offset and colour. The gallery header
is one gradient:
.hero {
display: flex;
align-items: center;
gap: 16px;
height: 160px;
padding: 0 32px;
background-image: linear-gradient(to right, #4f46e5, #06b6d4);
color: #ffffff;
}
Changing one stop's colour is one change in the plan, the same as changing a background colour.
Not yet
- Images on screen. partial The plan names the asset for every image and background, and nothing draws it until the renderer bridge lands (#43).