feat: add switch drag

This commit is contained in:
Huh Hyeongjun 2025-02-24 13:35:42 +09:00
parent b461b32aa8
commit b56ea5ee98
6 changed files with 164 additions and 6 deletions

51
package-lock.json generated
View File

@ -11,12 +11,15 @@
"@emotion/react": "^11.14.0",
"@react-three/fiber": "^9.0.4",
"@types/three": "^0.173.0",
"leaflet": "^1.9.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-leaflet": "^5.0.0",
"three": "^0.173.0"
},
"devDependencies": {
"@eslint/js": "^9.19.0",
"@types/leaflet": "^1.9.16",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^4.3.4",
@ -1165,6 +1168,17 @@
"node": ">= 8"
}
},
"node_modules/@react-leaflet/core": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz",
"integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==",
"license": "Hippocratic-2.1",
"peerDependencies": {
"leaflet": "^1.9.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
}
},
"node_modules/@react-three/fiber": {
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.0.4.tgz",
@ -1539,6 +1553,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/geojson": {
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@ -1546,6 +1567,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/leaflet": {
"version": "1.9.16",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.16.tgz",
"integrity": "sha512-wzZoyySUxkgMZ0ihJ7IaUIblG8Rdc8AbbZKLneyn+QjYsj5q1QU7TEKYqwTr10BGSzY5LI7tJk9Ifo+mEjdFRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/parse-json": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
@ -2889,6 +2920,12 @@
"json-buffer": "3.0.1"
}
},
"node_modules/leaflet": {
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
"license": "BSD-2-Clause"
},
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@ -3255,6 +3292,20 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/react-leaflet": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz",
"integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==",
"license": "Hippocratic-2.1",
"dependencies": {
"@react-leaflet/core": "^3.0.0"
},
"peerDependencies": {
"leaflet": "^1.9.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
}
},
"node_modules/react-reconciler": {
"version": "0.31.0",
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.31.0.tgz",

View File

@ -13,12 +13,15 @@
"@emotion/react": "^11.14.0",
"@react-three/fiber": "^9.0.4",
"@types/three": "^0.173.0",
"leaflet": "^1.9.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-leaflet": "^5.0.0",
"three": "^0.173.0"
},
"devDependencies": {
"@eslint/js": "^9.19.0",
"@types/leaflet": "^1.9.16",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^4.3.4",

View File

@ -0,0 +1,96 @@
import React, { useEffect, useState } from "react";
import {
MapContainer,
Rectangle,
TileLayer,
useMapEvents,
} from "react-leaflet";
import L, { LatLng, LatLngBounds } from "leaflet";
import "leaflet/dist/leaflet.css";
import { css } from "@emotion/react";
function RectangleSelector({ isDrag = true }: { isDrag: boolean }) {
const [bounds, setBounds] = useState<LatLngBounds | null>(null);
const [firstPoint, setFirstPoint] = useState<LatLng | null>(null);
useEffect(() => {
if (isDrag) {
map.dragging.enable();
} else {
map.dragging.disable();
}
}, [isDrag]);
const map = useMapEvents({
mousedown(e) {
if (isDrag == false) {
setFirstPoint(e.latlng);
}
},
mousemove(e) {
if (firstPoint) {
setBounds(new L.LatLngBounds(firstPoint, e.latlng));
}
},
mouseup(e) {
if (firstPoint) {
setBounds(new L.LatLngBounds(firstPoint, e.latlng));
console.log(firstPoint, e.latlng);
setFirstPoint(null);
}
},
});
return bounds ? (
<Rectangle bounds={bounds} pathOptions={{ color: "blue" }} />
) : null;
}
export function MapComponent() {
const [isDrag, setIsDrag] = useState(true);
const handleClickSwitchDrag = () => {
setIsDrag(!isDrag);
};
return (
<div
css={css({
position: "relative",
})}
>
<button
css={css({
position: "absolute",
zIndex: 9999,
right: "1rem",
top: "1rem",
color: "#000000",
backgroundColor: "#ffffff96",
backdropFilter: "blur(8px)",
border: "none",
padding: "0.5rem 1rem",
borderRadius: "8px",
outline: "rgba(240, 240, 244, 0.51) solid 0.1rem",
cursor: "pointer",
transition: "0.2s",
":hover": {
backgroundColor: "#ebeef0c2",
},
})}
onClick={handleClickSwitchDrag}
>
{isDrag ? "Disable" : "Enable"} Drag
</button>
<MapContainer
center={[36.48656, 127.29064]}
zoom={13}
style={{ height: "70vh", width: "100%" }}
>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<RectangleSelector isDrag={isDrag} />
</MapContainer>
</div>
);
}

View File

@ -10,6 +10,10 @@ body,
BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI",
"Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", sans-serif;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
* {

View File

@ -1,19 +1,23 @@
import { css } from "@emotion/react";
import { Space } from "../space/Space";
import { Space } from "../three/Space";
import { FullscreenModal } from "../components/FullscreenModal";
import { Title } from "@/components/text/Title";
import { Description } from "@/components/text/Description";
import { Column } from "@/components/flex/Column";
import { MapComponent } from "@/components/map/SelectMap";
function App() {
return (
<div css={css({ height: "100%", width: "100%" })}>
<FullscreenModal isOpen={true}>
<Column gap="0.5rem">
<Title>Generate 3d map</Title>
<Description>
Tools to create 3D maps based on maps and export them in 3D format
</Description>
<Column gap="1rem">
<Column gap="0.5rem">
<Title>Generate 3d map</Title>
<Description>
Tools to create 3D maps based on maps and export them in 3D format
</Description>
</Column>
<MapComponent></MapComponent>
</Column>
</FullscreenModal>
<Space />