diff --git a/package-lock.json b/package-lock.json index e5d7a46..3dfcb06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,8 @@ "tailwindcss": "^3.2.4", "tinycolor2": "^1.4.2", "typescript": "^4.8.2", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zustand": "^4.5.0" } }, "node_modules/@adobe/css-tools": { @@ -20372,6 +20373,14 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -21307,6 +21316,33 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.0.tgz", + "integrity": "sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } }, "dependencies": { @@ -35799,6 +35835,12 @@ "requires-port": "^1.0.0" } }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -36536,6 +36578,14 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "zustand": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.0.tgz", + "integrity": "sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A==", + "requires": { + "use-sync-external-store": "1.2.0" + } } } } diff --git a/package.json b/package.json index f32cf5b..a953e1a 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "tailwindcss": "^3.2.4", "tinycolor2": "^1.4.2", "typescript": "^4.8.2", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zustand": "^4.5.0" }, "scripts": { "start": "react-scripts start", diff --git a/public/assets/moth-cover.png b/public/assets/moth-cover.png new file mode 100644 index 0000000..6dca23a Binary files /dev/null and b/public/assets/moth-cover.png differ diff --git a/roadmap.md b/roadmap.md index ffa46ae..f44810b 100644 --- a/roadmap.md +++ b/roadmap.md @@ -3,14 +3,9 @@ - move dot out of range resets color to red - changing the canvas size is weird - hsl rgb number inputs dont all work + - the first time you use an action it wipes the screen # MVP -- previous 2 color switch - - just as a quick reference, does no effect the pallete - - - colors used in pallette - - when changing color in pallete, update all instances of that color in all layers - - box selection tools - stretch with side grab - rotate with corner grab @@ -23,6 +18,20 @@ - lighten/darken tool +- basic shapes + - square + - circle + - rect + - oval + +- previous 2 color switch + - just as a quick reference, does no effect the pallete + - colors used in pallette + - when changing color in pallete, update all instances of that color in all layers + +- light/dark theme +- minimize UI + - import/export - import project file - export selected frame diff --git a/src/App.tsx b/src/App.tsx index 1826540..2d614bc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import React from 'react'; +import mixpanel from 'mixpanel-browser'; import { Home } from './pages/Home/index'; import { BrowserRouter, Routes, Route, } from 'react-router-dom'; -import mixpanel from 'mixpanel-browser'; mixpanel.init('d0fd76de4b27e761d97f59a0ca878094', {debug: true, track_pageview: true, persistence: 'localStorage'}); diff --git a/src/pages/Home/Canvas.tsx b/src/pages/Home/Canvas.tsx index f707a56..5b54a60 100644 --- a/src/pages/Home/Canvas.tsx +++ b/src/pages/Home/Canvas.tsx @@ -1,47 +1,14 @@ -import { IoEye } from "react-icons/io5"; import { ImMinus } from "react-icons/im"; import ReactTooltip from 'react-tooltip'; -import { HiEyeOff } from "react-icons/hi"; +import { IToolSettings } from "../../types"; import { BiPlusMedical } from "react-icons/bi"; +import React, { useEffect, useRef } from 'react'; import { FaUndoAlt, FaRedoAlt } from "react-icons/fa"; -import React, { useEffect, useRef, useState } from 'react'; -import { ICanvas, ILayer, IColor, IFrame, IToolSettings, IColorPallete, IColorStats } from "./"; -import { useShortcuts } from "../../utils/useShortcuts"; - - -interface IProps { - frames: IFrame[]; - pixelSize: number; - activeLayer: ILayer; - activeFrame: IFrame; - activeColor: IColor; - defaultCanvasSize: number; - toolSettings: IToolSettings; - activeColorPallete: IColorPallete; - setCanvas: (canvas: ICanvas) => void; - setActiveLayer: (layer: ILayer) => void; - setActiveColor: (color: IColor) => void; - setActiveColorPallete: (colorPallete: IColorPallete) => void; - setColorStats: (cs: IColorStats | ((cs: IColorStats) => IColorStats)) => void; - setToolSettings: (toolSettings: IToolSettings | ((t: IToolSettings) => IToolSettings)) => void; - canvas?: ICanvas; -} - - -interface IMouseState { - x: number; - y: number; - left: boolean; - right: boolean; - middle: boolean; - movementX: number; - movementY: number; - type: 'mousedown' | 'mouseup' | 'mousemove'; -} +import { useCanvas as useCanvasHook, useShortcuts } from "../../utils"; -export function Canvas(props: IProps) { - const data = useCanvas(props); +export function Canvas() { + const data = useCanvas(); return (
@@ -97,13 +64,13 @@ export function Canvas(props: IProps) { -
- false} - onPointerDown={data.mouseEventHandler as any} - onPointerMove={data.mouseEventHandler as any} - onPointerUp={data.mouseEventHandler as any} - onMouseDown={data.mouseEventHandler} - onMouseMove={data.mouseEventHandler} - onMouseUp={data.mouseEventHandler}> -
+
) } -function useCanvas(props: IProps) { - const canvasRef = useRef(null); - let [showGrid, setShowGrid] = useState(true); - let [layerHistory, setLayerHistory] = useState([]); - let [selectionArea, setSelectionArea] = useState(null); - let [activePoints, setActivePoints] = useState<{ x: number, y: number }[]>([]); - let [tempCanvas, setTempCanvas] = useState<{ element: HTMLCanvasElement, ctx: CanvasRenderingContext2D } | null>(null); - let shortcuts = useShortcuts({ +function useCanvas() { + const mainCanvasContainer = useRef(null); + const mainCanvas = useCanvasHook(); + const canvas1 = useCanvasHook(); + const canvas2 = useCanvasHook(); + + let selectionArea = useRef({ + data: null, + startPosition: { x: 0, y: 0 }, + currentPosition: { x: 0, y: 0 }, + }); + let mouseState = useRef({ + left: false, + right: false, + middle: false, + x: 0, + y: 0, + movementX: 0, + movementY: 0, + type: 'mousemove' + }); + let toolState = useRef({ + stage: "preview" + }); + let propsCache = useRef({ ...props }); + let { current: activePoints } = useRef<{ x: number, y: number }[]>([]); + let keys = useShortcuts({ "control+z": undo, "[": () => setBrushSize(-1), "]": () => setBrushSize(1), }); + let toolHandlers = { + "brush": (mouseState, paintState, x, y) => { + if (mouseState.left || mouseState.right) paintState.savePixels = true; - useEffect(() => { - paint(null, true); - }, [showGrid, props.activeLayer, props.activeFrame]); + // center the mouse cursor in the brush + let width = props.toolSettings.size; + let height = props.toolSettings.size; + let newX = x == 0 ? 0 : (x - (width / 2)) + ((x - (width / 2)) % 1); + let newY = y == 0 ? 0 : (y - (height / 2)) + ((y - (height / 2)) % 1); - useEffect(() => { - let ctx = canvasRef?.current?.getContext('2d'); + canvas1.ctx!.fillRect(newX, newY, width, height); - if (!canvasRef.current) return; - if (!ctx) return; + return paintState; + }, + "eraser": (mouseState, paintState, x, y) => { + if (mouseState.left || mouseState.right) paintState.savePixels = true; - let canvasState: ICanvas = { - element: canvasRef.current, - ctx: ctx, - height: props.defaultCanvasSize * props.pixelSize, - width: props.defaultCanvasSize * props.pixelSize, - zoom: 15, - }; - - canvasState.element!.width = canvasState.width; - canvasState.element!.height = canvasState.height; + // center the mouse cursor in the brush + let width = props.toolSettings.size; + let height = props.toolSettings.size; + let newX = x == 0 ? 0 : (x - (width / 2)) + ((x - (width / 2)) % 1); + let newY = y == 0 ? 0 : (y - (height / 2)) + ((y - (height / 2)) % 1); + + canvas1.ctx!.fillStyle = "rgba(255, 255, 255, .004)"; + canvas1.ctx!.fillRect(newX, newY, width, height); + + return paintState; + }, + "bucket": (mouseState, paintState, x, y) => { + if (mouseState.left || mouseState.right) paintState.savePixels = true; + // floodFill(props.activeLayer.image, x, y, props.activeColor); + + return paintState; + }, + "line": (mouseState, paintState, x, y) => { + if (mouseState.type == "mouseup") { + paintState.savePixels = true; + activePoints = []; + } + if (mouseState.type == "mousedown") { + activePoints = [{ x, y }]; + } - props.setCanvas({ ...canvasState }); + let point = activePoints[0] ?? { x, y }; + let xDelta = x - point.x; + let yDelta = y - point.y; + let distance = Math.sqrt(xDelta * xDelta + yDelta * yDelta); + let pointCount = Math.floor(distance); + let progress = 1 / pointCount; + let points = [point]; - let tempCanvas = document.createElement("canvas"); - tempCanvas.width = canvasState.width; - tempCanvas.height = canvasState.height; + for (let i = 1; i < pointCount; i++) { + let newX = points[0].x + (x - points[0].x) * (progress * i); + let newY = points[0].y + (y - points[0].y) * (progress * i); + points.push({ x: newX, y: newY }); + } - let tempCtx = tempCanvas.getContext("2d"); - tempCtx!.imageSmoothingEnabled = false; + points.push({ x, y }); + points.forEach(point => { + point.x = point.x - (point.x % 1); + point.y = point.y - (point.y % 1); - setTempCanvas({ - element: tempCanvas, - ctx: tempCtx! - }); - }, [canvasRef.current]); + canvas1.ctx!.fillRect(point.x, point.y, 1, 1); + }); - useEffect(() => { - if (!props?.canvas?.ctx) return; + return paintState; + }, + "mirror": (mouseState, paintState, x, y) => { + if (mouseState.left || mouseState.right) paintState.savePixels = true; - paint(); + if (props.toolSettings.mirror.x) { + canvas1.ctx!.fillRect(x, y, props.toolSettings.size, props.toolSettings.size); + canvas1.ctx!.fillRect(mainCanvas.width - x - 1, y, props.toolSettings.size, props.toolSettings.size); + } + if (props.toolSettings.mirror.y) { + canvas1.ctx!.fillRect(x, y, props.toolSettings.size, props.toolSettings.size); + canvas1.ctx!.fillRect(x, mainCanvas.height - y - 1, props.toolSettings.size, props.toolSettings.size); + } + if (props.toolSettings.mirror.x && props.toolSettings.mirror.y) { + canvas1.ctx!.fillRect(x, y, props.toolSettings.size, props.toolSettings.size); + canvas1.ctx!.fillRect(mainCanvas.width - x - 1, y, props.toolSettings.size, props.toolSettings.size); + canvas1.ctx!.fillRect(x, mainCanvas.height - y - 1, props.toolSettings.size, props.toolSettings.size); + canvas1.ctx!.fillRect(mainCanvas.width - x - 1, mainCanvas.height - y - 1, props.toolSettings.size, props.toolSettings.size); + } - props.canvas.element!.style.width = `${props.canvas.width * (props.canvas?.zoom ?? 1)}px`; - props.canvas.element!.style.height = `${props.canvas.height * (props.canvas.zoom ?? 1)}px`; - props.canvas.element!.addEventListener('wheel', (e) => { - e.preventDefault(); - e.stopPropagation(); + return paintState; + }, + // "box": (mouseState, paintState, x, y) => { + // let point1 = activePoints[0] ?? { x, y }; + // let point2 = activePoints[1] ?? { x, y }; + // let lastPoint = activePoints[2] ?? { x, y }; + // let minY = Math.min(point1.y, point2.y); + // let maxY = Math.max(point1.y, point2.y); + // let minX = Math.min(point1.x, point2.x); + // let maxX = Math.max(point1.x, point2.x); + // let topLeftPoint = { + // x: minX, + // y: minY + // }; + // let bottomRightPoint = { + // x: maxX, + // y: maxY + // }; + // let height = selectionArea?.height ?? bottomRightPoint.y - topLeftPoint.y; + // let width = selectionArea?.width ?? bottomRightPoint.x - topLeftPoint.x; + + // if (!selectionArea) { + // if (mouseState.type == "mouseup") { + // let tempCanvas1 = document.createElement("canvas"); + // tempCanvas1.width = props.activeLayer.image.width; + // tempCanvas1.height = props.activeLayer.image.height; + // let tempCtx1 = tempCanvas1.getContext("2d")!; + // tempCtx1.putImageData(props.activeLayer.image, 0, 0); + + // let newImage = tempCtx1.getImageData(topLeftPoint.x, topLeftPoint.y, width, height); + // setSelectionArea(newImage); + // setActivePoints([point1, point2]); + // } + // if (mouseState.type == "mousedown") setActivePoints([{ x, y }]); + // } + + // if (mouseState.type == "mousedown") { + // let insideSelection = x >= lastPoint.x && x <= lastPoint.x + width && y >= lastPoint.y && y <= lastPoint.y + height; + + // if (!insideSelection) { + // paintState.savePixels = true; + // setSelectionArea(null); + // setActivePoints([]); + + // if (props.toolSettings.leftTool == "box") props.setToolSettings((t) => ({ ...t, leftTool: "brush" })); + // if (props.toolSettings.middleTool == "box") props.setToolSettings((t) => ({ ...t, middleTool: "brush" })); + // if (props.toolSettings.rightTool == "box") props.setToolSettings((t) => ({ ...t, rightTool: "brush" })); + + // canvas1.ctx!.fillStyle = "rgba(255, 255, 255, .004)"; + // canvas1.ctx!.fillRect(topLeftPoint.x, topLeftPoint.y, width, height); + // } + // } + + // return paintState; + // }, + // "move": (mouseState, paintState, x, y) => { + // if (!selectionArea) { + // setActivePoints([{ x: 0, y: 0 }, { x: props.activeLayer.image.width, y: props.activeLayer.image.height }]); + // setSelectionArea(props.activeLayer.image); + // } + + // if (mouseState.type == "mousedown" && selectionArea) { + // let point1 = activePoints[0] ?? { x, y }; + // let point2 = activePoints[1] ?? { x, y }; + // let lastPoint = activePoints[2] ?? { x, y }; + // let minY = Math.min(point1.y, point2.y); + // let maxY = Math.max(point1.y, point2.y); + // let minX = Math.min(point1.x, point2.x); + // let maxX = Math.max(point1.x, point2.x); + // let topLeftPoint = { + // x: minX, + // y: minY + // }; + // let bottomRightPoint = { + // x: maxX, + // y: maxY + // }; + // let height = selectionArea?.height ?? bottomRightPoint.y - topLeftPoint.y; + // let width = selectionArea?.width ?? bottomRightPoint.x - topLeftPoint.x; + // let insideSelection = x >= lastPoint.x && x <= lastPoint.x + width && y >= lastPoint.y && y <= lastPoint.y + height; + + // if (!insideSelection) { + // paintState.savePixels = true; + // setSelectionArea(null); + // setActivePoints([]); + + // if (props.toolSettings.leftTool == "move") props.setToolSettings((t) => ({ ...t, leftTool: "brush" })); + // if (props.toolSettings.middleTool == "move") props.setToolSettings((t) => ({ ...t, middleTool: "brush" })); + // if (props.toolSettings.rightTool == "move") props.setToolSettings((t) => ({ ...t, rightTool: "brush" })); + + // canvas1.ctx!.fillStyle = "rgba(255, 255, 255, .004)"; + // canvas1.ctx!.fillRect(topLeftPoint.x, topLeftPoint.y, width, height); + // } + // } + + // return paintState; + // }, + "eyedropper": (mouseState, paintState, x, y) => { + let image = mainCanvas.getImageData(); + let current = { + r: image.data[(((y * image.width) + x) * 4)], + g: image.data[(((y * image.width) + x) * 4) + 1], + b: image.data[(((y * image.width) + x) * 4) + 2], + a: image.data[(((y * image.width) + x) * 4) + 3] + }; + props.setActiveColor(current); - let zoom = e.deltaY > 0 ? -.2 : .2; - setZoom(zoom); - }, { passive: false }); - }, [props.canvas]); + return paintState; + }, + }; useEffect(() => { - // push a copy of activeLayer to layerHistory - if (!props.canvas) return; - if (!props.canvas.ctx) return; - - let image = props.canvas.ctx!.createImageData(props.activeLayer.image.width, props.activeLayer.image.height); - image.data.set(props.activeLayer.image.data); + (function render() { + requestAnimationFrame(render); + paint(); + })(); + }, []); - let oldData = layerHistory[layerHistory.length - 1]; - let newData = image; + useEffect(() => { + let setMouseState = e => { + mouseState.current = { + x: e.clientX, + y: e.clientY, + movementX: e.movementX, + movementY: e.movementY, + left: e.buttons === 1, + middle: e.buttons === 4 || e.button === 1, + right: e.buttons === 2 || e.button === 2, + type: e.type as IMouseState["type"] + } as IMouseState; + }; - if (oldData && newData) - if (JSON.stringify(oldData.image.data) === JSON.stringify(newData.data)) return; + document.addEventListener('mousedown', setMouseState); + document.addEventListener('mouseup', setMouseState); + document.addEventListener('mousemove', setMouseState); + document.addEventListener('pointerdown', setMouseState); + document.addEventListener('pointerup', setMouseState); + document.addEventListener('pointermove', setMouseState); - let layerCopy: ILayer = { - symbol: props.activeLayer.symbol, - name: props.activeLayer.name, - image: image, - opacity: props.activeLayer.opacity - }; + document.querySelector(".p-app__canvas-container")!.addEventListener('wheel', (e: any) => { + e.preventDefault(); + e.stopPropagation(); + // setZoom(e.deltaY > 0 ? -.2 : .2); + }, { passive: false }); - setLayerHistory(lh => { - let layerHistoryCopy = [...lh, layerCopy]; - if (layerHistory.length >= 200) - layerHistoryCopy = layerHistoryCopy.slice(-200); - return layerHistoryCopy; - }); - }, [props.canvas]); + return () => { + document.removeEventListener('mousedown', setMouseState); + document.removeEventListener('mouseup', setMouseState); + document.removeEventListener('mousemove', setMouseState); + document.removeEventListener('pointerdown', setMouseState); + document.removeEventListener('pointerup', setMouseState); + document.removeEventListener('pointermove', setMouseState); + } + }, []); useEffect(() => { - if (!tempCanvas) return; - if (!props.canvas) return; - - tempCanvas.element.width = props.canvas.width; - tempCanvas.element.height = props.canvas.height; - }, [props.canvas?.height, props.canvas?.width]); + mainCanvasContainer.current?.appendChild(mainCanvas.canvas); + mainCanvas.ctx!.imageSmoothingEnabled = false; + }, []); useEffect(() => { - ReactTooltip.rebuild(); - }, [props.toolSettings]) + propsCache.current = { ...props }; + }, [props]); - function setZoom(zoomDelta: number) { - if (!props.canvas) return; - if (!props.canvas.ctx) return; + useEffect(() => { ReactTooltip.rebuild() }, [props.toolSettings]); - let newZoom = ((props.canvas?.zoom ?? 1) + zoomDelta < 1) - ? (zoomDelta < 0 ? ((props.canvas?.zoom ?? 1) / 2) : ((props.canvas?.zoom ?? 1) * 2)) : (props.canvas?.zoom ?? 1) + zoomDelta; + // function setZoom(zoomDelta: number) { + // if (!props.canvas) return; + // if (!props.canvas.ctx) return; - props.canvas.zoom = newZoom; - props.setCanvas({ ...props.canvas }); - } + // let newZoom = ((props.canvas?.zoom ?? 1) + zoomDelta < 1) + // ? (zoomDelta < 0 ? ((props.canvas?.zoom ?? 1) / 2) : ((props.canvas?.zoom ?? 1) * 2)) : (props.canvas?.zoom ?? 1) + zoomDelta; + + // props.canvas.zoom = newZoom; + // props.setCanvas({ ...props.canvas }); + // } function setBrushSize(delta: number) { props.setToolSettings((p: IToolSettings) => ({ ...p, size: (p.size + delta < 1) ? 1 : (p.size + delta) })); } - function mouseEventHandler(e: React.MouseEvent) { - if (!props.canvas) return; - // e.preventDefault(); - e.stopPropagation(); - - let mouseState: IMouseState = { - x: e.clientX, - y: e.clientY, - movementX: e.movementX, - movementY: e.movementY, - left: e.buttons === 1, - middle: e.buttons === 4 || e.button === 1, - right: e.buttons === 2 || e.button === 2, - type: e.type as IMouseState["type"] - }; + function undo() { } - paint({ ...mouseState }); + function redo() { } - // fill in missed pixels if the mouse is moving to fast - let lastPoint = (window as any).GLOBAL_LAST_POINT ?? { x: 0, y: 0 }; - if (Math.abs(mouseState.x - lastPoint.x) != 1 && Math.abs(mouseState.y - lastPoint.y) != 1) { - let distance = 18; - let progress = 1 / distance; - let newMouseState: IMouseState; - let prevPoint: { x: number, y: number } | null = null; + // function resizeHandler(size: { height?: number, width?: number }) { + // if (!props.canvas) return; - for (let i = 1; i <= distance; i++) { - let point = { - x: lastPoint.x + (mouseState.x - lastPoint.x) * (progress * i), - y: lastPoint.y + (mouseState.y - lastPoint.y) * (progress * i), - } + // let canvasState = { ...props.canvas }; - if (prevPoint != null && point.x == prevPoint.x && point.y == prevPoint.y) continue; - prevPoint = point; + // props.canvas.element!.style.width = `${canvasState.width * (props.canvas?.zoom ?? 1)}px`; + // props.canvas.element!.style.height = `${canvasState.height * (props.canvas?.zoom ?? 1)}px`; - newMouseState = { - ...mouseState, - x: point.x, - y: point.y, - }; - paint(newMouseState); - } - } - (window as any).GLOBAL_LAST_POINT = { x: mouseState.x, y: mouseState.y }; - } + // props.setCanvas(canvasState); + // } - function drawCheckeredGrid() { - if (!props.canvas) return; - if (!props.canvas.ctx) return; - if (!showGrid) return; - - let size = props.pixelSize; - let rows = props.canvas.height / size; - let cols = props.canvas.width / size; - - for (let i = 0; i < rows; i++) { - for (let j = 0; j < cols; j++) { - let x = j * size; - let y = i * size; - let color = (i + j) % 2 === 0 ? '#fff' : '#ddd'; - props.canvas.ctx.fillStyle = color; - props.canvas.ctx.fillRect(x, y, size, size); - } - } - } - - function floodFill( - image: ImageData, - sx: number, - sy: number, - newColor: IColor, - prevColor?: IColor, - scannedCoords?: { [key: string]: boolean } - ) { - if (!prevColor) prevColor = { - r: image.data[(((sy * image.width) + sx) * 4)], - g: image.data[(((sy * image.width) + sx) * 4) + 1], - b: image.data[(((sy * image.width) + sx) * 4) + 2], - a: image.data[(((sy * image.width) + sx) * 4) + 3] + function paint() { + let paintState = { + savePixels: false }; - if (!scannedCoords) scannedCoords = {}; - if (scannedCoords[`${sx},${sy}`]) return; - if (sx < 0) return; - if (sy < 0) return; - if (sx >= image.width) return; - if (sy >= image.height) return; - - scannedCoords[`${sx},${sy}`] = true; - - let current = { - r: image.data[(((sy * image.width) + sx) * 4)], - g: image.data[(((sy * image.width) + sx) * 4) + 1], - b: image.data[(((sy * image.width) + sx) * 4) + 2], - a: image.data[(((sy * image.width) + sx) * 4) + 3] - } - - let colorsAreTheSame = current.r == prevColor.r && current.g == prevColor.g && current.b == prevColor.b && current.a == prevColor.a; - if (!colorsAreTheSame) return; - - tempCanvas!.ctx.fillStyle = `rgba(${newColor.r},${newColor.g},${newColor.b},${newColor.a / 255})`; - tempCanvas!.ctx.fillRect(sx, sy, props.pixelSize, props.pixelSize); - - floodFill(image, sx - props.pixelSize, sy, newColor, current, scannedCoords); - floodFill(image, sx + props.pixelSize, sy, newColor, current, scannedCoords); - floodFill(image, sx, sy - props.pixelSize, newColor, current, scannedCoords); - floodFill(image, sx, sy + props.pixelSize, newColor, current, scannedCoords); - } - - function undo() { - setLayerHistory(oldLayerHistory => { - let targetLayer: ILayer; - let oldLayerState = oldLayerHistory.pop(); - if (!oldLayerState) return oldLayerHistory; - - props.frames.forEach(frame => { - frame.layers.forEach(layer => { - if (layer.symbol == oldLayerState?.symbol) - targetLayer = layer; - }); - }); - if (!targetLayer!) return oldLayerHistory; - - targetLayer!.image = oldLayerState!.image; - props.setActiveLayer({ ...targetLayer }); - - return [...oldLayerHistory]; - }); - } - - function redo(){} - - function resizeHandler(size: { height?: number, width?: number }) { - if (!props.canvas) return; - - let canvasState = { ...props.canvas }; - if (size.height) canvasState.height = size.height * props.pixelSize; - if (size.width) canvasState.width = size.width * props.pixelSize; - - props.canvas.element!.style.width = `${canvasState.width * (props.canvas?.zoom ?? 1)}px`; - props.canvas.element!.style.height = `${canvasState.height * (props.canvas?.zoom ?? 1)}px`; - - props.setCanvas(canvasState); - } - - function paint(mouseState?: IMouseState | null, fakeEvent: boolean = false) { - if (!props?.canvas?.ctx) return; - if (!tempCanvas?.ctx) return; - if (!mouseState) { - mouseState = { - left: false, - right: false, - middle: false, - x: 0, - y: 0, - movementX: 0, - movementY: 0, - type: 'mousemove' - }; - } - - props.canvas.ctx.clearRect(0, 0, props.canvas.width, props.canvas.height); - tempCanvas.ctx.clearRect(0, 0, props.canvas.width, props.canvas.height); - drawCheckeredGrid(); + canvas1.clear(); + canvas2.clear(); + mainCanvas.clear(); + mainCanvas.drawGrid(); // color related operations - let existingColor = props.activeColorPallete.colors - .find(color => JSON.stringify(color) === JSON.stringify(props.activeColor)); + let existingColor = propsCache.current.activeColorPallete.colors + .find(color => JSON.stringify(color) === JSON.stringify(propsCache.current.activeColor)); - if (!existingColor && (mouseState.left || mouseState.right || mouseState.middle)) { - let newColorPallete = { ...props.activeColorPallete }; - newColorPallete.colors.push(props.activeColor); - props.setActiveColorPallete(newColorPallete); + if (!existingColor && (mouseState.current.left || mouseState.current.right || mouseState.current.middle)) { + let newColorPallete = { ...propsCache.current.activeColorPallete }; + newColorPallete.colors.push(propsCache.current.activeColor); + propsCache.current.setActiveColorPallete(newColorPallete); } - if ((mouseState.left || mouseState.right || mouseState.middle)) { - let { r, g, b, a } = props.activeColor; + if ((mouseState.current.left || mouseState.current.right || mouseState.current.middle)) { + let { r, g, b, a } = propsCache.current.activeColor; let colorString = `${r},${g},${b},${a}`; - props.setColorStats(cs => ({ + propsCache.current.setColorStats(cs => ({ ...cs, [colorString]: { count: (cs[colorString]?.count ?? 0) + 1, @@ -471,239 +458,50 @@ function useCanvas(props: IProps) { } // position relative to the canvas element - let rect = props.canvas.element!.getBoundingClientRect(); - let x = (mouseState.x - rect.x) / (props.canvas?.zoom ?? 1); - let y = (mouseState.y - rect.y) / (props.canvas?.zoom ?? 1); - - let tool = ""; - if (mouseState.left) tool = props.toolSettings.leftTool; - else if (mouseState.right) tool = props.toolSettings.rightTool; - else if (mouseState.middle) tool = props.toolSettings.middleTool; - else tool = props.toolSettings.leftTool; - - const snapping = true; - if (snapping) { - x = x - (x % props.pixelSize); - y = y - (y % props.pixelSize); - } - - tempCanvas!.ctx!.fillStyle = `rgba(${props.activeColor.r}, ${props.activeColor.g}, ${props.activeColor.b}, ${props.activeColor.a / 255})`; - - // show temp pixels from brush previews - let savePixels = false; - if (tool == "brush") { - if (mouseState.left || mouseState.right) savePixels = true; - - // center the mouse cursor in the brush - let width = props.pixelSize * props.toolSettings.size; - let height = props.pixelSize * props.toolSettings.size; - let newX = x == 0 ? 0 : (x - (width / 2)) + ((x - (width / 2)) % props.pixelSize); - let newY = y == 0 ? 0 : (y - (height / 2)) + ((y - (height / 2)) % props.pixelSize); - - tempCanvas!.ctx!.fillRect(newX, newY, width, height); - } - else if (tool == "eraser") { - if (mouseState.left || mouseState.right) savePixels = true; - - // center the mouse cursor in the brush - let width = props.pixelSize * props.toolSettings.size; - let height = props.pixelSize * props.toolSettings.size; - let newX = x == 0 ? 0 : (x - (width / 2)) + ((x - (width / 2)) % props.pixelSize); - let newY = y == 0 ? 0 : (y - (height / 2)) + ((y - (height / 2)) % props.pixelSize); - - tempCanvas!.ctx!.fillStyle = "rgba(255, 255, 255, .004)"; - tempCanvas!.ctx!.fillRect(newX, newY, width, height); - } - else if (tool == "bucket") { - if (mouseState.left || mouseState.right) savePixels = true; - - floodFill(props.activeLayer.image, x, y, props.activeColor); - } - else if (tool == "line") { - if (mouseState.type == "mouseup") { - savePixels = true; - setActivePoints([]); - } - if (mouseState.type == "mousedown") { - setActivePoints([{ x, y }]); - } - - let point = activePoints[0] ?? { x, y }; - let xDelta = x - point.x; - let yDelta = y - point.y; - let distance = Math.sqrt(xDelta * xDelta + yDelta * yDelta); - let pointCount = Math.floor(distance); - let progress = 1 / pointCount; - let points = [point]; - - for (let i = 1; i < pointCount; i++) { - let newX = points[0].x + (x - points[0].x) * (progress * i); - let newY = points[0].y + (y - points[0].y) * (progress * i); - points.push({ x: newX, y: newY }); - } + let rect = mainCanvas.canvas.getBoundingClientRect(); + let x = Math.floor(mouseState.current.x - rect.x); + let y = Math.floor(mouseState.current.y - rect.y); - points.push({ x, y }); - points.forEach(point => { - point.x = point.x - (point.x % props.pixelSize); - point.y = point.y - (point.y % props.pixelSize); - - tempCanvas!.ctx!.fillRect(point.x, point.y, props.pixelSize, props.pixelSize); - }); - } - else if (tool == "mirror") { - if (mouseState.left || mouseState.right) savePixels = true; - - if (props.toolSettings.mirror.x) { - tempCanvas!.ctx!.fillRect(x, y, props.pixelSize * props.toolSettings.size, props.pixelSize * props.toolSettings.size); - tempCanvas!.ctx!.fillRect(props.canvas.width - x - props.pixelSize, y, props.pixelSize * props.toolSettings.size, props.pixelSize * props.toolSettings.size); - } - if (props.toolSettings.mirror.y) { - tempCanvas!.ctx!.fillRect(x, y, props.pixelSize * props.toolSettings.size, props.pixelSize * props.toolSettings.size); - tempCanvas!.ctx!.fillRect(x, props.canvas.height - y - props.pixelSize, props.pixelSize * props.toolSettings.size, props.pixelSize * props.toolSettings.size); - } - if (props.toolSettings.mirror.x && props.toolSettings.mirror.y) { - tempCanvas!.ctx!.fillRect(x, y, props.pixelSize * props.toolSettings.size, props.pixelSize * props.toolSettings.size); - tempCanvas!.ctx!.fillRect(props.canvas.width - x - props.pixelSize, y, props.pixelSize * props.toolSettings.size, props.pixelSize * props.toolSettings.size); - tempCanvas!.ctx!.fillRect(x, props.canvas.height - y - props.pixelSize, props.pixelSize * props.toolSettings.size, props.pixelSize * props.toolSettings.size); - tempCanvas!.ctx!.fillRect(props.canvas.width - x - props.pixelSize, props.canvas.height - y - props.pixelSize, props.pixelSize * props.toolSettings.size, props.pixelSize * props.toolSettings.size); - } - } - else if (tool == "box") { - let point1 = activePoints[0] ?? { x, y }; - let point2 = activePoints[1] ?? { x, y }; - let lastPoint = activePoints[2] ?? { x, y }; - let minY = Math.min(point1.y, point2.y); - let maxY = Math.max(point1.y, point2.y); - let minX = Math.min(point1.x, point2.x); - let maxX = Math.max(point1.x, point2.x); - let topLeftPoint = { - x: minX, - y: minY - }; - let bottomRightPoint = { - x: maxX, - y: maxY - }; - let height = selectionArea?.height ?? bottomRightPoint.y - topLeftPoint.y; - let width = selectionArea?.width ?? bottomRightPoint.x - topLeftPoint.x; - - if (!selectionArea) { - if (mouseState.type == "mouseup") { - let tempCanvas1 = document.createElement("canvas"); - tempCanvas1.width = props.activeLayer.image.width; - tempCanvas1.height = props.activeLayer.image.height; - let tempCtx1 = tempCanvas1.getContext("2d")!; - tempCtx1.putImageData(props.activeLayer.image, 0, 0); - - let newImage = tempCtx1.getImageData(topLeftPoint.x, topLeftPoint.y, width, height); - setSelectionArea(newImage); - setActivePoints([point1, point2]); - } - if (mouseState.type == "mousedown") setActivePoints([{ x, y }]); - } + const { leftTool, rightTool, middleTool } = propsCache.current.toolSettings; + const tool = + mouseState.current.left ? leftTool : + mouseState.current.right ? rightTool : + mouseState.current.middle ? middleTool : leftTool; - if (mouseState.type == "mousedown") { - let insideSelection = x >= lastPoint.x && x <= lastPoint.x + width && y >= lastPoint.y && y <= lastPoint.y + height; - - if (!insideSelection) { - savePixels = true; - setSelectionArea(null); - setActivePoints([]); + let { r, g, b, a } = propsCache.current.activeColor; + canvas1.ctx!.fillStyle = `rgba(${r}, ${g}, ${b}, ${a / 255})`; - if (props.toolSettings.leftTool == "box") props.setToolSettings((t) => ({ ...t, leftTool: "brush" })); - if (props.toolSettings.middleTool == "box") props.setToolSettings((t) => ({ ...t, middleTool: "brush" })); - if (props.toolSettings.rightTool == "box") props.setToolSettings((t) => ({ ...t, rightTool: "brush" })); - - tempCanvas!.ctx!.fillStyle = "rgba(255, 255, 255, .004)"; - tempCanvas!.ctx!.fillRect(topLeftPoint.x, topLeftPoint.y, width, height); - } - } - } - else if (tool == "move") { - if (!selectionArea) { - setActivePoints([{ x: 0, y: 0 }, { x: props.activeLayer.image.width, y: props.activeLayer.image.height }]); - setSelectionArea(props.activeLayer.image); - } - - if (mouseState.type == "mousedown" && selectionArea) { - let point1 = activePoints[0] ?? { x, y }; - let point2 = activePoints[1] ?? { x, y }; - let lastPoint = activePoints[2] ?? { x, y }; - let minY = Math.min(point1.y, point2.y); - let maxY = Math.max(point1.y, point2.y); - let minX = Math.min(point1.x, point2.x); - let maxX = Math.max(point1.x, point2.x); - let topLeftPoint = { - x: minX, - y: minY - }; - let bottomRightPoint = { - x: maxX, - y: maxY - }; - let height = selectionArea?.height ?? bottomRightPoint.y - topLeftPoint.y; - let width = selectionArea?.width ?? bottomRightPoint.x - topLeftPoint.x; - let insideSelection = x >= lastPoint.x && x <= lastPoint.x + width && y >= lastPoint.y && y <= lastPoint.y + height; - - if (!insideSelection) { - savePixels = true; - setSelectionArea(null); - setActivePoints([]); - - if (props.toolSettings.leftTool == "move") props.setToolSettings((t) => ({ ...t, leftTool: "brush" })); - if (props.toolSettings.middleTool == "move") props.setToolSettings((t) => ({ ...t, middleTool: "brush" })); - if (props.toolSettings.rightTool == "move") props.setToolSettings((t) => ({ ...t, rightTool: "brush" })); - - tempCanvas!.ctx!.fillStyle = "rgba(255, 255, 255, .004)"; - tempCanvas!.ctx!.fillRect(topLeftPoint.x, topLeftPoint.y, width, height); - } - } - } + //* draw a preview of the selected tool + paintState = toolHandlers[tool](mouseState.current, paintState, x, y); // draw selection widget - if (!savePixels && (tool == "box" || tool == "move")) { - let localTopLeft = activePoints[2] ?? activePoints[0] ?? { x, y }; - let localBottomRight = (activePoints[2] && selectionArea) ? { x: activePoints[2].x + selectionArea?.width, y: activePoints[2].y + selectionArea?.height } : activePoints[1] ?? { x, y }; + // if (!paintState.savePixels && (tool == "box" || tool == "move")) { + // let localTopLeft = activePoints[2] ?? activePoints[0] ?? { x, y }; + // let localBottomRight = (activePoints[2] && selectionArea) ? { x: activePoints[2].x + selectionArea?.width, y: activePoints[2].y + selectionArea?.height } : activePoints[1] ?? { x, y }; - tempCanvas!.ctx!.lineWidth = props.pixelSize; - tempCanvas!.ctx!.strokeRect(Math.max(localTopLeft.x - (props.pixelSize / 2), 0), Math.max(localTopLeft.y - (props.pixelSize / 2), 0), localBottomRight.x - localTopLeft.x, localBottomRight.y - localTopLeft.y); - } + // canvas1.ctx!.lineWidth = 1; + // canvas1.ctx!.strokeRect(Math.max(localTopLeft.x - 0.5, 0), Math.max(localTopLeft.y - 0.5, 0), localBottomRight.x - localTopLeft.x, localBottomRight.y - localTopLeft.y); + // } // if were currently selecting an area - if (selectionArea) { - let tempCanvas1 = document.createElement("canvas"); - let tempCtx1 = tempCanvas1.getContext("2d"); - tempCanvas1.width = props.activeLayer.image.width; - tempCanvas1.height = props.activeLayer.image.height; - - let tempCanvas2 = document.createElement("canvas"); - let tempCtx2 = tempCanvas2.getContext("2d"); - tempCanvas2.width = props.activeLayer.image.width; - tempCanvas2.height = props.activeLayer.image.height; - - let offsetX = Math.sign(mouseState.movementX) * props.pixelSize; - let offsetY = Math.sign(mouseState.movementY) * props.pixelSize; - let lastPoint = (activePoints.length <= 2) ? activePoints[0] : activePoints[activePoints.length - 1]; - - if (mouseState.left || mouseState.right) { - lastPoint.x += offsetX; - lastPoint.y += offsetY; - setActivePoints(p => [p[0], p[1], { ...lastPoint }]); - } + // if (selectionArea) { + // let offsetX = mouseState.current.movementX; + // let offsetY = mouseState.current.movementY; + // let lastPoint = (activePoints.length <= 2) ? activePoints[0] : activePoints[activePoints.length - 1]; - tempCtx1!.putImageData(selectionArea, lastPoint.x, lastPoint.y); - tempCtx2!.drawImage(tempCanvas1, 0, 0); - tempCanvas.ctx!.drawImage(tempCanvas2, 0, 0); + // if (mouseState.current.left) { + // lastPoint.x += offsetX; + // lastPoint.y += offsetY; + // setActivePoints(p => [p[0], p[1], { ...lastPoint }]); + // } - let image = tempCtx2!.createImageData(selectionArea.width, selectionArea.height); - image.data.set(tempCtx2!.getImageData(lastPoint.x, lastPoint.y, selectionArea.width, selectionArea.height).data); - if (!savePixels) setSelectionArea(image); - } + // canvas1.putImageData(selectionArea, lastPoint.x, lastPoint.y); + // } - let tempImage = tempCanvas!.ctx!.getImageData(0, 0, props.canvas.width, props.canvas.height); + let tempImage = canvas1.getImageData(); // save pixels to layer - if (savePixels) { + if (paintState.savePixels) { for (let i = 0; i < tempImage.data.length; i += 4) { let r = tempImage.data[i]; let g = tempImage.data[i + 1]; @@ -711,77 +509,54 @@ function useCanvas(props: IProps) { let a = tempImage.data[i + 3]; // paint colored pixels - if (r != 0 || g != 0 || b != 0 || a != 0) { - props.activeLayer.image!.data[i] = tempImage.data[i]; - props.activeLayer.image!.data[i + 1] = tempImage.data[i + 1]; - props.activeLayer.image!.data[i + 2] = tempImage.data[i + 2]; - props.activeLayer.image!.data[i + 3] = tempImage.data[i + 3]; + if (r != 0 || g != 0 || b != 0 || a > 1) { + propsCache.current.activeLayer.image!.data[i] = tempImage.data[i]; + propsCache.current.activeLayer.image!.data[i + 1] = tempImage.data[i + 1]; + propsCache.current.activeLayer.image!.data[i + 2] = tempImage.data[i + 2]; + propsCache.current.activeLayer.image!.data[i + 3] = tempImage.data[i + 3]; } // erase invisible pixels if (a === 1) { - props.activeLayer.image!.data[i] = 0; - props.activeLayer.image!.data[i + 1] = 0; - props.activeLayer.image!.data[i + 2] = 0; - props.activeLayer.image!.data[i + 3] = 0; + propsCache.current.activeLayer.image!.data[i] = 0; + propsCache.current.activeLayer.image!.data[i + 1] = 0; + propsCache.current.activeLayer.image!.data[i + 2] = 0; + propsCache.current.activeLayer.image!.data[i + 3] = 0; } } - props.setActiveLayer({ ...props.activeLayer }); + propsCache.current.setActiveLayer({ ...propsCache.current.activeLayer }); } - let reversedLayers = props.activeFrame.layers.slice().reverse(); + let reversedLayers = propsCache.current.activeFrame.layers.slice().reverse(); reversedLayers.forEach((layer) => { - if (!props.canvas) return; - if (!props.canvas.ctx) return; - // apply layer opacity let diff = 255 - layer.opacity; - tempCanvas!.ctx!.putImageData(layer.image, 0, 0); - let imageDataCopy = tempCanvas!.ctx!.getImageData(0, 0, props.canvas.width, props.canvas.height) + canvas1.putImageData(layer.image); + let imageDataCopy = canvas1.getImageData(); for (let i = 3; i < imageDataCopy.data.length; i += 4) { imageDataCopy.data[i] = imageDataCopy.data[i] - diff; } // start painting old pixels - tempCanvas!.ctx!.putImageData(imageDataCopy, 0, 0); - - // paint selection area - if (selectionArea && (layer.symbol == props.activeLayer.symbol)) { - tempCanvas!.ctx!.clearRect(activePoints[0].x, activePoints[0].y, selectionArea.width, selectionArea.height); - } + canvas1.putImageData(imageDataCopy); // finish painting old pixels - props.canvas.ctx.drawImage(tempCanvas!.element, 0, 0, props.canvas.width, props.canvas.height); + mainCanvas.drawImage(canvas1.canvas); - // paint new pixels - if (layer.symbol == props.activeLayer.symbol) { - tempCanvas!.ctx!.putImageData(tempImage, 0, 0); - props.canvas.ctx.drawImage(tempCanvas!.element, 0, 0, props.canvas.width, props.canvas.height); + // paint new pixels + if (layer.symbol == propsCache.current.activeLayer.symbol) { + canvas1.putImageData(tempImage); + mainCanvas.drawImage(canvas1.canvas); } }); - - if (tool == "eyedropper" && mouseState.type == "mousedown") { - let image = props.canvas.ctx.getImageData(0, 0, props.canvas.width, props.canvas.height); - let current = { - r: image.data[(((y * image.width) + x) * 4)], - g: image.data[(((y * image.width) + x) * 4) + 1], - b: image.data[(((y * image.width) + x) * 4) + 2], - a: image.data[(((y * image.width) + x) * 4) + 3] - }; - props.setActiveColor(current); - } } return { undo, redo, - setZoom, - showGrid, - canvasRef, - setShowGrid, setBrushSize, - resizeHandler, - mouseEventHandler, + // resizeHandler, + mainCanvasContainer, }; } diff --git a/src/pages/Home/Colors.tsx b/src/pages/Home/Colors.tsx index 4edf1d2..19bed84 100644 --- a/src/pages/Home/Colors.tsx +++ b/src/pages/Home/Colors.tsx @@ -1,14 +1,14 @@ import ReactTooltip from 'react-tooltip'; -import { HiColorSwatch } from "react-icons/hi"; import { BsTrophyFill } from "react-icons/bs"; import { BiPlusMedical } from "react-icons/bi"; +import { HiColorSwatch } from "react-icons/hi"; import { MdMovieFilter } from "react-icons/md"; import React, { useEffect, useState } from 'react'; import { IoImage, IoLayers } from "react-icons/io5"; import { Popover, ColorPicker } from "../../components/"; import { FaSortAlphaDown, FaFilter } from "react-icons/fa"; import { MdDelete, MdAccessTimeFilled } from "react-icons/md"; -import { IColorPallete, IColor, IColorStats, IFrame, ILayer } from "./"; +import { IColorPallete, IColor, IColorStats, IFrame, ILayer } from "../../types"; interface IProps { @@ -24,6 +24,7 @@ interface IProps { setActiveColorPallete: (colorPallete: IColorPallete | any, colorPalettesOverride?: IColorPallete[]) => void; } + export function Colors(props: IProps) { const data = useColors(props); @@ -154,6 +155,23 @@ export function Colors(props: IProps) { ))} + + {/*
+ {data.mostRecentColors.map((_, i) => { + let index = data.mostRecentColors.length - 1 - i; + let recentColor = data.mostRecentColors[index]; + + return ( +
props.setActiveColor(recentColor)} + style={{ + background: `rgb(${recentColor.r}, ${recentColor.g}, ${recentColor.b})` + }}> +
+ ) + })} +
*/} ) } diff --git a/src/pages/Home/Frames.tsx b/src/pages/Home/Frames.tsx index bf03b01..c6ad44e 100644 --- a/src/pages/Home/Frames.tsx +++ b/src/pages/Home/Frames.tsx @@ -1,24 +1,22 @@ -import React, { useState, useEffect } from 'react'; -import { ICanvas, ILayer, IFrame, IPreview } from './'; +import { Preview } from './Preview'; import { IoPlay, IoStop, IoCopy } from "react-icons/io5"; +import React, { useState, useEffect, useReducer } from 'react'; +import { ICanvas, ILayer, IFrame, IPreview } from '../../types'; +import { initReducerState, reducer, useCanvas } from '../../utils'; import { BsFillCaretLeftFill, BsFillCaretRightFill } from "react-icons/bs"; import { MdAddPhotoAlternate, MdDelete, MdLayers, MdLayersClear } from "react-icons/md"; -import { Preview } from './Preview'; interface IProps { frames: IFrame[]; - canvas?: ICanvas; - preview: IPreview; activeFrame: IFrame; activeLayer: ILayer; - defaultCanvasSize: number; setFrames: React.Dispatch>; - setPreview: React.Dispatch>; setActiveFrame: (frame: IFrame, frames?: IFrame[]) => void; setActiveLayer: (layer: ILayer, frame?: IFrame, frames?: IFrame[]) => void; } + export function Frames(props: IProps) { const data = useLayers(props); @@ -27,7 +25,7 @@ export function Frames(props: IProps) {