From b75ec7bef3e46f5226a466fca509b6aa023dfd5e Mon Sep 17 00:00:00 2001 From: pixelass Date: Thu, 11 Apr 2024 02:39:17 +0200 Subject: [PATCH 1/3] feat: improve ui and add presets --- src/components/audio-analyzer.tsx | 23 ++- src/components/composite-area.tsx | 124 +++++++-------- src/components/drawing-area.tsx | 13 +- src/components/index.tsx | 94 ++++++++--- src/components/layout.tsx | 7 +- src/components/vj.tsx | 230 +++++++++++++++++++-------- src/components/waveform-area.tsx | 253 +++++++++++++----------------- src/constants.ts | 21 ++- src/pages/_app.tsx | 10 -- 9 files changed, 439 insertions(+), 336 deletions(-) diff --git a/src/components/audio-analyzer.tsx b/src/components/audio-analyzer.tsx index 1fd884d..1727d36 100644 --- a/src/components/audio-analyzer.tsx +++ b/src/components/audio-analyzer.tsx @@ -1,3 +1,4 @@ +import Box from "@mui/joy/Box"; import Option from "@mui/joy/Option"; import Select from "@mui/joy/Select"; import { useSetAtom } from "jotai"; @@ -28,13 +29,15 @@ function DeviceSelector({ onSelect }: { onSelect: (deviceId: string) => void }) } return ( - + + + ); } @@ -119,9 +122,5 @@ export function AudioAnalyzer() { setAudioDevice(deviceId); } - return ( - <> - - - ); + return ; } diff --git a/src/components/composite-area.tsx b/src/components/composite-area.tsx index 13ba515..b6417df 100644 --- a/src/components/composite-area.tsx +++ b/src/components/composite-area.tsx @@ -2,39 +2,31 @@ import { useSDK } from "@captn/react/use-sdk"; import { useAtom } from "jotai"; import { useEffect, useRef } from "react"; -import { drawingCanvasAtom, waveformCanvasAtom } from "../atoms"; - +import { drawingCanvasAtom, waveformCanvasAtom } from "@/atoms"; import { APP_ID } from "@/constants"; type CompositeParameters = { background?: string; + canvas?: OffscreenCanvas | null; }; export function CompositeArea({ background }: CompositeParameters) { const canvas = useRef(null); - const [drawingCanvass] = useAtom(drawingCanvasAtom); - const [waveformCanvass] = useAtom(waveformCanvasAtom); + const [drawingCanvas_] = useAtom(drawingCanvasAtom); + const [waveformCanvas_] = useAtom(waveformCanvasAtom); const { send } = useSDK(APP_ID, {}); useEffect(() => { - console.log("composite.area"); - const canvasElement = canvas.current; - - // - // function readerHandler() { - // const arrayBuffer: ArrayBuffer = reader.result as ArrayBuffer; - // const buffer = Buffer.from(arrayBuffer); - - // send({ action: "livePainting:imageBuffer", payload: buffer }); - // } - - // const reader = new FileReader(); + // Target for 60 FPS + const targetFrameInterval = 1000 / 60; + let lastRenderTime = Date.now(); + let animationFrameId: number; - // reader.addEventListener("load", readerHandler); + const canvasElement = canvas.current; - if (!canvasElement) { + if (!canvasElement || !waveformCanvas_ || !drawingCanvas_) { return; } @@ -43,6 +35,10 @@ export function CompositeArea({ background }: CompositeParameters) { canvasElement.width = 512 * dpr; const context = canvasElement.getContext("2d"); + if (!context) { + return; + } + const offscreenCanvas = document.createElement("canvas"); const scale = 0.125; offscreenCanvas.width = canvas.current.width * scale; @@ -50,70 +46,61 @@ export function CompositeArea({ background }: CompositeParameters) { const offscreenContext = offscreenCanvas.getContext("2d"); - if (offscreenContext) { - offscreenContext.scale(scale, scale); - } - - if (!context) { + if (!offscreenContext) { return; } + offscreenContext.scale(scale, scale); context.scale(dpr, dpr); - const targetFrameInterval = 1000 / 50; - let lastRenderTime = Date.now(); - - let animationFrameId: number; - function renderLoop() { const now = Date.now(); const elapsed = now - lastRenderTime; + if ( + !context || + !canvasElement || + !drawingCanvas_ || + !offscreenContext || + !waveformCanvas_ + ) { + return; + } + if (elapsed > targetFrameInterval) { lastRenderTime = now - (elapsed % targetFrameInterval); - if (!context || !canvasElement) { - return; - } - // Clear the canvas context.clearRect(0, 0, canvasElement.width, canvasElement.height); - if (drawingCanvass) { - context.drawImage(drawingCanvass, 0, 0); - } - - if (waveformCanvass) { - context.drawImage(waveformCanvass, 0, 0); - } - - if (offscreenContext) { - offscreenContext.fillStyle = "#000"; - offscreenContext.rect(0, 0, canvasElement.width, canvasElement.height); - offscreenContext.fill(); - - offscreenContext.drawImage(canvas.current, 0, 0); - - // Send the composite canvas data to the backend - offscreenCanvas.toBlob( - async blob => { - if (!blob) { - return; - } - - const arrayBuffer = await blob.arrayBuffer(); - const buffer = Buffer.from(arrayBuffer); - - send({ - action: "livePainting:imageBuffer", - payload: { appId: APP_ID, buffer }, - }); - // Reader.readAsArrayBuffer(blob); - }, - "image/jpeg", - 0.1 - ); - } + // Create layers + context.drawImage(waveformCanvas_, 0, 0); + context.drawImage(drawingCanvas_, 0, 0); + + offscreenContext.fillStyle = "#000000"; + offscreenContext.rect(0, 0, canvasElement.width, canvasElement.height); + offscreenContext.fill(); + + offscreenContext.drawImage(canvasElement, 0, 0); + + // Send the composite canvas data to the backend + offscreenCanvas.toBlob( + async blob => { + if (!blob) { + return; + } + + const arrayBuffer = await blob.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + + send({ + action: "livePainting:imageBuffer", + payload: { appId: APP_ID, buffer }, + }); + }, + "image/jpeg", + 0.1 + ); } animationFrameId = requestAnimationFrame(renderLoop); @@ -125,9 +112,8 @@ export function CompositeArea({ background }: CompositeParameters) { // Cleanup function to cancel the loop when the component unmounts or dependencies change return () => { cancelAnimationFrame(animationFrameId); - // Reader.removeEventListener("load", readerHandler); }; - }, [drawingCanvass, waveformCanvass, send]); + }, [drawingCanvas_, waveformCanvas_, send]); return ; } diff --git a/src/components/drawing-area.tsx b/src/components/drawing-area.tsx index 5a44311..41626af 100644 --- a/src/components/drawing-area.tsx +++ b/src/components/drawing-area.tsx @@ -2,16 +2,21 @@ import Box from "@mui/joy/Box"; import { useAtom } from "jotai"; import { type PointerEvent as ReactPointerEvent, useEffect, useRef } from "react"; -import { clearCounterAtom, drawingCanvasAtom, livePaintingOptionsAtom } from "../atoms"; - -export function DrawingArea({ isOverlay }: { isOverlay?: boolean }) { +import { drawingCanvasAtom, livePaintingOptionsAtom } from "../atoms"; + +export function DrawingArea({ + isOverlay, + clearCounter, +}: { + isOverlay?: boolean; + clearCounter: number; +}) { const canvas = useRef(null); const context = useRef(null); const [drawingCanvas, setDrawingCanvas] = useAtom(drawingCanvasAtom); const isDrawing = useRef(false); const [livePaintingOptions] = useAtom(livePaintingOptionsAtom); - const [clearCounter] = useAtom(clearCounterAtom); const canvasContainerReference = useRef(null); function startDrawing(event: ReactPointerEvent) { diff --git a/src/components/index.tsx b/src/components/index.tsx index 2bc2e96..c03870d 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -237,33 +237,77 @@ export function PromptSheet({ py: 2, }} > - - Style - option && {option.value}} + onChange={(_event, value_) => { + if (value_) { + onIllustrationStyleChange(value_); + } + }} + > + {Object.entries(illustrationStyles).map(([key_]) => ( + + ))} + + + + + + + + + Prompt