Skip to content

Instantly share code, notes, and snippets.

@twelch
Last active February 9, 2020 05:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save twelch/d1c57227e75262b4caad556a39335702 to your computer and use it in GitHub Desktop.
Save twelch/d1c57227e75262b4caad556a39335702 to your computer and use it in GitHub Desktop.
Mapbox GL JS compare window circle magnifying glass
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.46.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.46.0/mapbox-gl.css' rel='stylesheet' />
<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-compare/v0.1.0/mapbox-gl-compare.js'></script>
</head>
<body>
<style>
body {
overflow: hidden;
}
body * {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#before {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100vw;
height: 100vh;
}
#after {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
margin-top: calc(-100vh / 2 + 140px);
margin-left: calc(-100vw / 2 + 140px)
/*clip-path: circle(140px at center);
-webkit-clip-path: circle(140px at center);
*/
}
.container {
margin: auto;
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
width: 280px;
height: 280px;
border-radius: 100%;
overflow: hidden;
/* Safari overflow hidden + border-radius bug https://hk.saowen.com/a/fd38838b1554e3bc311ebabb7c4cc3d7e44d62a45ba44bc2792172c6cd76c10a */
-webkit-transform: translateZ(0);
transform: translateZ(0);
}
.shadow {
margin: auto;
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
width: 280px;
height: 280px;
border-radius: 999px;
box-shadow: inset 10px 10px 19px -4px rgba(0,0,0,0.65);
border: 1px solid #777;
pointer-events: none;
}
</style>
<div id='before' class='map'></div>
<div class='container'>
<div id='after' class='map'></div>
</div>
<div class='shadow'></div>
<script>
function moveToMapPosition (master, clones) {
var center = master.getCenter();
var zoom = master.getZoom();
var bearing = master.getBearing();
var pitch = master.getPitch();
clones.forEach(function (clone) {
clone.jumpTo({
center: center,
zoom: zoom,
bearing: bearing,
pitch: pitch
});
});
}
// Sync movements of two maps.
//
// All interactions that result in movement end up firing
// a "move" event. The trick here, though, is to
// ensure that movements don't cycle from one map
// to the other and back again, because such a cycle
// - could cause an infinite loop
// - prematurely halts prolonged movements like
// double-click zooming, box-zooming, and flying
function syncMaps () {
var maps;
var argLen = arguments.length;
if (argLen === 1) {
maps = arguments[0];
} else {
maps = [];
for (var i = 0; i < argLen; i++) {
maps.push(arguments[i]);
}
}
// Create all the movement functions, because if they're created every time
// they wouldn't be the same and couldn't be removed.
var fns = [];
maps.forEach(function (map, index) {
fns[index] = sync.bind(null, map, maps.filter(function (o, i) { return i !== index; }));
});
function on () {
maps.forEach(function (map, index) {
map.on('move', fns[index]);
});
}
function off () {
maps.forEach(function (map, index) {
map.off('move', fns[index]);
});
}
// When one map moves, we turn off the movement listeners
// on all the maps, move it, then turn the listeners on again
function sync (master, clones) {
off();
moveToMapPosition(master, clones);
on();
}
on();
}
function Compare(a, b, options) {
this.options = options ? options : {};
this._onDown = this._onDown.bind(this);
this._onMove = this._onMove.bind(this);
this._onMouseUp = this._onMouseUp.bind(this);
this._onTouchEnd = this._onTouchEnd.bind(this);
this._swiper = document.createElement('div');
this._swiper.className = 'compare-swiper';
this._container = document.createElement('div');
this._container.className = 'mapboxgl-compare';
this._container.appendChild(this._swiper);
a.getContainer().appendChild(this._container);
this._clippedMap = b;
this._bounds = b.getContainer().getBoundingClientRect();
// this._setPosition(this._bounds.width / 2);
syncMaps(a, b);
/* b.on('resize', function() {
this._bounds = b.getContainer().getBoundingClientRect();
if (this._x) this._setPosition(this._x);
}.bind(this)); */
if (this.options && this.options.mousemove) {
a.getContainer().addEventListener('mousemove', this._onMove);
b.getContainer().addEventListener('mousemove', this._onMove);
}
this._swiper.addEventListener('mousedown', this._onDown);
this._swiper.addEventListener('touchstart', this._onDown);
}
Compare.prototype = {
_setPointerEvents: function(v) {
this._container.style.pointerEvents = v;
this._swiper.style.pointerEvents = v;
},
_onDown: function(e) {
if (e.touches) {
document.addEventListener('touchmove', this._onMove);
document.addEventListener('touchend', this._onTouchEnd);
} else {
document.addEventListener('mousemove', this._onMove);
document.addEventListener('mouseup', this._onMouseUp);
}
},
_setPosition: function(x) {
var pos = 'translate(' + x + 'px, 0)';
this._container.style.transform = pos;
this._container.style.WebkitTransform = pos;
this._clippedMap.getContainer().style.clip = 'rect(0, 999em, ' + this._bounds.height + 'px,' + x + 'px)';
this._x = x;
},
_onMove: function(e) {
if (this.options && this.options.mousemove) {
this._setPointerEvents(e.touches ? 'auto' : 'none');
}
this._setPosition(this._getX(e));
},
_onMouseUp: function() {
document.removeEventListener('mousemove', this._onMove);
document.removeEventListener('mouseup', this._onMouseUp);
},
_onTouchEnd: function() {
document.removeEventListener('touchmove', this._onMove);
document.removeEventListener('touchend', this._onTouchEnd);
},
_getX: function(e) {
e = e.touches ? e.touches[0] : e;
var x = e.clientX - this._bounds.left;
if (x < 0) x = 0;
if (x > this._bounds.width) x = this._bounds.width;
return x;
}
};
mapboxgl.Compare = Compare;
/*
{
"version": 8,
"sources": {
"raster-tiles": {
"type": "raster",
"url": "mapbox://twelch.9a8w1imb",
"tileSize": 256
}
},
"layers": [{
"id": "simple-tiles",
"type": "raster",
"source": "raster-tiles",
"minzoom": 0,
"maxzoom": 22
}]
},
*/
mapboxgl.accessToken = 'pk.eyJ1IjoidHdlbGNoIiwiYSI6ImNqZW5vazRvcjEyczAyd25xamczMmh3OG0ifQ.vJy47O0vb1Z_F7OWIbZpyg';
var beforeMap = new mapboxgl.Map({
container: 'before',
style: 'mapbox://styles/mapbox/outdoors-v10',
center: [-112.018866, 33.438473],
minZoom: 9.5,
maxZoom: 13,
zoom: 11.5,
pitch: 50
});
var afterMap = new mapboxgl.Map({
container: 'after',
style: 'mapbox://styles/mapbox/outdoors-v10',
center: [-112.018866, 33.438473],
minZoom: 9.5,
maxZoom: 13,
zoom: 11.5,
pitch: 50
});
afterMap.on('load', function () {
afterMap.addLayer({
"id": "turney",
"type": "raster",
"source": {
"type": "raster",
"url": "mapbox://twelch.9a8w1imb",
"tileSize": 256
},
"minzoom": 0,
"maxzoom": 22
});
});
var map = new mapboxgl.Compare(beforeMap, afterMap, {
// Set this to enable comparing two maps by mouse movement:
// mousemove: true
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment