feat(ui): image loading fallback for raster layers

This commit is contained in:
psychedelicious 2024-06-06 17:14:42 +10:00
parent 51de25122a
commit 40cab08133
3 changed files with 62 additions and 25 deletions

View File

@ -115,6 +115,7 @@
"githubLabel": "Github",
"goTo": "Go to",
"hotkeysLabel": "Hotkeys",
"loadingImage": "Loading Image",
"imageFailedToLoad": "Unable to Load Image",
"img2img": "Image To Image",
"inpaint": "inpaint",

View File

@ -2,6 +2,7 @@ import { rgbaColorToString } from 'features/canvas/util/colorToString';
import { getObjectGroupId } from 'features/controlLayers/konva/naming';
import type { BrushLine, EraserLine, ImageObject, RectShape } from 'features/controlLayers/store/types';
import { DEFAULT_RGBA_COLOR } from 'features/controlLayers/store/types';
import { t } from 'i18next';
import Konva from 'konva';
import { getImageDTO } from 'services/api/endpoints/images';
import { v4 as uuidv4 } from 'uuid';
@ -81,16 +82,58 @@ export const createRectShape = (rectShape: RectShape, layerObjectGroup: Konva.Gr
return konvaRect;
};
export const createImageObject = async (
const createImagePlaceholderGroup = (
imageObject: ImageObject
): { konvaPlaceholderGroup: Konva.Group; onError: () => void; onLoading: () => void; onLoaded: () => void } => {
const { width, height } = imageObject.image;
const konvaPlaceholderGroup = new Konva.Group({ name: 'image-placeholder', listening: false });
const konvaPlaceholderRect = new Konva.Rect({
fill: 'hsl(220 12% 45% / 1)', // 'base.500'
width,
height,
});
const konvaPlaceholderText = new Konva.Text({
name: 'image-placeholder-text',
fill: 'hsl(220 12% 10% / 1)', // 'base.900'
width,
height,
align: 'center',
verticalAlign: 'middle',
fontFamily: '"Inter Variable", sans-serif',
fontSize: width / 16,
fontStyle: '600',
text: 'Loading Image',
listening: false,
});
konvaPlaceholderGroup.add(konvaPlaceholderRect);
konvaPlaceholderGroup.add(konvaPlaceholderText);
const onError = () => {
konvaPlaceholderText.text(t('common.imageFailedToLoad', 'Image Failed to Load'));
};
const onLoading = () => {
konvaPlaceholderText.text(t('common.loadingImage', 'Loading Image'));
};
const onLoaded = () => {
konvaPlaceholderGroup.destroy();
};
return { konvaPlaceholderGroup, onError, onLoading, onLoaded };
};
export const createImageObjectGroup = async (
imageObject: ImageObject,
layerObjectGroup: Konva.Group,
name: string
): Promise<Konva.Image | null> => {
const imageDTO = await getImageDTO(imageObject.image.name);
if (!imageDTO) {
return null;
}
return new Promise((resolve) => {
): Promise<Konva.Group> => {
const konvaImageGroup = new Konva.Group({ id: imageObject.id, name, listening: false });
const placeholder = createImagePlaceholderGroup(imageObject);
konvaImageGroup.add(placeholder.konvaPlaceholderGroup);
layerObjectGroup.add(konvaImageGroup);
getImageDTO(imageObject.image.name).then((imageDTO) => {
if (!imageDTO) {
placeholder.onError();
return;
}
const imageEl = new Image();
imageEl.onload = () => {
const konvaImage = new Konva.Image({
@ -99,15 +142,16 @@ export const createImageObject = async (
listening: false,
image: imageEl,
});
layerObjectGroup.add(konvaImage);
resolve(konvaImage);
placeholder.onLoaded();
konvaImageGroup.add(konvaImage);
};
imageEl.onerror = () => {
resolve(null);
placeholder.onError();
};
imageEl.id = imageObject.id;
imageEl.src = imageDTO.image_url;
});
return konvaImageGroup;
};
/**
* Creates a konva group for a layer's objects.

View File

@ -9,14 +9,13 @@ import {
import {
createBrushLine,
createEraserLine,
createImageObject,
createImageObjectGroup,
createObjectGroup,
createRectShape,
} from 'features/controlLayers/konva/renderers/objects';
import { getScaledFlooredCursorPosition, mapId, selectRasterObjects } from 'features/controlLayers/konva/util';
import type { RasterLayer, Tool } from 'features/controlLayers/store/types';
import Konva from 'konva';
import { assert } from 'tsafe';
/**
* Logic for creating and rendering raster layers.
@ -109,10 +108,7 @@ export const renderRasterLayer = async (
}
}
for (let i = 0; i < layerState.objects.length; i++) {
const obj = layerState.objects[i];
assert(obj);
const zIndex = layerState.objects.length - i;
for (const obj of layerState.objects) {
if (obj.type === 'brush_line') {
const konvaBrushLine =
konvaObjectGroup.findOne<Konva.Line>(`#${obj.id}`) ??
@ -121,7 +117,6 @@ export const renderRasterLayer = async (
if (konvaBrushLine.points().length !== obj.points.length) {
konvaBrushLine.points(obj.points);
}
konvaBrushLine.zIndex(zIndex);
} else if (obj.type === 'eraser_line') {
const konvaEraserLine =
konvaObjectGroup.findOne<Konva.Line>(`#${obj.id}`) ??
@ -130,17 +125,14 @@ export const renderRasterLayer = async (
if (konvaEraserLine.points().length !== obj.points.length) {
konvaEraserLine.points(obj.points);
}
konvaEraserLine.zIndex(zIndex);
} else if (obj.type === 'rect_shape') {
const konvaRect =
konvaObjectGroup.findOne<Konva.Rect>(`#${obj.id}`) ??
if (!konvaObjectGroup.findOne<Konva.Rect>(`#${obj.id}`)) {
createRectShape(obj, konvaObjectGroup, RASTER_LAYER_RECT_SHAPE_NAME);
konvaRect.zIndex(zIndex);
}
} else if (obj.type === 'image') {
const konvaImage =
konvaObjectGroup.findOne<Konva.Image>(`#${obj.id}`) ??
(await createImageObject(obj, konvaObjectGroup, RASTER_LAYER_IMAGE_NAME));
konvaImage?.zIndex(zIndex);
if (!konvaObjectGroup.findOne<Konva.Group>(`#${obj.id}`)) {
createImageObjectGroup(obj, konvaObjectGroup, RASTER_LAYER_IMAGE_NAME);
}
}
}