feat: add basic konva implementation
This commit is contained in:
parent
a536253511
commit
fd71c506c6
4 changed files with 182 additions and 30 deletions
|
|
@ -13,11 +13,14 @@
|
|||
"@radix-ui/react-menubar": "^1.1.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"konva": "^9.3.16",
|
||||
"lucide-react": "^0.464.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-konva": "^18.2.10",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"use-image": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.15.0",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ importers:
|
|||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
konva:
|
||||
specifier: ^9.3.16
|
||||
version: 9.3.16
|
||||
lucide-react:
|
||||
specifier: ^0.464.0
|
||||
version: 0.464.0(react@18.3.1)
|
||||
|
|
@ -26,12 +29,18 @@ importers:
|
|||
react-dom:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1(react@18.3.1)
|
||||
react-konva:
|
||||
specifier: ^18.2.10
|
||||
version: 18.2.10(konva@9.3.16)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
tailwind-merge:
|
||||
specifier: ^2.5.5
|
||||
version: 2.5.5
|
||||
tailwindcss-animate:
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(tailwindcss@3.4.16)
|
||||
use-image:
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
devDependencies:
|
||||
'@eslint/js':
|
||||
specifier: ^9.15.0
|
||||
|
|
@ -802,6 +811,9 @@ packages:
|
|||
'@types/react-dom@18.3.1':
|
||||
resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==}
|
||||
|
||||
'@types/react-reconciler@0.28.8':
|
||||
resolution: {integrity: sha512-SN9c4kxXZonFhbX4hJrZy37yw9e7EIxcpHCxQv5JUS18wDE5ovkQKlqQEkufdJCCMfuI9BnjUJvhYeJ9x5Ra7g==}
|
||||
|
||||
'@types/react@18.3.12':
|
||||
resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==}
|
||||
|
||||
|
|
@ -1237,6 +1249,11 @@ packages:
|
|||
isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
its-fine@1.2.5:
|
||||
resolution: {integrity: sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==}
|
||||
peerDependencies:
|
||||
react: '>=18.0'
|
||||
|
||||
jackspeak@3.4.3:
|
||||
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
|
||||
|
||||
|
|
@ -1273,6 +1290,9 @@ packages:
|
|||
keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
|
||||
konva@9.3.16:
|
||||
resolution: {integrity: sha512-qa47cefGDDHzkToGRGDsy24f/Njrz7EHP56jQ8mlDcjAPO7vkfTDeoBDIfmF7PZtpfzDdooafQmEUJMDU2F7FQ==}
|
||||
|
||||
levn@0.4.1:
|
||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
|
@ -1464,6 +1484,19 @@ packages:
|
|||
peerDependencies:
|
||||
react: ^18.3.1
|
||||
|
||||
react-konva@18.2.10:
|
||||
resolution: {integrity: sha512-ohcX1BJINL43m4ynjZ24MxFI1syjBdrXhqVxYVDw2rKgr3yuS0x/6m1Y2Z4sl4T/gKhfreBx8KHisd0XC6OT1g==}
|
||||
peerDependencies:
|
||||
konva: ^8.0.1 || ^7.2.5 || ^9.0.0
|
||||
react: '>=18.0.0'
|
||||
react-dom: '>=18.0.0'
|
||||
|
||||
react-reconciler@0.29.2:
|
||||
resolution: {integrity: sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
peerDependencies:
|
||||
react: ^18.3.1
|
||||
|
||||
react-refresh@0.14.2:
|
||||
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -1667,6 +1700,12 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
|
||||
use-image@1.1.1:
|
||||
resolution: {integrity: sha512-n4YO2k8AJG/BcDtxmBx8Aa+47kxY5m335dJiCQA5tTeVU4XdhrhqR6wT0WISRXwdMEOv5CSjqekDZkEMiiWaYQ==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
|
||||
use-sidecar@1.1.2:
|
||||
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -2380,6 +2419,10 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/react': 18.3.12
|
||||
|
||||
'@types/react-reconciler@0.28.8':
|
||||
dependencies:
|
||||
'@types/react': 18.3.12
|
||||
|
||||
'@types/react@18.3.12':
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.13
|
||||
|
|
@ -2846,6 +2889,11 @@ snapshots:
|
|||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
its-fine@1.2.5(react@18.3.1):
|
||||
dependencies:
|
||||
'@types/react-reconciler': 0.28.8
|
||||
react: 18.3.1
|
||||
|
||||
jackspeak@3.4.3:
|
||||
dependencies:
|
||||
'@isaacs/cliui': 8.0.2
|
||||
|
|
@ -2874,6 +2922,8 @@ snapshots:
|
|||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
|
||||
konva@9.3.16: {}
|
||||
|
||||
levn@0.4.1:
|
||||
dependencies:
|
||||
prelude-ls: 1.2.1
|
||||
|
|
@ -3033,6 +3083,22 @@ snapshots:
|
|||
react: 18.3.1
|
||||
scheduler: 0.23.2
|
||||
|
||||
react-konva@18.2.10(konva@9.3.16)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@types/react-reconciler': 0.28.8
|
||||
its-fine: 1.2.5(react@18.3.1)
|
||||
konva: 9.3.16
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-reconciler: 0.29.2(react@18.3.1)
|
||||
scheduler: 0.23.2
|
||||
|
||||
react-reconciler@0.29.2(react@18.3.1):
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
react: 18.3.1
|
||||
scheduler: 0.23.2
|
||||
|
||||
react-refresh@0.14.2: {}
|
||||
|
||||
react-remove-scroll-bar@2.3.6(@types/react@18.3.12)(react@18.3.1):
|
||||
|
|
@ -3258,6 +3324,11 @@ snapshots:
|
|||
optionalDependencies:
|
||||
'@types/react': 18.3.12
|
||||
|
||||
use-image@1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
use-sidecar@1.1.2(@types/react@18.3.12)(react@18.3.1):
|
||||
dependencies:
|
||||
detect-node-es: 1.1.0
|
||||
|
|
|
|||
BIN
public/blocks/programmer-art/stone.png
Normal file
BIN
public/blocks/programmer-art/stone.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 223 B |
136
src/App.tsx
136
src/App.tsx
|
|
@ -1,38 +1,116 @@
|
|||
import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger } from "@/components/ui/menubar"
|
||||
import ThemeChanger from "./components/menubar/theme-changer"
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Image, Layer, Stage, Text } from "react-konva";
|
||||
|
||||
import {
|
||||
Menubar,
|
||||
MenubarContent,
|
||||
MenubarItem,
|
||||
MenubarMenu,
|
||||
MenubarSeparator,
|
||||
MenubarSub,
|
||||
MenubarSubContent,
|
||||
MenubarSubTrigger,
|
||||
MenubarTrigger,
|
||||
} from "@/components/ui/menubar";
|
||||
|
||||
import ThemeChanger from "./components/menubar/theme-changer";
|
||||
import useImage from "use-image";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<Menubar>
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>File</MenubarTrigger>
|
||||
<MenubarContent>
|
||||
<MenubarItem>Open Schematic</MenubarItem>
|
||||
<MenubarItem>Open Image</MenubarItem>
|
||||
const stageContainerRef = useRef<HTMLDivElement>(null);
|
||||
const stageRef = useRef(null);
|
||||
|
||||
<MenubarSeparator />
|
||||
const [stageSize, setStageSize] = useState({
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
|
||||
<MenubarSub>
|
||||
<MenubarSubTrigger>Export to...</MenubarSubTrigger>
|
||||
<MenubarSubContent>
|
||||
<MenubarItem>.schematic</MenubarItem>
|
||||
<MenubarItem>.litematic</MenubarItem>
|
||||
<MenubarItem>image</MenubarItem>
|
||||
</MenubarSubContent>
|
||||
</MenubarSub>
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
const [stageScale, setStageScale] = useState(1);
|
||||
const [stageCoords, setStageCoords] = useState({
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>More</MenubarTrigger>
|
||||
<MenubarContent>
|
||||
const [image] = useImage("/blocks/programmer-art/stone.png");
|
||||
|
||||
const onWheel = (e) => {
|
||||
const stage = e.target.getStage();
|
||||
const oldScale = stage.scaleX();
|
||||
const pointer = stage.getPointerPosition();
|
||||
|
||||
const mousePoint = {
|
||||
x: (pointer.x - stage.x()) / oldScale,
|
||||
y: (pointer.y - stage.y()) / oldScale,
|
||||
};
|
||||
|
||||
const newScale = e.evt.deltaY < 0 ? oldScale * 1.05 : oldScale / 1.05;
|
||||
|
||||
setStageScale(newScale);
|
||||
setStageCoords({
|
||||
x: pointer.x - mousePoint.x * newScale,
|
||||
y: pointer.y - mousePoint.y * newScale,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (stageContainerRef.current && stageRef.current) {
|
||||
setStageSize({
|
||||
width: stageContainerRef.current.offsetWidth,
|
||||
height: stageContainerRef.current.offsetHeight,
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<main className="h-screen grid grid-rows-[2.5rem_1fr]">
|
||||
<Menubar>
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>File</MenubarTrigger>
|
||||
<MenubarContent>
|
||||
<MenubarItem>Open Schematic</MenubarItem>
|
||||
<MenubarItem>Open Image</MenubarItem>
|
||||
|
||||
<MenubarSeparator />
|
||||
|
||||
<MenubarSub>
|
||||
<MenubarSubTrigger>Export to...</MenubarSubTrigger>
|
||||
<MenubarSubContent>
|
||||
<MenubarItem>.schematic</MenubarItem>
|
||||
<MenubarItem>.litematic</MenubarItem>
|
||||
<MenubarItem>image</MenubarItem>
|
||||
</MenubarSubContent>
|
||||
</MenubarSub>
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>More</MenubarTrigger>
|
||||
<MenubarContent>
|
||||
<ThemeChanger />
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
</Menubar>
|
||||
</>
|
||||
)
|
||||
</MenubarMenu>
|
||||
</Menubar>
|
||||
|
||||
<div ref={stageContainerRef} className="w-full h-full">
|
||||
<Stage
|
||||
width={stageSize.width}
|
||||
height={stageSize.height}
|
||||
draggable
|
||||
ref={stageRef}
|
||||
x={stageCoords.x}
|
||||
y={stageCoords.y}
|
||||
scaleX={stageScale}
|
||||
scaleY={stageScale}
|
||||
onWheel={onWheel}
|
||||
>
|
||||
<Layer>
|
||||
<Text text="test" fontSize={20} fill="white" />
|
||||
<Image image={image} />
|
||||
</Layer>
|
||||
</Stage>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default App
|
||||
export default App;
|
||||
|
|
|
|||
Loading…
Reference in a new issue