From 3ede04c31b0e455f375538872990ba8824ac2ed8 Mon Sep 17 00:00:00 2001 From: Ivan Li Date: Tue, 21 Mar 2023 23:42:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(gui):=20=E5=A2=9E=E5=BC=BA=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=B1=8F=E9=A2=84=E8=A7=88=E6=95=88=E6=9E=9C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 +- src/components/display-list-container.tsx | 61 +++++++++--- src/components/display-view.tsx | 17 +++- src/components/screen-view.tsx | 113 ++++++++++++++++++---- 4 files changed, 156 insertions(+), 37 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index bb2b6b4..719616a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,7 +15,7 @@ function App() { }); return ( -
+
{displayStore.displays.map((display) => { return ; diff --git a/src/components/display-list-container.tsx b/src/components/display-list-container.tsx index f697e46..517db2b 100644 --- a/src/components/display-list-container.tsx +++ b/src/components/display-list-container.tsx @@ -1,15 +1,45 @@ -import { createEffect, createMemo, createSignal, on, ParentComponent } from 'solid-js'; +import { + createEffect, + createSignal, + onCleanup, + onMount, + ParentComponent, +} from 'solid-js'; import { displayStore, setDisplayStore } from '../stores/display.store'; export const DisplayListContainer: ParentComponent = (props) => { + let root: HTMLElement; const [olStyle, setOlStyle] = createSignal({ top: '0px', left: '0px', }); const [rootStyle, setRootStyle] = createSignal({ - width: '100%', + // width: '100%', height: '100%', }); + const [bound, setBound] = createSignal({ + left: 0, + top: 0, + right: 100, + bottom: 100, + }); + + const resetSize = () => { + const _bound = bound(); + + setDisplayStore({ + viewScale: root.clientWidth / (_bound.right - _bound.left), + }); + + setOlStyle({ + top: `${-_bound.top * displayStore.viewScale}px`, + left: `${-_bound.left * displayStore.viewScale}px`, + }); + + setRootStyle({ + height: `${(_bound.bottom - _bound.top) * displayStore.viewScale}px`, + }); + }; createEffect(() => { const boundLeft = Math.min(0, ...displayStore.displays.map((display) => display.x)); @@ -23,22 +53,27 @@ export const DisplayListContainer: ParentComponent = (props) => { ...displayStore.displays.map((display) => display.y + display.height), ); - setDisplayStore({ - viewScale: 1200 / (boundRight - boundLeft), + setBound({ + left: boundLeft, + top: boundTop, + right: boundRight, + bottom: boundBottom, + }); + let observer: ResizeObserver; + onMount(() => { + observer = new ResizeObserver(resetSize); + observer.observe(root); }); - setOlStyle({ - top: `${-boundTop * displayStore.viewScale}px`, - left: `${-boundLeft * displayStore.viewScale}px`, - }); - - setRootStyle({ - width: `${(boundRight - boundLeft) * displayStore.viewScale}px`, - height: `${(boundBottom - boundTop) * displayStore.viewScale}px`, + onCleanup(() => { + observer?.unobserve(root); }); }); + + createEffect(() => {}); + return ( -
+
    {props.children}
diff --git a/src/components/display-view.tsx b/src/components/display-view.tsx index 088ae81..48ef2ac 100644 --- a/src/components/display-view.tsx +++ b/src/components/display-view.tsx @@ -16,18 +16,29 @@ export const DisplayView: Component = (props) => { const style = createMemo(() => ({ top: `${props.display.y * displayStore.viewScale}px`, left: `${props.display.x * displayStore.viewScale}px`, + height: `${size().height}px`, + width: `${size().width}px`, })); return ( -
+
+
Test
+
Test
+
Test
+
Test
); }; diff --git a/src/components/screen-view.tsx b/src/components/screen-view.tsx index f3a2df3..0216171 100644 --- a/src/components/screen-view.tsx +++ b/src/components/screen-view.tsx @@ -13,9 +13,7 @@ import { type ScreenViewProps = { displayId: number; - height: number; - width: number; -} & Omit, 'height' | 'width'>; +} & JSX.HTMLAttributes; async function subscribeScreenshotUpdate(displayId: number) { await invoke('subscribe_encoded_screenshot_updated', { @@ -26,7 +24,63 @@ async function subscribeScreenshotUpdate(displayId: number) { export const ScreenView: Component = (props) => { const [localProps, rootProps] = splitProps(props, ['displayId']); let canvas: HTMLCanvasElement; + let root: HTMLDivElement; const [ctx, setCtx] = createSignal(null); + const [drawInfo, setDrawInfo] = createSignal({ + drawX: 0, + drawY: 0, + drawWidth: 0, + drawHeight: 0, + }); + const [imageData, setImageData] = createSignal<{ + buffer: Uint8ClampedArray; + width: number; + height: number; + } | null>(null); + + const resetSize = () => { + const aspectRatio = canvas.width / canvas.height; + + const drawWidth = Math.round( + Math.min(root.clientWidth, root.clientHeight * aspectRatio), + ); + const drawHeight = Math.round( + Math.min(root.clientHeight, root.clientWidth / aspectRatio), + ); + + const drawX = Math.round((root.clientWidth - drawWidth) / 2); + const drawY = Math.round((root.clientHeight - drawHeight) / 2); + + setDrawInfo({ + drawX, + drawY, + drawWidth, + drawHeight, + }); + + canvas.width = root.clientWidth; + canvas.height = root.clientHeight; + + draw(true); + }; + + const draw = (cached: boolean = false) => { + const { drawX, drawY, drawWidth, drawHeight } = drawInfo(); + + let _ctx = ctx(); + let raw = imageData(); + if (_ctx && raw) { + _ctx.clearRect(0, 0, canvas.width, canvas.height); + if (cached) { + for (let i = 3; i < raw.buffer.length; i += 8) { + raw.buffer[i] = Math.floor(raw.buffer[i] * 0.7); + } + } + const img = new ImageData(raw.buffer, raw.width, raw.height); + _ctx.putImageData(img, drawX, drawY); + } + }; + createEffect(() => { const unlisten = listen<{ base64_image: string; @@ -34,9 +88,10 @@ export const ScreenView: Component = (props) => { height: number; width: number; }>('encoded-screenshot-updated', (event) => { + const { drawWidth, drawHeight } = drawInfo(); if (event.payload.display_id === localProps.displayId) { const url = convertFileSrc( - `displays/${localProps.displayId}?width=${canvas.width}&height=${canvas.height}`, + `displays/${localProps.displayId}?width=${drawWidth}&height=${drawHeight}`, 'ambient-light', ); fetch(url, { @@ -44,18 +99,17 @@ export const ScreenView: Component = (props) => { }) .then((res) => res.body?.getReader().read()) .then((buffer) => { - console.log(buffer?.value?.length); - - let _ctx = ctx(); - if (_ctx && buffer?.value) { - _ctx.clearRect(0, 0, canvas.width, canvas.height); - const img = new ImageData( - new Uint8ClampedArray(buffer.value), - canvas.width, - canvas.height, - ); - _ctx.putImageData(img, 0, 0); + // console.log(buffer?.value?.length); + if (buffer?.value) { + setImageData({ + buffer: new Uint8ClampedArray(buffer?.value), + width: drawWidth, + height: drawHeight, + }); + } else { + setImageData(null); } + draw(); }); } @@ -63,10 +117,6 @@ export const ScreenView: Component = (props) => { }); subscribeScreenshotUpdate(localProps.displayId); - onMount(() => { - setCtx(canvas.getContext('2d')); - }); - onCleanup(() => { unlisten.then((unlisten) => { unlisten(); @@ -74,5 +124,28 @@ export const ScreenView: Component = (props) => { }); }); - return ; + createEffect(() => { + let resizeObserver: ResizeObserver; + + onMount(() => { + setCtx(canvas.getContext('2d')); + new ResizeObserver(() => { + resetSize(); + }).observe(root); + }); + + onCleanup(() => { + resizeObserver?.unobserve(root); + }); + }); + + return ( +
+ +
+ ); };