Skip to content

Commit 7f5cf2a

Browse files
authored
Slider End Event (#45)
* create sliderend event that will signal when the slider has stopped moving * linting
1 parent 604ddb4 commit 7f5cf2a

File tree

3 files changed

+94
-26
lines changed

3 files changed

+94
-26
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ Compare two different map styles side-by-side, or compare different layers from
231231
| `pitchend` | `{ center: [number, number], zoom: number, bearing: number, pitch: number }` | Emitted when the user finishes changing the map pitch (tilt) |
232232
| `rotateend` | `{ center: [number, number], zoom: number, bearing: number, pitch: number }` | Emitted when the user finishes rotating the map |
233233
| `loading-complete` | `void` | Emitted when both maps have finished loading and are ready |
234+
| `sliderend` | `{ percentage: number, position: number }` | Emitted when the user finishes dragging the slider. `percentage` is 0-100 representing how much of mapA is visible (0% = all mapB, 100% = all mapA). `position` is the pixel position of the slider. |
234235

235236

236237

@@ -285,6 +286,7 @@ A hybrid component that can work in single-map mode or comparison mode. Perfect
285286
| `pitchend` | `{ center: [number, number], zoom: number, bearing: number, pitch: number }` | Emitted when the user finishes changing the map pitch (tilt) |
286287
| `rotateend` | `{ center: [number, number], zoom: number, bearing: number, pitch: number }` | Emitted when the user finishes rotating the map |
287288
| `loading-complete` | `void` | Emitted when the map(s) have finished loading and are ready |
289+
| `sliderend` | `{ percentage: number, position: number }` | Emitted when the user finishes dragging the slider. `percentage` is 0-100 representing how much of mapA is visible (0% = all mapB, 100% = all mapA). `position` is the pixel position of the slider. |
288290
| `map-ready-a` | `MaplibreMap` | Emitted when map A is ready, provides the map instance |
289291
| `map-ready-b` | `MaplibreMap` | Emitted when map B is ready, provides the map instance (only in compare mode) |
290292

src/components/MapCompare.vue

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export default defineComponent({
105105
default: () => undefined,
106106
},
107107
},
108-
emits: ['panend', 'zoomend', 'pitchend', 'rotateend', 'loading-complete'],
108+
emits: ['panend', 'zoomend', 'pitchend', 'rotateend', 'loading-complete', 'sliderend'],
109109
setup(props, { slots, emit }) {
110110
const containerRef = ref<HTMLElement>();
111111
const mapARef = ref<HTMLElement>();
@@ -123,6 +123,7 @@ export default defineComponent({
123123
let mapAZoomEndHandler: (() => void) | null = null;
124124
let mapAPitchEndHandler: (() => void) | null = null;
125125
let mapARotateEndHandler: (() => void) | null = null;
126+
let sliderEndHandler: ((data: { currentPosition: number | null }) => void) | null = null;
126127
127128
// Helper function to enforce absolute positioning on map containers
128129
// MapLibre automatically sets position to relative during initialization/resize
@@ -189,6 +190,9 @@ export default defineComponent({
189190
190191
// Unmount existing swiper if it exists
191192
if (mapCompareInstance) {
193+
if (sliderEndHandler) {
194+
mapCompareInstance.off('slideend', sliderEndHandler);
195+
}
192196
mapCompareInstance.unmount();
193197
mapCompareInstance = null;
194198
}
@@ -214,6 +218,31 @@ export default defineComponent({
214218
swiperRef.value.classList.add('has-custom-icon');
215219
}
216220
}
221+
222+
// Set up slider end event listener
223+
if (mapCompareInstance) {
224+
// Remove existing handler if it exists
225+
if (sliderEndHandler) {
226+
mapCompareInstance.off('slideend', sliderEndHandler);
227+
}
228+
229+
sliderEndHandler = (data: { currentPosition: number | null }) => {
230+
if (!containerRef.value || data.currentPosition === null) return;
231+
232+
const bounds = containerRef.value.getBoundingClientRect();
233+
const isHorizontal = orientation === 'horizontal';
234+
const dimension = isHorizontal ? bounds.height : bounds.width;
235+
if (dimension > 0) {
236+
const percentage = (data.currentPosition / dimension) * 100;
237+
emit('sliderend', {
238+
percentage: Math.round(percentage * 100) / 100, // Round to 2 decimal places
239+
position: data.currentPosition,
240+
});
241+
}
242+
};
243+
244+
mapCompareInstance.on('slideend', sliderEndHandler);
245+
}
217246
};
218247
219248
const initializeMaps = async () => {
@@ -418,6 +447,9 @@ export default defineComponent({
418447
});
419448
420449
onBeforeUnmount(() => {
450+
if (mapCompareInstance && sliderEndHandler) {
451+
mapCompareInstance.off('slideend', sliderEndHandler);
452+
}
421453
mapCompareInstance?.unmount();
422454
mapCompareInstance = null;
423455
if (mapA) {
@@ -452,6 +484,7 @@ export default defineComponent({
452484
mapAZoomEndHandler = null;
453485
mapAPitchEndHandler = null;
454486
mapARotateEndHandler = null;
487+
sliderEndHandler = null;
455488
});
456489
457490
// Computed swiper options with defaults and dark mode support

src/components/ToggleCompare.vue

Lines changed: 58 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export default defineComponent({
132132
default: () => undefined,
133133
},
134134
},
135-
emits: ['panend', 'zoomend', 'pitchend', 'rotateend', 'loading-complete', 'map-ready-a', 'map-ready-b'],
135+
emits: ['panend', 'zoomend', 'pitchend', 'rotateend', 'loading-complete', 'map-ready-a', 'map-ready-b', 'sliderend'],
136136
setup(props, { slots, emit }) {
137137
const containerRef = ref<HTMLElement>();
138138
const mapARef = ref<HTMLElement>();
@@ -150,6 +150,7 @@ export default defineComponent({
150150
let mapAZoomEndHandler: (() => void) | null = null;
151151
let mapAPitchEndHandler: (() => void) | null = null;
152152
let mapARotateEndHandler: (() => void) | null = null;
153+
let sliderEndHandler: ((data: { currentPosition: number | null }) => void) | null = null;
153154
154155
// Helper function to enforce absolute positioning on map containers
155156
const enforceAbsolutePosition = () => {
@@ -168,17 +169,23 @@ export default defineComponent({
168169
mapBRef.value.style.setProperty('height', '100%', 'important');
169170
}
170171
};
171-
172-
const updateLayers = (mapType: 'A' | 'B') => {
172+
const updateLayerOrdering = (mapType: 'A' | 'B') => {
173173
const map = mapType === 'A' ? mapA : mapB;
174+
const enabledLayers = mapType === 'A' ? props.mapLayersA : props.mapLayersB;
174175
if (!map) return;
175-
if (!map.isStyleLoaded()) {
176-
map.once('style.load', () => {
177-
updateLayerVisibilityandOrdering(mapType);
176+
if (!enabledLayers || enabledLayers.length === 0) return;
177+
178+
// Reorder layers based on layerOrder prop
179+
const layersInStyle = map.getStyle().layers || [];
180+
const orderedLayers = props.layerOrder === 'topmost'
181+
? enabledLayers
182+
: [...enabledLayers].reverse();
183+
184+
orderedLayers.forEach((layerId) => {
185+
if (layersInStyle.find((l) => l.id === layerId)) {
186+
map.moveLayer(layerId);
187+
}
178188
});
179-
} else {
180-
updateLayerVisibilityandOrdering(mapType);
181-
}
182189
};
183190
184191
const updateLayerVisibilityandOrdering = (mapType: 'A' | 'B') => {
@@ -198,32 +205,28 @@ export default defineComponent({
198205
}
199206
});
200207
}
201-
updateLayerOrdering(mapType);
208+
updateLayerOrdering(mapType);
202209
};
203210
204-
const updateLayerOrdering = (mapType: 'A' | 'B') => {
211+
const updateLayers = (mapType: 'A' | 'B') => {
205212
const map = mapType === 'A' ? mapA : mapB;
206-
const enabledLayers = mapType === 'A' ? props.mapLayersA : props.mapLayersB;
207213
if (!map) return;
208-
if (!enabledLayers || enabledLayers.length === 0) return;
209-
210-
// Reorder layers based on layerOrder prop
211-
const layersInStyle = map.getStyle().layers || [];
212-
const orderedLayers = props.layerOrder === 'topmost'
213-
? enabledLayers
214-
: [...enabledLayers].reverse();
215-
216-
orderedLayers.forEach((layerId) => {
217-
if (layersInStyle.find((l) => l.id === layerId)) {
218-
map.moveLayer(layerId);
219-
}
220-
});
214+
if (!map.isStyleLoaded()) {
215+
map.once('style.load', () => {
216+
updateLayerVisibilityandOrdering(mapType);
217+
});
218+
} else {
219+
updateLayerVisibilityandOrdering(mapType);
220+
}
221221
};
222222
223223
const initializeSwiper = () => {
224224
if (!mapA || !mapB || !containerRef.value || !props.compareEnabled) return;
225225
// Unmount existing swiper if it exists
226226
if (mapCompareInstance) {
227+
if (sliderEndHandler) {
228+
mapCompareInstance.off('slideend', sliderEndHandler);
229+
}
227230
mapCompareInstance.unmount();
228231
mapCompareInstance = null;
229232
}
@@ -247,9 +250,38 @@ export default defineComponent({
247250
swiperRef.value.classList.add('has-custom-icon');
248251
}
249252
}
253+
254+
// Set up slider end event listener
255+
if (mapCompareInstance) {
256+
// Remove existing handler if it exists
257+
if (sliderEndHandler) {
258+
mapCompareInstance.off('slideend', sliderEndHandler);
259+
}
260+
261+
sliderEndHandler = (data: { currentPosition: number | null }) => {
262+
if (!containerRef.value || data.currentPosition === null) return;
263+
264+
const bounds = containerRef.value.getBoundingClientRect();
265+
const isHorizontal = orientation === 'horizontal';
266+
const dimension = isHorizontal ? bounds.height : bounds.width;
267+
268+
if (dimension > 0) {
269+
const percentage = (data.currentPosition / dimension) * 100;
270+
emit('sliderend', {
271+
percentage: Math.round(percentage * 100) / 100, // Round to 2 decimal places
272+
position: data.currentPosition,
273+
});
274+
}
275+
};
276+
277+
mapCompareInstance.on('slideend', sliderEndHandler);
278+
}
250279
};
251280
252281
const cleanupComparison = () => {
282+
if (mapCompareInstance && sliderEndHandler) {
283+
mapCompareInstance.off('slideend', sliderEndHandler);
284+
}
253285
if (mapCompareInstance) {
254286
mapCompareInstance.unmount();
255287
mapCompareInstance = null;
@@ -534,6 +566,7 @@ export default defineComponent({
534566
mapAZoomEndHandler = null;
535567
mapAPitchEndHandler = null;
536568
mapARotateEndHandler = null;
569+
sliderEndHandler = null;
537570
});
538571
539572
// Computed swiper options with defaults and dark mode support

0 commit comments

Comments
 (0)