1
- import React , { useCallback , useEffect , useMemo , useRef } from 'react' ;
1
+ import React , { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
2
2
import PropTypes from 'prop-types' ;
3
3
import loadBMap , { initMapInfo } from '../../../../utils/map-utils' ;
4
4
import { appAvatarURL , baiduMapKey , googleMapKey , mediaUrl } from '../../../../utils/constants' ;
@@ -8,21 +8,27 @@ import { MAP_TYPE as MAP_PROVIDER } from '../../../../constants';
8
8
import { EVENT_BUS_TYPE , MAP_TYPE , STORAGE_MAP_CENTER_KEY , STORAGE_MAP_TYPE_KEY , STORAGE_MAP_ZOOM_KEY } from '../../../constants' ;
9
9
import { createBMapGeolocationControl , createBMapZoomControl } from './control' ;
10
10
import { customAvatarOverlay , customImageOverlay } from './overlay' ;
11
+ import ModalPortal from '../../../../components/modal-portal' ;
12
+ import ImageDialog from '../../../../components/dialog/image-dialog' ;
11
13
12
14
import './index.css' ;
13
15
14
16
const DEFAULT_POSITION = { lng : 104.195 , lat : 35.861 } ;
15
17
const DEFAULT_ZOOM = 4 ;
16
- const BATCH_SIZE = 500 ;
17
18
const MAX_ZOOM = 21 ;
18
19
const MIN_ZOOM = 3 ;
19
20
20
- const MapView = ( { images, onOpenCluster } ) => {
21
+ const MapView = ( { images } ) => {
22
+ const [ imageIndex , setImageIndex ] = useState ( 0 ) ;
23
+ const [ clusterLeaveIds , setClusterLeaveIds ] = useState ( [ ] ) ;
24
+
21
25
const mapInfo = useMemo ( ( ) => initMapInfo ( { baiduMapKey, googleMapKey } ) , [ ] ) ;
26
+ const clusterLeaves = useMemo ( ( ) => images . filter ( image => clusterLeaveIds . includes ( image . id ) ) , [ images , clusterLeaveIds ] ) ;
22
27
23
28
const mapRef = useRef ( null ) ;
24
29
const clusterRef = useRef ( null ) ;
25
30
const batchIndexRef = useRef ( 0 ) ;
31
+ const clickTimeoutRef = useRef ( null ) ;
26
32
27
33
const saveMapState = useCallback ( ( ) => {
28
34
if ( ! mapRef . current ) return ;
@@ -68,44 +74,57 @@ const MapView = ({ images, onOpenCluster }) => {
68
74
return { center : savedCenter , zoom : savedZoom } ;
69
75
} , [ ] ) ;
70
76
71
- const onClickMarker = useCallback ( ( e , markers ) => {
72
- saveMapState ( ) ;
73
- const imageIds = markers . map ( marker => marker . _id ) ;
74
- onOpenCluster ( imageIds ) ;
75
- } , [ onOpenCluster , saveMapState ] ) ;
76
-
77
- const renderMarkersBatch = useCallback ( ( ) => {
78
- if ( ! images . length || ! clusterRef . current ) return ;
79
-
80
- const startIndex = batchIndexRef . current * BATCH_SIZE ;
81
- const endIndex = Math . min ( startIndex + BATCH_SIZE , images . length ) ;
82
- const batchMarkers = [ ] ;
83
-
84
- for ( let i = startIndex ; i < endIndex ; i ++ ) {
85
- const image = images [ i ] ;
86
- const { lng, lat } = image ;
87
- const point = new window . BMapGL . Point ( lng , lat ) ;
88
- const marker = customImageOverlay ( point , image , {
89
- callback : ( e , markers ) => onClickMarker ( e , markers )
90
- } ) ;
91
- batchMarkers . push ( marker ) ;
92
- }
93
- clusterRef . current . addMarkers ( batchMarkers ) ;
94
-
95
- if ( endIndex < images . length ) {
96
- batchIndexRef . current += 1 ;
97
- setTimeout ( renderMarkersBatch , 20 ) ; // Schedule the next batch
98
- }
99
- } , [ images , onClickMarker ] ) ;
77
+ const getPoints = useCallback ( ( images ) => {
78
+ if ( ! window . Cluster || ! images ) return [ ] ;
79
+ return window . Cluster . pointTransformer ( images , ( data ) => ( {
80
+ point : [ data . location . lng , data . location . lat ] ,
81
+ properties : {
82
+ id : data . id ,
83
+ src : data . src ,
84
+ }
85
+ } ) ) ;
86
+ } , [ ] ) ;
100
87
101
88
const initializeCluster = useCallback ( ( ) => {
102
- if ( mapRef . current && ! clusterRef . current ) {
103
- clusterRef . current = new window . BMapLib . MarkerCluster ( mapRef . current , {
104
- callback : ( e , markers ) => onClickMarker ( e , markers ) ,
105
- maxZoom : 21 ,
106
- } ) ;
107
- }
108
- } , [ onClickMarker ] ) ;
89
+ clusterRef . current = new window . Cluster . View ( mapRef . current , {
90
+ clusterRadius : 80 ,
91
+ updateRealTime : true ,
92
+ fitViewOnClick : false ,
93
+ isAnimation : true ,
94
+ clusterMap : ( properties ) => ( { src : properties . src , id : properties . id } ) ,
95
+ clusterReduce : ( acc , properties ) => {
96
+ if ( ! acc . properties ) {
97
+ acc . properties = [ ] ;
98
+ }
99
+ acc . properties . push ( properties ) ;
100
+ } ,
101
+ renderClusterStyle : {
102
+ type : window . Cluster . ClusterRender . DOM ,
103
+ inject : ( props ) => customImageOverlay ( props ) ,
104
+ } ,
105
+ } ) ;
106
+
107
+ clusterRef . current . setData ( getPoints ( images ) ) ;
108
+
109
+ clusterRef . current . on ( window . Cluster . ClusterEvent . CLICK , ( element ) => {
110
+ if ( clickTimeoutRef . current ) {
111
+ clearTimeout ( clickTimeoutRef . current ) ;
112
+ clickTimeoutRef . current = null ;
113
+ return ;
114
+ } else {
115
+ clickTimeoutRef . current = setTimeout ( ( ) => {
116
+ let imageIds = [ ] ;
117
+ if ( element . isCluster ) {
118
+ imageIds = clusterRef . current . getLeaves ( element . id ) . map ( item => item . properties . id ) . filter ( Boolean ) ;
119
+ } else {
120
+ imageIds = [ element . properties . id ] ;
121
+ }
122
+ clickTimeoutRef . current = null ;
123
+ setClusterLeaveIds ( imageIds ) ;
124
+ } , 300 ) ;
125
+ }
126
+ } ) ;
127
+ } , [ images , getPoints ] ) ;
109
128
110
129
const renderBaiduMap = useCallback ( ( ) => {
111
130
if ( ! mapRef . current || ! window . BMapGL . Map ) return ;
@@ -141,8 +160,20 @@ const MapView = ({ images, onOpenCluster }) => {
141
160
initializeCluster ( ) ;
142
161
143
162
batchIndexRef . current = 0 ;
144
- renderMarkersBatch ( ) ;
145
- } , [ addMapController , initializeCluster , initializeUserMarker , renderMarkersBatch , getBMapType , loadMapState ] ) ;
163
+ } , [ addMapController , initializeCluster , initializeUserMarker , getBMapType , loadMapState ] ) ;
164
+
165
+ const handleClose = useCallback ( ( ) => {
166
+ setImageIndex ( 0 ) ;
167
+ setClusterLeaveIds ( [ ] ) ;
168
+ } , [ ] ) ;
169
+
170
+ const moveToPrevImage = useCallback ( ( ) => {
171
+ setImageIndex ( ( imageIndex + clusterLeaves . length - 1 ) % clusterLeaves . length ) ;
172
+ } , [ imageIndex , clusterLeaves . length ] ) ;
173
+
174
+ const moveToNextImage = useCallback ( ( ) => {
175
+ setImageIndex ( ( imageIndex + 1 ) % clusterLeaves . length ) ;
176
+ } , [ imageIndex , clusterLeaves . length ] ) ;
146
177
147
178
useEffect ( ( ) => {
148
179
const modifyMapTypeSubscribe = window . sfMetadataContext . eventBus . subscribe ( EVENT_BUS_TYPE . MODIFY_MAP_TYPE , ( newType ) => {
@@ -172,6 +203,17 @@ const MapView = ({ images, onOpenCluster }) => {
172
203
return (
173
204
< div className = "sf-metadata-view-map" >
174
205
< div className = "sf-metadata-map-container" ref = { mapRef } id = "sf-metadata-map-container" > </ div >
206
+ { clusterLeaveIds . length > 0 && (
207
+ < ModalPortal >
208
+ < ImageDialog
209
+ imageItems = { clusterLeaves }
210
+ imageIndex = { imageIndex }
211
+ closeImagePopup = { handleClose }
212
+ moveToPrevImage = { moveToPrevImage }
213
+ moveToNextImage = { moveToNextImage }
214
+ />
215
+ </ ModalPortal >
216
+ ) }
175
217
</ div >
176
218
) ;
177
219
} ;
0 commit comments