Side by Side
Synchronize two maps.
<script lang="ts">
import { MapLibre, Projection } from 'svelte-maplibre-gl';
import { Label } from '$lib/components/ui/label/index.js';
import { Switch } from '$lib/components/ui/switch/index.js';
import * as RadioGroup from '$lib/components/ui/radio-group/index.js';
let center = $state({ lng: 137, lat: 36.5 });
let zoom = $state(4);
let bearing = $state(0);
let pitch = $state(0);
let roll: number | undefined = $state(undefined);
let elevation: number | undefined = $state(undefined);
const MODES = ['Side by Side', 'Split', 'Scope'] as const;
type Modes = (typeof MODES)[number];
let splitMode: Modes = $state('Side by Side');
let width = $state(0);
let globe = $state(false);
let point = $state({ x: 100, y: 100 });
export const ro = (node: Element) => {
const ro = new ResizeObserver(([entry]) => (width = entry.contentRect.width));
ro.observe(node);
return { destroy: () => ro.disconnect() };
};
</script>
<div class="mb-3 flex items-center justify-between">
<RadioGroup.Root bind:value={splitMode} class="flex flex-row gap-x-3">
{#each MODES as mode}
<div class="flex items-center space-x-1">
<RadioGroup.Item value={mode} id={mode} />
<Label for={mode}>{mode}</Label>
</div>
{/each}
</RadioGroup.Root>
<div class="flex items-center space-x-2">
<Switch id="globe" bind:checked={globe} />
<Label for="globe">Globe</Label>
</div>
</div>
<div
class="relative flex h-[55vh] min-h-[300px] flex-row-reverse"
use:ro
role="application"
onmousemove={(ev) => (point = { x: ev.offsetX, y: ev.offsetY })}
>
<MapLibre
class="flex-1"
style="https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json"
bind:center
bind:zoom
bind:bearing
bind:pitch
bind:roll
bind:elevation
padding={splitMode === 'Split' ? { left: 0, right: width / 2, top: 0, bottom: 0 } : undefined}
>
<Projection type={globe ? 'globe' : undefined} />
</MapLibre>
<MapLibre
class={splitMode === 'Scope' ? '!absolute inset-0' : 'flex-1'}
style="https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json"
inlineStyle={splitMode === 'Scope' ? `clip-path: circle(15% at ${point.x}px ${point.y}px);` : undefined}
bind:center
bind:zoom
bind:bearing
bind:pitch
bind:roll
bind:elevation
attributionControl={false}
padding={splitMode === 'Split' ? { left: width / 2, right: 0, top: 0, bottom: 0 } : undefined}
>
<Projection type={globe ? 'globe' : undefined} />
</MapLibre>
</div>