@mint-ui/map 1.2.0-test.10 → 1.2.0-test.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/mint-map/core/MintMapController.d.ts +1 -0
- package/dist/components/mint-map/core/MintMapCore.js +1 -2
- package/dist/components/mint-map/core/advanced/woongCanvas/WoongCanvasLayer.d.ts +162 -0
- package/dist/components/mint-map/core/advanced/woongCanvas/{WoongKonvaMarker.js → WoongCanvasLayer.js} +136 -82
- package/dist/components/mint-map/core/advanced/woongCanvas/index.d.ts +2 -2
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/renderer.d.ts +59 -0
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/renderer.js +226 -0
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/types.d.ts +95 -1
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/utils.d.ts +17 -0
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/utils.js +42 -0
- package/dist/components/mint-map/core/wrapper/MapMarkerWrapper.js +22 -1
- package/dist/components/mint-map/google/GoogleMintMapController.d.ts +1 -0
- package/dist/components/mint-map/google/GoogleMintMapController.js +6 -1
- package/dist/components/mint-map/kakao/KakaoMintMapController.d.ts +1 -0
- package/dist/components/mint-map/kakao/KakaoMintMapController.js +6 -1
- package/dist/components/mint-map/naver/NaverMintMapController.d.ts +3 -0
- package/dist/components/mint-map/naver/NaverMintMapController.js +39 -4
- package/dist/index.es.js +458 -83
- package/dist/index.js +4 -2
- package/dist/index.umd.js +460 -83
- package/package.json +1 -1
- package/dist/components/mint-map/core/advanced/woongCanvas/ClusterMarker.d.ts +0 -11
- package/dist/components/mint-map/core/advanced/woongCanvas/WoongKonvaMarker.d.ts +0 -54
|
@@ -24,6 +24,7 @@ export declare abstract class MintMapController {
|
|
|
24
24
|
abstract updateMarker(marker: Marker, options: MarkerOptions): void;
|
|
25
25
|
abstract clearDrawable(drawable: Drawable): boolean;
|
|
26
26
|
abstract markerToTheTop(marker: Marker): void;
|
|
27
|
+
abstract restoreMarkerZIndex(marker: Marker): void;
|
|
27
28
|
abstract isMapDragged(): boolean;
|
|
28
29
|
abstract setMapDragged(value: boolean): void;
|
|
29
30
|
abstract setMarkerZIndex(marker: Marker, zIndex: number): void;
|
|
@@ -9,8 +9,7 @@ var MapTypes = require('../types/MapTypes.js');
|
|
|
9
9
|
var MintMapProvider = require('./provider/MintMapProvider.js');
|
|
10
10
|
var MintMapCore_module = require('./MintMapCore.module.scss.js');
|
|
11
11
|
require('./advanced/woongCanvas/shared/types.js');
|
|
12
|
-
require('
|
|
13
|
-
require('../types/MapEventTypes.js');
|
|
12
|
+
require('./advanced/woongCanvas/shared/utils.js');
|
|
14
13
|
var context = require('./advanced/woongCanvas/shared/context.js');
|
|
15
14
|
require('./advanced/woongCanvas/shared/performance.js');
|
|
16
15
|
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { MarkerOptions } from "../../../types";
|
|
3
|
+
import { KonvaCanvasMarkerData, CanvasDataType, CustomRenderBase, CustomRenderAnimation, CustomRenderEvent } from "./shared";
|
|
4
|
+
export { KonvaMarkerProvider, LRUCache, SpatialHashGrid, CanvasDataType } from "./shared";
|
|
5
|
+
export type { KonvaCanvasMarkerOption, Paths, KonvaCanvasMarkerData, CustomRenderBase, CustomRenderAnimation, CustomRenderEvent, RenderUtils, RenderBaseParams, RenderAnimationParams, RenderEventParams } from "./shared";
|
|
6
|
+
/**
|
|
7
|
+
* 공통 Props (MARKER와 POLYGON 모두 사용)
|
|
8
|
+
*/
|
|
9
|
+
interface WoongCanvasLayerBaseProps<T> extends Pick<MarkerOptions, 'zIndex' | 'anchor' | 'visible'> {
|
|
10
|
+
/** 렌더링할 데이터 배열 (마커 또는 폴리곤) */
|
|
11
|
+
data: KonvaCanvasMarkerData<T>[];
|
|
12
|
+
/** 마커 클릭 시 호출되는 콜백 (선택) */
|
|
13
|
+
onClick?: (payload: KonvaCanvasMarkerData<T>, selectedIds: Set<string>) => void;
|
|
14
|
+
/** 마커에 마우스 오버 시 호출되는 콜백 (선택) */
|
|
15
|
+
onMouseOver?: (payload: KonvaCanvasMarkerData<T>) => void;
|
|
16
|
+
/** 마커에서 마우스 아웃 시 호출되는 콜백 (선택) */
|
|
17
|
+
onMouseOut?: (payload: KonvaCanvasMarkerData<T>) => void;
|
|
18
|
+
/** 다중 선택 활성화 여부 (기본: false) */
|
|
19
|
+
enableMultiSelect?: boolean;
|
|
20
|
+
/** hover 시 마커를 최상단으로 표시 (기본: false) */
|
|
21
|
+
topOnHover?: boolean;
|
|
22
|
+
/** 뷰포트 컬링 활성화 여부 (기본: true) */
|
|
23
|
+
enableViewportCulling?: boolean;
|
|
24
|
+
/** 뷰포트 컬링 여유 공간 (기본: 100px) */
|
|
25
|
+
cullingMargin?: number;
|
|
26
|
+
/** LRU 캐시 최대 크기 (기본: 10000) */
|
|
27
|
+
maxCacheSize?: number;
|
|
28
|
+
/** 외부에서 제어하는 선택된 항목 배열 (선택) */
|
|
29
|
+
selectedItems?: KonvaCanvasMarkerData<T>[];
|
|
30
|
+
/** 외부에서 전달된 단일 선택 아이템 (특별한 효과용) */
|
|
31
|
+
selectedItem?: KonvaCanvasMarkerData<T> | null;
|
|
32
|
+
/** 상호작용 비활성화 (hover, click 등 모든 이벤트 차단) */
|
|
33
|
+
disableInteraction?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* MARKER 타입 Props - 커스텀 렌더링 필수
|
|
37
|
+
*/
|
|
38
|
+
interface WoongCanvasLayerPropsForMarker<T> extends WoongCanvasLayerBaseProps<T> {
|
|
39
|
+
/** 데이터 타입 */
|
|
40
|
+
dataType: CanvasDataType.MARKER;
|
|
41
|
+
/** Base Layer 렌더링 함수 (필수) */
|
|
42
|
+
renderBase: CustomRenderBase<T>;
|
|
43
|
+
/** Animation Layer 렌더링 함수 (선택, 애니메이션용) */
|
|
44
|
+
renderAnimation?: CustomRenderAnimation<T>;
|
|
45
|
+
/** Event Layer 렌더링 함수 (선택) */
|
|
46
|
+
renderEvent?: CustomRenderEvent<T>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* POLYGON 타입 Props - 스타일 속성으로 내부 처리
|
|
50
|
+
*/
|
|
51
|
+
interface WoongCanvasLayerPropsForPolygon<T> extends WoongCanvasLayerBaseProps<T> {
|
|
52
|
+
/** 데이터 타입 */
|
|
53
|
+
dataType: CanvasDataType.POLYGON;
|
|
54
|
+
/** 기본 폴리곤 채우기 색상 (필수) */
|
|
55
|
+
baseFillColor: string;
|
|
56
|
+
/** 기본 폴리곤 테두리 색상 (필수) */
|
|
57
|
+
baseStrokeColor: string;
|
|
58
|
+
/** 기본 폴리곤 테두리 두께 (필수) */
|
|
59
|
+
baseLineWidth: number;
|
|
60
|
+
/** 선택된 폴리곤 채우기 색상 (필수) */
|
|
61
|
+
selectedFillColor: string;
|
|
62
|
+
/** 선택된 폴리곤 테두리 색상 (필수) */
|
|
63
|
+
selectedStrokeColor: string;
|
|
64
|
+
/** 선택된 폴리곤 테두리 두께 (필수) */
|
|
65
|
+
selectedLineWidth: number;
|
|
66
|
+
/** 마지막 선택된 폴리곤 채우기 색상 (선택, 기본값: selectedFillColor) */
|
|
67
|
+
activeFillColor?: string;
|
|
68
|
+
/** 마지막 선택된 폴리곤 테두리 색상 (선택, 기본값: selectedStrokeColor) */
|
|
69
|
+
activeStrokeColor?: string;
|
|
70
|
+
/** 마지막 선택된 폴리곤 테두리 두께 (선택, 기본값: selectedLineWidth) */
|
|
71
|
+
activeLineWidth?: number;
|
|
72
|
+
/** Hover 시 폴리곤 채우기 색상 (선택, 기본값: selectedFillColor) */
|
|
73
|
+
hoveredFillColor?: string;
|
|
74
|
+
/** Hover 시 폴리곤 테두리 색상 (선택, 기본값: selectedStrokeColor) */
|
|
75
|
+
hoveredStrokeColor?: string;
|
|
76
|
+
/** Hover 시 폴리곤 테두리 두께 (선택, 기본값: selectedLineWidth) */
|
|
77
|
+
hoveredLineWidth?: number;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 최종 Props 타입 - Discriminated Union
|
|
81
|
+
*/
|
|
82
|
+
export declare type WoongCanvasLayerProps<T> = WoongCanvasLayerPropsForMarker<T> | WoongCanvasLayerPropsForPolygon<T>;
|
|
83
|
+
/**
|
|
84
|
+
* 🚀 WoongCanvasLayer - Konva 기반 초고성능 마커/폴리곤 렌더링 컴포넌트
|
|
85
|
+
*
|
|
86
|
+
* ## 📌 주요 특징
|
|
87
|
+
* - **30,000개 이상의 폴리곤/마커를 60fps로 렌더링**
|
|
88
|
+
* - **Multi-Layer 아키텍처**: Base/Animation/Event 레이어 분리
|
|
89
|
+
* - **Spatial Hash Grid**: O(1) 수준의 빠른 Hit Test
|
|
90
|
+
* - **LRU 캐시**: 좌표 변환 결과 캐싱으로 성능 최적화
|
|
91
|
+
* - **Viewport Culling**: 화면에 보이는 영역만 렌더링
|
|
92
|
+
* - **Discriminated Union Props**: 타입 안전한 MARKER/POLYGON 모드
|
|
93
|
+
*
|
|
94
|
+
* ## 🎯 사용 방법
|
|
95
|
+
*
|
|
96
|
+
* ### 1️⃣ POLYGON 모드 (자동 렌더링)
|
|
97
|
+
* ```tsx
|
|
98
|
+
* <WoongCanvasLayer
|
|
99
|
+
* dataType={CanvasDataType.POLYGON}
|
|
100
|
+
* data={polygons}
|
|
101
|
+
* baseFillColor="rgba(255, 100, 100, 0.5)"
|
|
102
|
+
* baseStrokeColor="rgba(200, 50, 50, 0.8)"
|
|
103
|
+
* baseLineWidth={2}
|
|
104
|
+
* selectedFillColor="rgba(255, 193, 7, 0.7)"
|
|
105
|
+
* selectedStrokeColor="rgba(255, 152, 0, 1)"
|
|
106
|
+
* selectedLineWidth={4}
|
|
107
|
+
* hoveredFillColor="rgba(100, 150, 255, 0.8)" // optional
|
|
108
|
+
* hoveredStrokeColor="rgba(0, 100, 200, 1)" // optional
|
|
109
|
+
* hoveredLineWidth={3} // optional
|
|
110
|
+
* enableMultiSelect={true}
|
|
111
|
+
* onClick={handleClick}
|
|
112
|
+
* />
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* ### 2️⃣ MARKER 모드 (커스텀 렌더링)
|
|
116
|
+
* ```tsx
|
|
117
|
+
* <WoongCanvasLayer
|
|
118
|
+
* dataType={CanvasDataType.MARKER}
|
|
119
|
+
* data={markers}
|
|
120
|
+
* renderBase={renderMarkerBase} // required
|
|
121
|
+
* renderAnimation={renderMarkerAnimation} // optional
|
|
122
|
+
* renderEvent={renderMarkerEvent} // optional
|
|
123
|
+
* topOnHover={true}
|
|
124
|
+
* onClick={handleClick}
|
|
125
|
+
* />
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* ## 📊 데이터 형식
|
|
129
|
+
* ```typescript
|
|
130
|
+
* const data: KonvaCanvasMarkerData<T>[] = [
|
|
131
|
+
* {
|
|
132
|
+
* id: 'unique-id',
|
|
133
|
+
* position: new Position(lat, lng),
|
|
134
|
+
* // POLYGON: paths 필수
|
|
135
|
+
* paths: [[[lat, lng], [lat, lng], ...]],
|
|
136
|
+
* // MARKER: boxWidth/boxHeight 권장 (Hit Test 정확도)
|
|
137
|
+
* boxWidth: 60,
|
|
138
|
+
* boxHeight: 75,
|
|
139
|
+
* // 커스텀 데이터
|
|
140
|
+
* ...customData
|
|
141
|
+
* }
|
|
142
|
+
* ];
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
* ## ⚡ 성능 최적화 팁
|
|
146
|
+
* 1. **동적 boxWidth 계산**: `measureText()`로 실제 너비 계산 후 전달
|
|
147
|
+
* 2. **enableViewportCulling**: 대량 데이터 시 필수 (기본 true)
|
|
148
|
+
* 3. **selectedItems 외부 관리**: 상태를 외부에서 관리하여 리렌더링 최소화
|
|
149
|
+
* 4. **React.memo 최적화**: 컴포넌트가 자동으로 불필요한 리렌더링 방지
|
|
150
|
+
*
|
|
151
|
+
* @template T 마커/폴리곤 데이터의 추가 속성 타입
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* // 동적 boxWidth 계산 예시
|
|
155
|
+
* const tempCtx = document.createElement('canvas').getContext('2d');
|
|
156
|
+
* tempCtx.font = 'bold 15px Arial';
|
|
157
|
+
* const boxWidth = Math.max(60, tempCtx.measureText(text).width + 20);
|
|
158
|
+
*
|
|
159
|
+
* @see {@link https://github.com/your-repo/docs/WoongCanvasLayer.md} 전체 문서
|
|
160
|
+
*/
|
|
161
|
+
declare const WoongCanvasLayer: <T>(props: WoongCanvasLayerProps<T>) => React.ReactPortal;
|
|
162
|
+
export default WoongCanvasLayer;
|
|
@@ -14,6 +14,7 @@ var types = require('./shared/types.js');
|
|
|
14
14
|
var utils = require('./shared/utils.js');
|
|
15
15
|
var context = require('./shared/context.js');
|
|
16
16
|
var performance = require('./shared/performance.js');
|
|
17
|
+
var renderer = require('./shared/renderer.js');
|
|
17
18
|
|
|
18
19
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
19
20
|
|
|
@@ -23,42 +24,27 @@ var Konva__default = /*#__PURE__*/_interopDefaultLegacy(Konva);
|
|
|
23
24
|
// 메인 컴포넌트
|
|
24
25
|
// ============================================================================
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
_b = _a.enableMultiSelect,
|
|
48
|
-
enableMultiSelect = _b === void 0 ? false : _b,
|
|
49
|
-
_c = _a.topOnHover,
|
|
50
|
-
topOnHover = _c === void 0 ? false : _c,
|
|
51
|
-
_d = _a.enableViewportCulling,
|
|
52
|
-
enableViewportCulling = _d === void 0 ? true : _d,
|
|
53
|
-
_e = _a.cullingMargin,
|
|
54
|
-
cullingMargin = _e === void 0 ? performance.DEFAULT_CULLING_MARGIN : _e,
|
|
55
|
-
_f = _a.maxCacheSize,
|
|
56
|
-
maxCacheSize = _f === void 0 ? performance.DEFAULT_MAX_CACHE_SIZE : _f,
|
|
57
|
-
externalSelectedItems = _a.selectedItems,
|
|
58
|
-
externalSelectedItem = _a.selectedItem,
|
|
59
|
-
_g = _a.disableInteraction,
|
|
60
|
-
disableInteraction = _g === void 0 ? false : _g,
|
|
61
|
-
options = tslib.__rest(_a, ["markers", "dataType", "onClick", "onMouseOver", "onMouseOut", "renderBase", "renderAnimation", "renderEvent", "enableMultiSelect", "topOnHover", "enableViewportCulling", "cullingMargin", "maxCacheSize", "selectedItems", "selectedItem", "disableInteraction"]); // --------------------------------------------------------------------------
|
|
27
|
+
var WoongCanvasLayerComponent = function (props) {
|
|
28
|
+
var data = props.data,
|
|
29
|
+
dataType = props.dataType,
|
|
30
|
+
onClick = props.onClick,
|
|
31
|
+
onMouseOver = props.onMouseOver,
|
|
32
|
+
onMouseOut = props.onMouseOut,
|
|
33
|
+
_a = props.enableMultiSelect,
|
|
34
|
+
enableMultiSelect = _a === void 0 ? false : _a,
|
|
35
|
+
_b = props.topOnHover,
|
|
36
|
+
topOnHover = _b === void 0 ? false : _b,
|
|
37
|
+
_c = props.enableViewportCulling,
|
|
38
|
+
enableViewportCulling = _c === void 0 ? true : _c,
|
|
39
|
+
_d = props.cullingMargin,
|
|
40
|
+
cullingMargin = _d === void 0 ? performance.DEFAULT_CULLING_MARGIN : _d,
|
|
41
|
+
_e = props.maxCacheSize,
|
|
42
|
+
maxCacheSize = _e === void 0 ? performance.DEFAULT_MAX_CACHE_SIZE : _e,
|
|
43
|
+
externalSelectedItems = props.selectedItems,
|
|
44
|
+
externalSelectedItem = props.selectedItem,
|
|
45
|
+
_f = props.disableInteraction,
|
|
46
|
+
disableInteraction = _f === void 0 ? false : _f,
|
|
47
|
+
options = tslib.__rest(props, ["data", "dataType", "onClick", "onMouseOver", "onMouseOut", "enableMultiSelect", "topOnHover", "enableViewportCulling", "cullingMargin", "maxCacheSize", "selectedItems", "selectedItem", "disableInteraction"]); // --------------------------------------------------------------------------
|
|
62
48
|
// Hooks & Context
|
|
63
49
|
// --------------------------------------------------------------------------
|
|
64
50
|
|
|
@@ -83,9 +69,9 @@ var WoongKonvaMarkerComponent = function (_a) {
|
|
|
83
69
|
// Data Refs - 선택 및 Hover 상태 관리
|
|
84
70
|
// --------------------------------------------------------------------------
|
|
85
71
|
|
|
86
|
-
/**
|
|
72
|
+
/** data prop을 ref로 추적 (stale closure 방지, useEffect에서 동기화) */
|
|
87
73
|
|
|
88
|
-
var markersRef = React.useRef(
|
|
74
|
+
var markersRef = React.useRef(data); // --------------------------------------------------------------------------
|
|
89
75
|
// State Refs - 선택 및 Hover 상태 관리
|
|
90
76
|
// --------------------------------------------------------------------------
|
|
91
77
|
|
|
@@ -257,6 +243,7 @@ var WoongKonvaMarkerComponent = function (_a) {
|
|
|
257
243
|
};
|
|
258
244
|
/**
|
|
259
245
|
* 마커 좌표 변환 결과를 캐시하고 반환
|
|
246
|
+
*
|
|
260
247
|
* @param markerData 마커 데이터
|
|
261
248
|
* @returns 변환된 좌표 또는 null
|
|
262
249
|
*/
|
|
@@ -336,7 +323,7 @@ var WoongKonvaMarkerComponent = function (_a) {
|
|
|
336
323
|
}
|
|
337
324
|
}
|
|
338
325
|
}; // --------------------------------------------------------------------------
|
|
339
|
-
// 렌더링 함수
|
|
326
|
+
// 렌더링 함수 결정 (dataType에 따라)
|
|
340
327
|
// --------------------------------------------------------------------------
|
|
341
328
|
|
|
342
329
|
/**
|
|
@@ -351,6 +338,18 @@ var WoongKonvaMarkerComponent = function (_a) {
|
|
|
351
338
|
/** Base Layer에서 사용할 빈 Set (재사용) */
|
|
352
339
|
|
|
353
340
|
React.useRef(new Set());
|
|
341
|
+
/**
|
|
342
|
+
* 실제 사용할 렌더링 함수 결정
|
|
343
|
+
* - MARKER: 외부에서 전달받은 renderBase 사용 (필수)
|
|
344
|
+
* - POLYGON: renderer.ts의 팩토리 함수로 생성 (props 기반)
|
|
345
|
+
*/
|
|
346
|
+
|
|
347
|
+
var renderBase = dataType === types.CanvasDataType.MARKER ? props.renderBase : renderer.renderPolygonBase(props.baseFillColor, props.baseStrokeColor, props.baseLineWidth);
|
|
348
|
+
var renderAnimation = dataType === types.CanvasDataType.MARKER ? props.renderAnimation : undefined;
|
|
349
|
+
var renderEvent = dataType === types.CanvasDataType.MARKER ? props.renderEvent : function () {
|
|
350
|
+
var polygonProps = props;
|
|
351
|
+
return renderer.renderPolygonEvent(polygonProps.selectedFillColor, polygonProps.selectedStrokeColor, polygonProps.selectedLineWidth, polygonProps.activeFillColor, polygonProps.activeStrokeColor, polygonProps.activeLineWidth, polygonProps.hoveredFillColor, polygonProps.hoveredStrokeColor, polygonProps.hoveredLineWidth);
|
|
352
|
+
}();
|
|
354
353
|
/**
|
|
355
354
|
* Base 레이어 렌더링 (뷰포트 컬링 적용, 선택된 마커 제외)
|
|
356
355
|
*
|
|
@@ -715,11 +714,11 @@ var WoongKonvaMarkerComponent = function (_a) {
|
|
|
715
714
|
|
|
716
715
|
try {
|
|
717
716
|
var clickedOffset = controller.positionToOffset(event.param.position);
|
|
718
|
-
var
|
|
717
|
+
var data_1 = findData(clickedOffset);
|
|
719
718
|
|
|
720
|
-
if (
|
|
721
|
-
handleLocalClick(
|
|
722
|
-
onClick(
|
|
719
|
+
if (data_1) {
|
|
720
|
+
handleLocalClick(data_1);
|
|
721
|
+
onClick(data_1, selectedIdsRef.current);
|
|
723
722
|
}
|
|
724
723
|
} catch (error) {
|
|
725
724
|
console.error('[WoongKonvaMarker] handleClick error:', error);
|
|
@@ -996,13 +995,13 @@ var WoongKonvaMarkerComponent = function (_a) {
|
|
|
996
995
|
|
|
997
996
|
doRenderEvent();
|
|
998
997
|
}, [externalSelectedItem]); // --------------------------------------------------------------------------
|
|
999
|
-
// Lifecycle:
|
|
998
|
+
// Lifecycle: 데이터 변경 시 렌더링
|
|
1000
999
|
// --------------------------------------------------------------------------
|
|
1001
1000
|
|
|
1002
1001
|
React.useEffect(function () {
|
|
1003
1002
|
if (!stageRef.current) return; // markersRef 동기화
|
|
1004
1003
|
|
|
1005
|
-
markersRef.current =
|
|
1004
|
+
markersRef.current = data; // 데이터 변경 시 즉시 transform 제거 및 캐시 정리 (겹침 방지)
|
|
1006
1005
|
|
|
1007
1006
|
if (containerRef.current) {
|
|
1008
1007
|
containerRef.current.style.transform = '';
|
|
@@ -1019,18 +1018,18 @@ var WoongKonvaMarkerComponent = function (_a) {
|
|
|
1019
1018
|
/**
|
|
1020
1019
|
* 선택 상태 동기화 (최적화 버전)
|
|
1021
1020
|
*
|
|
1022
|
-
*
|
|
1021
|
+
* data가 변경되면 selectedItemsMapRef도 업데이트 필요
|
|
1023
1022
|
* (참조가 바뀌므로 기존 Map의 데이터는 stale 상태)
|
|
1024
1023
|
*
|
|
1025
|
-
* 🔥 중요: 화면 밖
|
|
1026
|
-
* - 현재
|
|
1024
|
+
* 🔥 중요: 화면 밖 데이터도 선택 상태 유지!
|
|
1025
|
+
* - 현재 data에 있으면 최신 데이터로 업데이트
|
|
1027
1026
|
* - 없으면 기존 selectedItemsMapRef의 데이터 유지
|
|
1028
1027
|
*
|
|
1029
|
-
* 최적화:
|
|
1030
|
-
* - O(전체
|
|
1028
|
+
* 최적화: data를 Map으로 먼저 변환하여 find() 순회 제거
|
|
1029
|
+
* - O(전체 데이터 수 + 선택된 개수) - 매우 효율적
|
|
1031
1030
|
*/
|
|
1032
1031
|
|
|
1033
|
-
var markersMap = new Map(
|
|
1032
|
+
var markersMap = new Map(data.map(function (m) {
|
|
1034
1033
|
return [m.id, m];
|
|
1035
1034
|
}));
|
|
1036
1035
|
var newSelectedItemsMap = new Map();
|
|
@@ -1053,40 +1052,107 @@ var WoongKonvaMarkerComponent = function (_a) {
|
|
|
1053
1052
|
selectedItemsMapRef.current = newSelectedItemsMap; // 즉시 렌더링
|
|
1054
1053
|
|
|
1055
1054
|
renderAllImmediate();
|
|
1056
|
-
}, [
|
|
1055
|
+
}, [data]);
|
|
1057
1056
|
return reactDom.createPortal(React__default["default"].createElement("div", {
|
|
1058
1057
|
ref: containerRef,
|
|
1059
|
-
style:
|
|
1058
|
+
style: {
|
|
1060
1059
|
position: 'absolute',
|
|
1061
1060
|
width: '100%',
|
|
1062
1061
|
height: '100%'
|
|
1063
|
-
}
|
|
1064
|
-
pointerEvents: 'none'
|
|
1065
|
-
})
|
|
1062
|
+
}
|
|
1066
1063
|
}), divElement);
|
|
1067
1064
|
};
|
|
1068
1065
|
/**
|
|
1069
|
-
*
|
|
1066
|
+
* 🚀 WoongCanvasLayer - Konva 기반 초고성능 마커/폴리곤 렌더링 컴포넌트
|
|
1067
|
+
*
|
|
1068
|
+
* ## 📌 주요 특징
|
|
1069
|
+
* - **30,000개 이상의 폴리곤/마커를 60fps로 렌더링**
|
|
1070
|
+
* - **Multi-Layer 아키텍처**: Base/Animation/Event 레이어 분리
|
|
1071
|
+
* - **Spatial Hash Grid**: O(1) 수준의 빠른 Hit Test
|
|
1072
|
+
* - **LRU 캐시**: 좌표 변환 결과 캐싱으로 성능 최적화
|
|
1073
|
+
* - **Viewport Culling**: 화면에 보이는 영역만 렌더링
|
|
1074
|
+
* - **Discriminated Union Props**: 타입 안전한 MARKER/POLYGON 모드
|
|
1075
|
+
*
|
|
1076
|
+
* ## 🎯 사용 방법
|
|
1077
|
+
*
|
|
1078
|
+
* ### 1️⃣ POLYGON 모드 (자동 렌더링)
|
|
1079
|
+
* ```tsx
|
|
1080
|
+
* <WoongCanvasLayer
|
|
1081
|
+
* dataType={CanvasDataType.POLYGON}
|
|
1082
|
+
* data={polygons}
|
|
1083
|
+
* baseFillColor="rgba(255, 100, 100, 0.5)"
|
|
1084
|
+
* baseStrokeColor="rgba(200, 50, 50, 0.8)"
|
|
1085
|
+
* baseLineWidth={2}
|
|
1086
|
+
* selectedFillColor="rgba(255, 193, 7, 0.7)"
|
|
1087
|
+
* selectedStrokeColor="rgba(255, 152, 0, 1)"
|
|
1088
|
+
* selectedLineWidth={4}
|
|
1089
|
+
* hoveredFillColor="rgba(100, 150, 255, 0.8)" // optional
|
|
1090
|
+
* hoveredStrokeColor="rgba(0, 100, 200, 1)" // optional
|
|
1091
|
+
* hoveredLineWidth={3} // optional
|
|
1092
|
+
* enableMultiSelect={true}
|
|
1093
|
+
* onClick={handleClick}
|
|
1094
|
+
* />
|
|
1095
|
+
* ```
|
|
1070
1096
|
*
|
|
1071
|
-
*
|
|
1072
|
-
*
|
|
1073
|
-
*
|
|
1097
|
+
* ### 2️⃣ MARKER 모드 (커스텀 렌더링)
|
|
1098
|
+
* ```tsx
|
|
1099
|
+
* <WoongCanvasLayer
|
|
1100
|
+
* dataType={CanvasDataType.MARKER}
|
|
1101
|
+
* data={markers}
|
|
1102
|
+
* renderBase={renderMarkerBase} // required
|
|
1103
|
+
* renderAnimation={renderMarkerAnimation} // optional
|
|
1104
|
+
* renderEvent={renderMarkerEvent} // optional
|
|
1105
|
+
* topOnHover={true}
|
|
1106
|
+
* onClick={handleClick}
|
|
1107
|
+
* />
|
|
1108
|
+
* ```
|
|
1074
1109
|
*
|
|
1075
|
-
*
|
|
1110
|
+
* ## 📊 데이터 형식
|
|
1111
|
+
* ```typescript
|
|
1112
|
+
* const data: KonvaCanvasMarkerData<T>[] = [
|
|
1113
|
+
* {
|
|
1114
|
+
* id: 'unique-id',
|
|
1115
|
+
* position: new Position(lat, lng),
|
|
1116
|
+
* // POLYGON: paths 필수
|
|
1117
|
+
* paths: [[[lat, lng], [lat, lng], ...]],
|
|
1118
|
+
* // MARKER: boxWidth/boxHeight 권장 (Hit Test 정확도)
|
|
1119
|
+
* boxWidth: 60,
|
|
1120
|
+
* boxHeight: 75,
|
|
1121
|
+
* // 커스텀 데이터
|
|
1122
|
+
* ...customData
|
|
1123
|
+
* }
|
|
1124
|
+
* ];
|
|
1125
|
+
* ```
|
|
1126
|
+
*
|
|
1127
|
+
* ## ⚡ 성능 최적화 팁
|
|
1128
|
+
* 1. **동적 boxWidth 계산**: `measureText()`로 실제 너비 계산 후 전달
|
|
1129
|
+
* 2. **enableViewportCulling**: 대량 데이터 시 필수 (기본 true)
|
|
1130
|
+
* 3. **selectedItems 외부 관리**: 상태를 외부에서 관리하여 리렌더링 최소화
|
|
1131
|
+
* 4. **React.memo 최적화**: 컴포넌트가 자동으로 불필요한 리렌더링 방지
|
|
1132
|
+
*
|
|
1133
|
+
* @template T 마커/폴리곤 데이터의 추가 속성 타입
|
|
1134
|
+
*
|
|
1135
|
+
* @example
|
|
1136
|
+
* // 동적 boxWidth 계산 예시
|
|
1137
|
+
* const tempCtx = document.createElement('canvas').getContext('2d');
|
|
1138
|
+
* tempCtx.font = 'bold 15px Arial';
|
|
1139
|
+
* const boxWidth = Math.max(60, tempCtx.measureText(text).width + 20);
|
|
1140
|
+
*
|
|
1141
|
+
* @see {@link https://github.com/your-repo/docs/WoongCanvasLayer.md} 전체 문서
|
|
1076
1142
|
*/
|
|
1077
1143
|
|
|
1078
1144
|
|
|
1079
|
-
var
|
|
1080
|
-
// 1.
|
|
1081
|
-
var
|
|
1082
|
-
var
|
|
1145
|
+
var WoongCanvasLayer = React__default["default"].memo(WoongCanvasLayerComponent, function (prevProps, nextProps) {
|
|
1146
|
+
// 1. data 비교
|
|
1147
|
+
var prevData = prevProps.data;
|
|
1148
|
+
var nextData = nextProps.data; // 참조가 같으면 스킵
|
|
1083
1149
|
|
|
1084
|
-
if (
|
|
1150
|
+
if (prevData !== nextData) {
|
|
1085
1151
|
// 길이가 다르면 변경됨
|
|
1086
|
-
if (
|
|
1152
|
+
if (prevData.length !== nextData.length) return false; // 각 데이터의 ID 비교
|
|
1087
1153
|
|
|
1088
|
-
for (var i = 0; i <
|
|
1089
|
-
if (
|
|
1154
|
+
for (var i = 0; i < prevData.length; i++) {
|
|
1155
|
+
if (prevData[i].id !== nextData[i].id) {
|
|
1090
1156
|
return false; // 변경됨 → 리렌더링
|
|
1091
1157
|
}
|
|
1092
1158
|
}
|
|
@@ -1101,18 +1167,6 @@ var WoongKonvaMarker = React__default["default"].memo(WoongKonvaMarkerComponent,
|
|
|
1101
1167
|
return false; // 변경됨 → 리렌더링
|
|
1102
1168
|
}
|
|
1103
1169
|
|
|
1104
|
-
if (prevProps.onClick !== nextProps.onClick) {
|
|
1105
|
-
return false; // 변경됨 → 리렌더링
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
if (prevProps.onMouseOver !== nextProps.onMouseOver) {
|
|
1109
|
-
return false; // 변경됨 → 리렌더링
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
if (prevProps.onMouseOut !== nextProps.onMouseOut) {
|
|
1113
|
-
return false; // 변경됨 → 리렌더링
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
1170
|
if (prevProps.disableInteraction !== nextProps.disableInteraction) {
|
|
1117
1171
|
return false; // 변경됨 → 리렌더링
|
|
1118
1172
|
}
|
|
@@ -1127,4 +1181,4 @@ Object.defineProperty(exports, 'CanvasDataType', {
|
|
|
1127
1181
|
exports.KonvaMarkerProvider = context.KonvaMarkerProvider;
|
|
1128
1182
|
exports.LRUCache = performance.LRUCache;
|
|
1129
1183
|
exports.SpatialHashGrid = performance.SpatialHashGrid;
|
|
1130
|
-
exports["default"] =
|
|
1184
|
+
exports["default"] = WoongCanvasLayer;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { default as
|
|
2
|
-
export * from "./
|
|
1
|
+
export { default as WoongCanvasLayer } from "./WoongCanvasLayer";
|
|
2
|
+
export * from "./WoongCanvasLayer";
|
|
3
3
|
export * from "./shared";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 폴리곤 렌더링 유틸리티
|
|
3
|
+
*
|
|
4
|
+
* 이 파일은 폴리곤 렌더링을 위한 헬퍼 함수와 팩토리 함수를 제공합니다.
|
|
5
|
+
*/
|
|
6
|
+
import { CustomRenderBase, CustomRenderEvent, KonvaCanvasMarkerData } from "./types";
|
|
7
|
+
/**
|
|
8
|
+
* 폴리곤 그리기 헬퍼 함수 (도넛 폴리곤 지원)
|
|
9
|
+
*/
|
|
10
|
+
export declare const drawPolygon: ({ ctx, polygonOffsets, isDonutPolygon, fillColor, strokeColor, lineWidth }: {
|
|
11
|
+
ctx: CanvasRenderingContext2D;
|
|
12
|
+
polygonOffsets: number[][][][];
|
|
13
|
+
isDonutPolygon: boolean;
|
|
14
|
+
fillColor: string;
|
|
15
|
+
strokeColor: string;
|
|
16
|
+
lineWidth: number;
|
|
17
|
+
}) => void;
|
|
18
|
+
/**
|
|
19
|
+
* 폴리곤 Base 렌더링 함수
|
|
20
|
+
*
|
|
21
|
+
* @param baseFillColor 기본 폴리곤 채우기 색상
|
|
22
|
+
* @param baseStrokeColor 기본 폴리곤 테두리 색상
|
|
23
|
+
* @param baseLineWidth 기본 폴리곤 테두리 두께
|
|
24
|
+
* @returns Base Layer 렌더링 함수
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const renderBase = renderPolygonBase(
|
|
28
|
+
* 'rgba(255, 100, 100, 0.5)',
|
|
29
|
+
* 'rgba(200, 50, 50, 0.8)',
|
|
30
|
+
* 2
|
|
31
|
+
* );
|
|
32
|
+
*/
|
|
33
|
+
export declare const renderPolygonBase: <T = any>(baseFillColor: string, baseStrokeColor: string, baseLineWidth: number) => CustomRenderBase<KonvaCanvasMarkerData<T>>;
|
|
34
|
+
/**
|
|
35
|
+
* 폴리곤 Event 렌더링 함수
|
|
36
|
+
*
|
|
37
|
+
* @param selectedFillColor 선택된 폴리곤 채우기 색상
|
|
38
|
+
* @param selectedStrokeColor 선택된 폴리곤 테두리 색상
|
|
39
|
+
* @param selectedLineWidth 선택된 폴리곤 테두리 두께
|
|
40
|
+
* @param activeFillColor 마지막 선택된 폴리곤 채우기 색상 (선택, 기본값: selectedFillColor)
|
|
41
|
+
* @param activeStrokeColor 마지막 선택된 폴리곤 테두리 색상 (선택, 기본값: selectedStrokeColor)
|
|
42
|
+
* @param activeLineWidth 마지막 선택된 폴리곤 테두리 두께 (선택, 기본값: selectedLineWidth)
|
|
43
|
+
* @param hoveredFillColor Hover 시 폴리곤 채우기 색상 (선택, 기본값: selectedFillColor)
|
|
44
|
+
* @param hoveredStrokeColor Hover 시 폴리곤 테두리 색상 (선택, 기본값: selectedStrokeColor)
|
|
45
|
+
* @param hoveredLineWidth Hover 시 폴리곤 테두리 두께 (선택, 기본값: selectedLineWidth)
|
|
46
|
+
* @returns Event Layer 렌더링 함수
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const renderEvent = renderPolygonEvent(
|
|
50
|
+
* 'rgba(255, 193, 7, 0.7)',
|
|
51
|
+
* 'rgba(255, 152, 0, 1)',
|
|
52
|
+
* 4,
|
|
53
|
+
* 'rgba(255, 0, 0, 0.8)',
|
|
54
|
+
* undefined,
|
|
55
|
+
* undefined,
|
|
56
|
+
* 'rgba(100, 150, 255, 0.8)'
|
|
57
|
+
* );
|
|
58
|
+
*/
|
|
59
|
+
export declare const renderPolygonEvent: <T = any>(selectedFillColor: string, selectedStrokeColor: string, selectedLineWidth: number, activeFillColor?: string, activeStrokeColor?: string, activeLineWidth?: number, hoveredFillColor?: string, hoveredStrokeColor?: string, hoveredLineWidth?: number) => CustomRenderEvent<KonvaCanvasMarkerData<T>>;
|