---
title: "MaplibreSwipe"
description: "Swipe comparison that overlays a comparison map inside the main map with a draggable divider over the same view."
canonical_url: "https://maplibre.mhaibaraai.cn/en/docs/extensions/swipe"
---
# MaplibreSwipe

> Swipe comparison that overlays a comparison map inside the main map with a draggable divider over the same view.

## Introduction

`MaplibreSwipe` is a child of `MaplibreMap` that overlays a comparison map inside the main map and clips it at the divider. Put the comparison content in the default slot, such as `MaplibreTiandituLayer` or `MaplibreLayer`, or set a whole style with `map-style`. The comparison map follows the camera, size and projection of the main map one way, and interactions such as dragging and zooming are handled by the main map.

- **Layering**: the comparison map sits above the main map canvas and below the main map controls. Main controls and attribution stay fixed and always visible; the comparison attribution hugs the divider and only labels the revealed area.
- **On demand**: toggle it with `v-if`; the layers, controls and camera of the main map are untouched.
- **Comparison interaction**: mouse events inside the revealed area are forwarded to the comparison map, so layer events, `MaplibreTooltip` and `MaplibrePopup` in the slot work.

> [!NOTE]
> 
> Features of the main map covered by the comparison map still receive events. When they should be mutually exclusive, check with the exposed 
> 
> isPointRevealed
> 
> , as in the "Comparison interaction" example. Only the comparison canvas is clipped: its markers are not hidden by the divider, and its popups are not cut off near the divider.

## Usage

Place `MaplibreSwipe` inside the main map and overlay Tianditu imagery in its slot:

```vue [SwipeExample.vue]
<script setup lang="ts">
const position = ref(0.5)
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MaplibreMap :options="{ style: 'https://tiles.openfreemap.org/styles/liberty', center: [116.397, 39.908], zoom: 11 }">
      <MaplibreNavigationControl position="top-right" />
      <MaplibreScaleControl position="bottom-left" />
      <MaplibreSwipe v-model:position="position">
        <MaplibreTiandituLayer layer="img" annotation />
      </MaplibreSwipe>
    </MaplibreMap>
  </div>
</template>
```

## Examples

### Toggle with a button

Mount it on demand with `v-if`; closing destroys the comparison map and leaves the main map as it was:

```vue [SwipeToggleExample.vue]
<script setup lang="ts">
import type { FeatureCollection } from 'geojson'

const active = ref(false)

const data: FeatureCollection = {
  type: 'FeatureCollection',
  features: [
    { type: 'Feature', properties: {}, geometry: { type: 'Point', coordinates: [116.397, 39.908] } },
    { type: 'Feature', properties: {}, geometry: { type: 'Point', coordinates: [116.45, 39.93] } },
    { type: 'Feature', properties: {}, geometry: { type: 'Point', coordinates: [116.35, 39.88] } }
  ]
}
</script>

<template>
  <div class="relative h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MaplibreMap :options="{ style: 'https://tiles.openfreemap.org/styles/positron', center: [116.4, 39.9], zoom: 11 }">
      <MaplibreNavigationControl position="top-right" />
      <MaplibreLayer
        layer-id="swipe-toggle-points"
        type="circle"
        :source="{ type: 'geojson', data }"
        :paint="{ 'circle-radius': 8, 'circle-color': '#f43f5e', 'circle-stroke-width': 2, 'circle-stroke-color': '#fff' }"
      />
      <MaplibreSwipe v-if="active">
        <MaplibreTiandituLayer layer="img" annotation />
      </MaplibreSwipe>
    </MaplibreMap>
    <UButton
      class="absolute left-2 top-2 z-10"
      size="sm"
      :icon="active ? 'i-lucide-x' : 'i-lucide-columns-2'"
      :variant="active ? 'soft' : 'solid'"
      @click="active = !active"
    >
      {{ active ? '关闭卷帘' : '卷帘对比' }}
    </UButton>
  </div>
</template>
```

### Top and bottom

`orientation="horizontal"` uses a horizontal divider, and `map-style` sets a whole style for the comparison map:

```vue [SwipeHorizontalExample.vue]
<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MaplibreMap :options="{ style: 'https://tiles.openfreemap.org/styles/positron', center: [121.47, 31.23], zoom: 10 }">
      <MaplibreNavigationControl position="top-right" />
      <MaplibreSwipe
        orientation="horizontal"
        :position="0.4"
        map-style="https://tiles.openfreemap.org/styles/dark"
      />
    </MaplibreMap>
  </div>
</template>
```

### Comparison interaction

Hovering the comparison layer inside the revealed area shows a tooltip, and clicks on either side are handled by the layer on that side. The main layer uses `isPointRevealed` to ignore clicks in the covered area:

```vue [SwipeInteractionExample.vue]
<script setup lang="ts">
import type { FeatureCollection } from 'geojson'
import type { MapLayerMouseEvent } from 'maplibre-gl'

const swipe = useTemplateRef<{ isPointRevealed: (point: { x: number, y: number }) => boolean }>('swipe')
const clicked = ref('')

const districts: FeatureCollection = {
  type: 'FeatureCollection',
  features: [
    { type: 'Feature', properties: { name: '东城片区' }, geometry: { type: 'Polygon', coordinates: [[[116.40, 39.88], [116.44, 39.88], [116.44, 39.93], [116.40, 39.93], [116.40, 39.88]]] } },
    { type: 'Feature', properties: { name: '西城片区' }, geometry: { type: 'Polygon', coordinates: [[[116.34, 39.88], [116.38, 39.88], [116.38, 39.93], [116.34, 39.93], [116.34, 39.88]]] } }
  ]
}

// 主图图层与对照图层重叠：被卷帘遮住的主图要素不响应点击
function onMainClick(event: MapLayerMouseEvent) {
  if (swipe.value?.isPointRevealed(event.point)) return
  clicked.value = `主图：${event.features?.[0]?.properties?.name}`
}

function onOverlayClick(event: MapLayerMouseEvent) {
  clicked.value = `对照图：${event.features?.[0]?.properties?.name}`
}
</script>

<template>
  <div class="relative h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MaplibreMap :options="{ style: 'https://tiles.openfreemap.org/styles/positron', center: [116.39, 39.905], zoom: 12 }">
      <MaplibreLayer
        layer-id="swipe-main-districts"
        type="fill"
        :source="{ type: 'geojson', data: districts }"
        :paint="{ 'fill-color': '#3b82f6', 'fill-opacity': 0.35 }"
        @click="onMainClick"
      />
      <MaplibreSwipe ref="swipe">
        <MaplibreTiandituLayer layer="img" />
        <MaplibreLayer
          layer-id="swipe-overlay-districts"
          type="fill"
          :source="{ type: 'geojson', data: districts }"
          :paint="{ 'fill-color': '#f59e0b', 'fill-opacity': 0.45 }"
          @click="onOverlayClick"
        />
        <MaplibreTooltip layer-id="swipe-overlay-districts">
          <template #default="{ feature }">
            {{ feature?.properties?.name }}（对照图）
          </template>
        </MaplibreTooltip>
      </MaplibreSwipe>
    </MaplibreMap>
    <UBadge v-if="clicked" class="absolute left-2 top-2 z-10" color="neutral" variant="subtle">
      {{ clicked }}
    </UBadge>
  </div>
</template>
```

## API

### Props

```ts
/**
 * Props for the MaplibreSwipe component
 */
interface MaplibreSwipeProps {
  /**
   * 分隔方向：vertical 为竖直分隔条左右对比，horizontal 为水平分隔条上下对比
   * @default 'vertical'
   */
  orientation?: "vertical" | "horizontal" | undefined;
  /**
   * 方向键每次移动的比例
   * @default 0.05
   */
  step?: number | undefined;
  /**
   * 对照图的底图样式（对象或样式 URL）；省略时为空白样式，由插槽内图层提供内容
   */
  mapStyle?: string | StyleSpecification | undefined;
  /**
   * 分隔条位置，0..1
   * @default 0.5
   */
  position?: number | undefined;
}
```

### Emits

```ts
/**
 * Emitted events for the MaplibreSwipe component
 */
interface MaplibreSwipeEmits {
  update:position: (payload: [value: number]) => void;
}
```

### Slots

```ts
/**
 * Slots for the MaplibreSwipe component
 */
interface MaplibreSwipeSlots {
  /**
   * 对照内容：放置 MaplibreTiandituLayer、MaplibreLayer 等，作用于对照图
   */
  default(): any;
}
```

### Expose

| Name | Type | Description |
| --- | --- | --- |
| `isPointRevealed` | `(point: { x: number, y: number }) => boolean` | Whether a point in the main map container falls in the revealed area of the comparison map |

## Changelog

See commit history for [src/runtime/components/extensions/Swipe.vue](https://github.com/mhaibaraai/movk-maplibre/commits/main/src/runtime/components/extensions/Swipe.vue).


## Sitemap

See the full [sitemap](https://maplibre.mhaibaraai.cn/sitemap.md) for all pages.
