Skip to content

Commit

Permalink
Fix: race condition in editor size (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexamy authored Jul 11, 2024
1 parent f5a4de9 commit f4e9f3e
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 42 deletions.
32 changes: 19 additions & 13 deletions src/editor/DrawingBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,20 @@ const Canvas = styled("svg", {
},
});

export function DrawingBoard(props: { imageRef: HTMLImageElement }) {
export function DrawingBoard(props: {
image: HTMLImageElement;
background: HTMLImageElement;
}) {
const [store, api, setStore] = useAppStore().editor;
const region = useRegionContext();

const dimensions = createMemo(() => {
return {
width: props.image.naturalWidth,
height: props.image.naturalHeight,
};
});

function updatePoint(i: number, delta: { dx: number; dy: number }) {
setStore("points", i, (point) => ({
x: point.x + delta.dx / region.scale(),
Expand All @@ -27,7 +37,7 @@ export function DrawingBoard(props: { imageRef: HTMLImageElement }) {
}

return (
<DrawingCanvas imageRef={props.imageRef}>
<DrawingCanvas dimensions={dimensions()} image={props.background}>
<For each={api.quadPoints()}>{(quad) => <Quad points={quad} />}</For>
<Quad points={api.currentQuad()} />

Expand All @@ -49,30 +59,26 @@ export function DrawingBoard(props: { imageRef: HTMLImageElement }) {
// TODO move current point to DrawingCanvas

function DrawingCanvas(props: {
imageRef: HTMLImageElement;
children: JSX.Element;
dimensions: { width: number; height: number };
image: HTMLImageElement;
}) {
const region = useRegionContext();
const [_, { setCurrent, addPoint, deleteLastPoint }] = useAppStore().editor;
const [_2, { setCurrent, addPoint, deleteLastPoint }] = useAppStore().editor;

// style
const dimensions = createMemo(() => {
const rect = props.imageRef.getBoundingClientRect();
return { width: rect.width, height: rect.height };
});

const style = createMemo(() => ({
width: `${dimensions().width}px`,
height: `${dimensions().height}px`,
width: `${props.dimensions.width}px`,
height: `${props.dimensions.height}px`,
}));

const viewBox = createMemo(() =>
[0, 0, dimensions().width, dimensions().height].join(" ")
[0, 0, props.dimensions.width, props.dimensions.height].join(" ")
);

// handlers
function onMouseMove(e: MouseEvent) {
const rect = props.imageRef.getBoundingClientRect();
const rect = props.image?.getBoundingClientRect() ?? { left: 0, top: 0 };
const x = (e.clientX - rect.left) / region.scale();
const y = (e.clientY - rect.top) / region.scale();
setCurrent({ x, y });
Expand Down
30 changes: 8 additions & 22 deletions src/editor/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { styled } from "@macaron-css/solid";
import { createMemo, createSignal, Show } from "solid-js";
import { createSignal, Show } from "solid-js";
import { useAppStore } from "../store";
import { DrawingBoard } from "./DrawingBoard";

Expand All @@ -14,36 +14,22 @@ const Container = styled("div", {
});

export function Editor() {
const [store] = useAppStore().file;
const url = createMemo(() => URL.createObjectURL(store.blob));

// image reference is pointing at the same img element,
// but we must retrigger each time the image is loaded
const [imageRef, setImageRef] = createSignal<HTMLImageElement | undefined>(
undefined,
{ equals: false }
);
const [ref, setRef] = createSignal<HTMLImageElement>();
const [_, api] = useAppStore().file;

return (
<Container>
<Show when={store.blob.size > 0}>
<ImageBackground src={url()} onLoadRef={setImageRef} />
</Show>
<Show when={imageRef()}>
<DrawingBoard imageRef={imageRef()!} />
<Show when={api.data()}>
<ImageBackground src={api.data()!.url} setRef={setRef} />
<DrawingBoard image={api.data()!.image} background={ref()!} />
</Show>
</Container>
);
}

function ImageBackground(props: {
src: string;
onLoadRef: (ref: HTMLImageElement) => void;
setRef: (ref: HTMLImageElement) => void;
}) {
function onLoad(e: Event) {
const image = e.target as HTMLImageElement;
props.onLoadRef(image);
}

return <img src={props.src} alt="Uploaded image" onLoad={onLoad} />;
return <img ref={props.setRef} src={props.src} alt="Uploaded image" />;
}
4 changes: 2 additions & 2 deletions src/store/computed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function createComputedState(stores: {

const [isProjecting, startTransition] = useTransition();
const [data, { refetch: reproject }] = createResource(async () => {
const image = fileApi.image();
const image = fileApi.data()?.image;
const quads = editorApi.quadPoints();
if (!image) return defaultData;

Expand All @@ -59,7 +59,7 @@ export function createComputedState(stores: {

createEffect(
on(
() => [fileApi.image(), editorApi.quadPoints()] as const,
() => [fileApi.data(), editorApi.quadPoints()] as const,
debounce(() => startTransition(() => reproject()), 200)
)
);
Expand Down
6 changes: 3 additions & 3 deletions src/store/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ interface StoreData {
export function createFileStore() {
const [store, setStore] = createStore<StoreData>(getDefaultStore());

const [image] = createResource(
const [data] = createResource(
() => store.blob,
async (blob) => {
if (blob.size === 0) return;
const url = URL.createObjectURL(blob);
const image = await createImageSource(url);
return image;
return { url, image };
}
);

const api = { image };
const api = { data };

return [store, api, setStore] as const;
}
Expand Down
4 changes: 2 additions & 2 deletions src/toolbar/EditorToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export function EditorToolbar() {
const [_1, api] = useAppStore().file;
const [_2, { reset }] = useAppStore().editor;

const width = () => api.image()?.naturalWidth;
const height = () => api.image()?.naturalHeight;
const width = () => api.data()?.image.naturalWidth;
const height = () => api.data()?.image.naturalHeight;

// TODO remove
const move = useRegionContext();
Expand Down

0 comments on commit f4e9f3e

Please sign in to comment.