feat(ui): trying to fix flicker after transform

This commit is contained in:
psychedelicious 2024-07-30 21:10:45 +10:00
parent ea02323095
commit 1ddea87c35
4 changed files with 51 additions and 15 deletions

View File

@ -7,15 +7,16 @@ import { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasRect } from 'features/controlLayers/konva/CanvasRect';
import { getBrushLineId, getEraserLineId, getRectShapeId } from 'features/controlLayers/konva/naming';
import { konvaNodeToBlob, mapId, previewBlob } from 'features/controlLayers/konva/util';
import { layerRasterized } from 'features/controlLayers/store/canvasV2Slice';
import type {
BrushLine,
CanvasV2State,
Coordinate,
EraserLine,
LayerEntity,
Rect,
RectShape,
import { layerAllObjectsDeletedExceptOne, layerRasterized } from 'features/controlLayers/store/canvasV2Slice';
import {
type BrushLine,
type CanvasV2State,
type Coordinate,
type EraserLine,
imageDTOToImageObject,
type LayerEntity,
type Rect,
type RectShape,
} from 'features/controlLayers/store/types';
import Konva from 'konva';
import { debounce, get } from 'lodash-es';
@ -53,6 +54,8 @@ export class CanvasLayer {
_isFirstRender: boolean;
isTransforming: boolean;
isPendingBboxCalculation: boolean;
rasterizedObjectId: string | null;
rect: Rect;
bbox: Rect;
@ -188,6 +191,8 @@ export class CanvasLayer {
this._bboxNeedsUpdate = true;
this.isTransforming = false;
this._isFirstRender = true;
this.rasterizedObjectId = null;
this.isPendingBboxCalculation = false;
this._log = this.manager.getLogger(`layer_${this.id}`);
}
@ -326,6 +331,12 @@ export class CanvasLayer {
if (didUpdate) {
this.calculateBbox();
}
if (this.isTransforming && this.rasterizedObjectId) {
this.manager._store.dispatch(layerAllObjectsDeletedExceptOne({ id: this.id, objectId: this.rasterizedObjectId }));
this.isTransforming = false;
this.rasterizedObjectId = null;
}
}
async updateOpacity(arg?: { opacity: number }) {
@ -388,6 +399,10 @@ export class CanvasLayer {
async updateBbox() {
this._log.trace('Updating bbox');
if (this.isPendingBboxCalculation) {
return;
}
// If the bbox has no width or height, that means the layer is fully transparent. This can happen if it is only
// eraser lines, fully clipped brush lines or if it has been fully erased.
if (this.bbox.width === 0 || this.bbox.height === 0) {
@ -547,11 +562,16 @@ export class CanvasLayer {
}
const imageDTO = await uploadImage(blob, `${this.id}_transform.png`, 'other', true);
const { dispatch } = getStore();
dispatch(layerRasterized({ id: this.id, imageDTO, position: { x: rect.x, y: rect.y } }));
this.isTransforming = false;
const imageObject = imageDTOToImageObject(this.id, uuidv4(), imageDTO);
dispatch(layerRasterized({ id: this.id, imageObject, position: { x: rect.x, y: rect.y } }));
this.rasterizedObjectId = imageObject.id;
this.resetScale();
}
async finalizeTransform() {
//
}
async cancelTransform() {
this._log.debug('Canceling transform');
@ -572,9 +592,12 @@ export class CanvasLayer {
calculateBbox = debounce(() => {
this._log.debug('Calculating bbox');
this.isPendingBboxCalculation = true;
if (this.objects.size === 0) {
this.rect = this.getDefaultRect();
this.bbox = this.getDefaultRect();
this.isPendingBboxCalculation = false;
this.updateBbox();
return;
}
@ -607,6 +630,7 @@ export class CanvasLayer {
if (!needsPixelBbox) {
this.rect = deepClone(rect);
this.bbox = deepClone(rect);
this.isPendingBboxCalculation = false;
this._log.trace({ bbox: this.bbox, rect: this.rect }, 'Got bbox from client rect');
this.updateBbox();
return;
@ -636,6 +660,7 @@ export class CanvasLayer {
} else {
this.bbox = deepClone(rect);
}
this.isPendingBboxCalculation = false;
this._log.trace({ bbox: this.bbox, rect: this.rect, extents }, `Got bbox from worker`);
this.updateBbox();
clone.destroy();

View File

@ -329,7 +329,7 @@ export class CanvasManager {
for (const canvasLayer of this.layers.values()) {
if (!state.layers.entities.find((l) => l.id === canvasLayer.id)) {
this.log.debug(`Destroying deleted layer ${canvasLayer.id}`);
canvasLayer.destroy();
await canvasLayer.destroy();
this.layers.delete(canvasLayer.id);
}
}

View File

@ -220,6 +220,7 @@ export const {
layerTranslated,
layerBboxChanged,
layerImageAdded,
layerAllObjectsDeletedExceptOne,
layerAllDeleted,
layerImageCacheChanged,
layerScaled,

View File

@ -10,6 +10,7 @@ import type {
CanvasV2State,
Coordinate,
EraserLine,
ImageObject,
ImageObjectAddedArg,
LayerEntity,
PositionChangedArg,
@ -252,16 +253,25 @@ export const layersReducers = {
const { imageDTO } = action.payload;
state.layers.imageCache = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
},
layerRasterized: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO; position: Coordinate }>) => {
const { id, imageDTO, position } = action.payload;
layerRasterized: (state, action: PayloadAction<{ id: string; imageObject: ImageObject; position: Coordinate }>) => {
const { id, imageObject, position } = action.payload;
const layer = selectLayer(state, id);
if (!layer) {
return;
}
layer.objects = [imageDTOToImageObject(id, uuidv4(), imageDTO)];
layer.objects.push(imageObject);
layer.position = position;
state.layers.imageCache = null;
},
layerAllObjectsDeletedExceptOne: (state, action: PayloadAction<{ id: string; objectId: string }>) => {
const { id, objectId } = action.payload;
const layer = selectLayer(state, id);
if (!layer) {
return;
}
layer.objects = layer.objects.filter((obj) => obj.id === objectId);
state.layers.imageCache = null;
},
} satisfies SliceCaseReducers<CanvasV2State>;
const scalePoints = (points: number[], scaleX: number, scaleY: number) => {