add loading animation to Image

This commit is contained in:
Aran-Fey
2025-09-09 20:05:54 +02:00
parent 8d3180ff4e
commit faf194b1b9
2 changed files with 37 additions and 12 deletions

View File

@@ -20,7 +20,6 @@ export type ImageState = ComponentState & {
export class ImageComponent extends ComponentBase<ImageState> {
private imageElement: HTMLImageElement;
private isLoading: boolean = false;
private resizeObserver: ResizeObserver;
createElement(context: ComponentStatesUpdateContext): HTMLElement {
@@ -57,15 +56,7 @@ export class ImageComponent extends ComponentBase<ImageState> {
deltaState.imageUrl !== undefined &&
this.imageElement.src !== deltaState.imageUrl
) {
// Until the image is loaded and we get access to its resolution,
// let it fill the entire space. This is the correct size for all
// `fill_mode`s except `"fit"` anyway, so there's no harm in setting
// it now rather than later. (SVGs might temporarily render content
// outside of the viewbox, but the only way to prevent that would be
// to make the image invisible until loaded.)
this.isLoading = true;
this.imageElement.style.width = "100%";
this.imageElement.style.height = "100%";
this.element.classList.add("rio-loading");
this.imageElement.src = deltaState.imageUrl;
@@ -96,12 +87,12 @@ export class ImageComponent extends ComponentBase<ImageState> {
}
private _onLoad(): void {
this.isLoading = false;
this.element.classList.remove("rio-loading");
this._updateSize();
}
private _updateSize(): void {
if (this.isLoading) {
if (this.element.classList.contains("rio-loading")) {
// While loading a new image, the size is set to 100%. Don't
// overwrite it.
return;

View File

@@ -1,5 +1,26 @@
@import "../utils";
// A loading animation inspired by the "skeleton" effect
@mixin loading-animation-shimmer {
background: linear-gradient(
90deg,
var(--rio-local-bg) 25%,
var(--rio-local-bg-active) 45%,
var(--rio-local-bg-variant) 60%,
var(--rio-local-bg) 75%
);
background-size: 400%;
animation: shimmer 1.5s infinite linear;
}
@keyframes shimmer {
0% {
background-position-x: 100%;
}
100% {
background-position-x: 0%;
}
}
.rio-image {
pointer-events: none;
@@ -10,6 +31,19 @@
@include kill-size-request-with-absolute();
}
// Until the image is loaded and we get access to its resolution, let it
// fill the entire space. This is the correct size for all `fill_mode`s
// except `"fit"` anyway, so there's no harm in setting it now rather than
// later. (SVGs might temporarily render content outside of the viewbox, but
// the only way to prevent that would be to make the image invisible until
// loaded.)
&.rio-loading img {
width: 100%;
height: 100%;
@include loading-animation-shimmer();
}
// Error icon
svg {
pointer-events: auto;