├── .eslintrc ├── .github ├── FUNDING.yml └── workflows │ └── publish.yml ├── .gitignore ├── API.md ├── CHANGELOG.md ├── LICENCE ├── README.md ├── dist ├── maplibre-gl-compare.css └── maplibre-gl-compare.js ├── example ├── index.html └── index.js ├── index.js ├── package-lock.json ├── package.json ├── style.css └── test └── index.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "browser": true 5 | }, 6 | "globals": { 7 | "maplibregl": true 8 | }, 9 | "rules": { 10 | "strict": [0], 11 | "curly": [0], 12 | "no-new": [0], 13 | "no-alert": [0], 14 | "no-extra-bind": [0], 15 | "no-redeclare": [1], 16 | "new-cap": [0], 17 | "eol-last": [0], 18 | "no-mixed-requires": [0], 19 | "no-path-concat": [0], 20 | "camelcase": [0], 21 | "consistent-return": [0], 22 | "no-underscore-dangle": [0], 23 | "comma-spacing": [0], 24 | "key-spacing": [0], 25 | "no-use-before-define": [0] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [maplibre] 2 | open_collective: maplibre 3 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - v* 8 | 9 | jobs: 10 | publish: 11 | name: Publish 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Use Node.js 10 x64 17 | uses: actions/setup-node@v2 18 | with: 19 | node-version: 10 20 | architecture: x64 21 | registry-url: 'https://registry.npmjs.org' 22 | 23 | - name: Publish 24 | run: | 25 | npm publish --access=public --non-interactive 26 | env: 27 | NODE_AUTH_TOKEN: ${{ secrets.NPM_ORG_TOKEN }} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | example/style.css 3 | .idea/ 4 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Table of Contents 4 | 5 | * [Compare][1] 6 | * [Parameters][2] 7 | * [Examples][3] 8 | * [setSlider][4] 9 | * [Parameters][5] 10 | * [on][6] 11 | * [Parameters][7] 12 | * [fire][8] 13 | * [Parameters][9] 14 | * [off][10] 15 | * [Parameters][11] 16 | 17 | ## Compare 18 | 19 | * **See**: [Swipe between maps][12] 20 | 21 | ### Parameters 22 | 23 | * `a` **[Object][13]** The first MapLibre GL Map 24 | * `b` **[Object][13]** The second MapLibre GL Map 25 | * `container` **([string][14] | [HTMLElement][15])** An HTML Element, or an element selector string for the compare container. It should be a wrapper around the two map Elements. 26 | * `options` **[Object][13]** 27 | 28 | * `options.orientation` **[string][14]** The orientation of the compare slider. `vertical` creates a vertical slider bar to compare one map on the left (map A) with another map on the right (map B). `horizontal` creates a horizontal slider bar to compare on mop on the top (map A) and another map on the bottom (map B). (optional, default `vertical`) 29 | * `options.mousemove` **[boolean][16]** If `true` the compare slider will move with the cursor, otherwise the slider will need to be dragged to move. (optional, default `false`) 30 | 31 | ### Examples 32 | 33 | ```javascript 34 | var compare = new maplibregl.Compare(beforeMap, afterMap, '#wrapper', { 35 | orientation: 'vertical', 36 | mousemove: true 37 | }); 38 | ``` 39 | 40 | ### setSlider 41 | 42 | Set the position of the slider. 43 | 44 | #### Parameters 45 | 46 | * `x` **[number][17]** Slider position in pixels from left/top. 47 | 48 | ### on 49 | 50 | Adds a listener for events of a specified type. 51 | 52 | #### Parameters 53 | 54 | * `type` **[string][14]** The event type to listen for; one of `slideend`. 55 | * `fn` 56 | * `listener` **[Function][18]** The function to be called when the event is fired. 57 | 58 | Returns **[Compare][19]** `this` 59 | 60 | ### fire 61 | 62 | Fire an event of a specified type. 63 | 64 | #### Parameters 65 | 66 | * `type` **[string][14]** The event type to fire; one of `slideend`. 67 | * `data` **[Object][13]** Data passed to the event listener. 68 | 69 | Returns **[Compare][19]** `this` 70 | 71 | ### off 72 | 73 | Removes an event listener previously added with `Compare#on`. 74 | 75 | #### Parameters 76 | 77 | * `type` **[string][14]** The event type previously used to install the listener. 78 | * `fn` 79 | * `listener` **[Function][18]** The function previously installed as a listener. 80 | 81 | Returns **[Compare][19]** `this` 82 | 83 | [1]: #compare 84 | 85 | [2]: #parameters 86 | 87 | [3]: #examples 88 | 89 | [4]: #setslider 90 | 91 | [5]: #parameters-1 92 | 93 | [6]: #on 94 | 95 | [7]: #parameters-2 96 | 97 | [8]: #fire 98 | 99 | [9]: #parameters-3 100 | 101 | [10]: #off 102 | 103 | [11]: #parameters-4 104 | 105 | [12]: https://maplibre.org/maplibre-gl-js-docs/plugins/ 106 | 107 | [13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object 108 | 109 | [14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String 110 | 111 | [15]: https://developer.mozilla.org/docs/Web/HTML/Element 112 | 113 | [16]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean 114 | 115 | [17]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number 116 | 117 | [18]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function 118 | 119 | [19]: #compare 120 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [Unreleased] 8 | 9 | ### Added 10 | 11 | - Forked from https://github.com/mapbox/mapbox-gl-compare 12 | 13 | ### Changed 14 | 15 | - Changed the text mapboxgl to maplibregl 16 | 17 | ### Deprecated 18 | 19 | ### Removed 20 | 21 | ### Fixed 22 | 23 | ### Security 24 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 MapLibre contributors 2 | 3 | Copyright (c) 2016, Mapbox 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## MapLibre GL Compare 2 | 3 | Swipe and sync between two MapLibre maps. This plugin was originally developed for Mapbox (https://github.com/mapbox/mapbox-gl-compare) and was migrated to MapLibre after Mapbox changed its license. 4 | 5 | ## Code example 6 | 7 | ### Examples 8 | 9 | #### Full Example without the need of a token 10 | 11 | ```js 12 | 13 | 14 | 15 | 16 | Swipe between maps 17 | 21 | 22 | 23 | 27 | 40 | 41 | 42 | 43 | 63 | 64 | 65 |
66 |
67 |
68 |
69 | 93 | 94 | 95 | ``` 96 | 97 | #### Full Example with the need of a token 98 | 99 | ```js 100 | 101 | 102 | 103 | 104 | Swipe between maps 105 | 109 | 110 | 111 | 115 | 128 | 129 | 130 | 131 | 151 | 152 | 153 |
154 |
155 |
156 |
157 | 182 | 183 | 184 | ``` 185 | 186 | ### Usage 187 | 188 | ```js 189 | var beforeMap = new maplibregl.Map({ 190 | container: "before", 191 | style: "https://demotiles.maplibre.org/style.json", 192 | center: [7.221275, 50.326111], 193 | zoom: 5, 194 | }); 195 | 196 | var afterMap = new maplibregl.Map({ 197 | container: "after", 198 | style: 199 | "https://vectortiles.geo.admin.ch/styles/ch.swisstopo.leichte-basiskarte.vt/style.json", 200 | center: [7.221275, 50.326111], 201 | zoom: 5, 202 | }); 203 | 204 | // A selector or reference to HTML element 205 | var container = '#comparison-container'; 206 | 207 | var map = new maplibregl.Compare(beforeMap, afterMap, container, { 208 | mousemove: true, // Optional. Set to true to enable swiping during cursor movement. 209 | orientation: 'vertical' // Optional. Sets the orientation of swiper to horizontal or vertical, defaults to vertical 210 | }); 211 | ``` 212 | 213 | ### Methods 214 | 215 | ```js 216 | compare = new maplibregl.Compare(beforeMap, afterMap, container, { 217 | mousemove: true, // Optional. Set to true to enable swiping during cursor movement. 218 | orientation: 'vertical' // Optional. Sets the orientation of swiper to horizontal or vertical, defaults to vertical 219 | }); 220 | 221 | // Get Current position - this will return the slider's current position, in pixels 222 | compare.currentPosition; 223 | 224 | // Set Position - this will set the slider at the specified (x) number of pixels from the left-edge or top-edge of viewport based on swiper orientation 225 | compare.setSlider(x); 226 | 227 | //Listen to slider movement - and return current position on each slideend 228 | compare.on('slideend', (e) => { 229 | console.log(e.currentPosition); 230 | }); 231 | 232 | //Remove - this will remove the compare control from the DOM and stop synchronizing the two maps. 233 | compare.remove(); 234 | ``` 235 | 236 | ## Live examples 237 | 238 | [Compare hybrid with streets](https://rawcdn.githack.com/astridx/astridx.github.io/a9d7297a4fe1e3a4d7ebeb1e4e662fd1339ef3b5/maplibreexamples/plugins/maplibre-gl-compare-swipe-between-maps.html) 239 | 240 | [Compare swisstopo with demotiles](https://rawcdn.githack.com/astridx/astridx.github.io/a9d7297a4fe1e3a4d7ebeb1e4e662fd1339ef3b5/maplibreexamples/plugins/maplibre-gl-compare-swipe-between-maps_.html) 241 | 242 | ## Installation 243 | 244 | ### Native 245 | 246 | Add tags referencing `maplibre-gl-compare` after adding `maplibre-gl` to your website: 247 | 248 | ```html 249 | 250 | 251 | 252 | 253 | 254 | 255 | ``` 256 | 257 | ### Node 258 | 259 | 260 | 261 | ## Motivation 262 | 263 | This project makes it possible to easily compare two maps. 264 | 265 | ## API Reference 266 | 267 | [API Reference](API.md) 268 | 269 | ## Bug Reports & Feature Requests 270 | 271 | Please use the [issue tracker](https://github.com/maplibre/maplibre-gl-compare/issues) to report any bugs or file feature requests. 272 | 273 | ## Licence 274 | ISC © [MapLibre](https://github.com/maplibre) © [Mapbox](https://github.com/mapbox) 275 | -------------------------------------------------------------------------------- /dist/maplibre-gl-compare.css: -------------------------------------------------------------------------------- 1 | .maplibregl-compare { 2 | background-color: #fff; 3 | position: absolute; 4 | width: 2px; 5 | height: 100%; 6 | z-index: 1; 7 | } 8 | .maplibregl-compare .compare-swiper-vertical { 9 | background-color: #3887be; 10 | box-shadow: inset 0 0 0 2px #fff; 11 | display: inline-block; 12 | border-radius: 50%; 13 | position: absolute; 14 | width: 60px; 15 | height: 60px; 16 | top: 50%; 17 | left: -30px; 18 | margin: -30px 1px 0; 19 | color: #fff; 20 | cursor: ew-resize; 21 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiICAgd2lkdGg9IjYwIiAgIGhlaWdodD0iNjAiICAgdmVyc2lvbj0iMS4xIiAgIHZpZXdCb3g9IjAgMCA2MCA2MCIgICBpZD0ic3ZnNTQzNCIgICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjkxK2RldmVsK29zeG1lbnUgcjEyOTExIiAgIHNvZGlwb2RpOmRvY25hbWU9Imwtci5zdmciPiAgPG1ldGFkYXRhICAgICBpZD0ibWV0YWRhdGE1NDQ0Ij4gICAgPHJkZjpSREY+ICAgICAgPGNjOldvcmsgICAgICAgICByZGY6YWJvdXQ9IiI+ICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4gICAgICAgIDxkYzp0eXBlICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+ICAgICAgPC9jYzpXb3JrPiAgICA8L3JkZjpSREY+ICA8L21ldGFkYXRhPiAgPGRlZnMgICAgIGlkPSJkZWZzNTQ0MiIgLz4gIDxzb2RpcG9kaTpuYW1lZHZpZXcgICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIgICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IiAgICAgYm9yZGVyb3BhY2l0eT0iMSIgICAgIG9iamVjdHRvbGVyYW5jZT0iMTAiICAgICBncmlkdG9sZXJhbmNlPSIxMCIgICAgIGd1aWRldG9sZXJhbmNlPSIxMCIgICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIgICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTI4NiIgICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijc1MSIgICAgIGlkPSJuYW1lZHZpZXc1NDQwIiAgICAgc2hvd2dyaWQ9InRydWUiICAgICBpbmtzY2FwZTp6b29tPSI0IiAgICAgaW5rc2NhcGU6Y3g9IjI1Ljg4OTgzMSIgICAgIGlua3NjYXBlOmN5PSIzNC4zODE4MzMiICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMCIgICAgIGlua3NjYXBlOndpbmRvdy15PSIyMyIgICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjAiICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJzdmc1NDM0IiAgICAgaW5rc2NhcGU6b2JqZWN0LW5vZGVzPSJ0cnVlIiAgICAgaW5rc2NhcGU6c25hcC1zbW9vdGgtbm9kZXM9InRydWUiPiAgICA8aW5rc2NhcGU6Z3JpZCAgICAgICB0eXBlPSJ4eWdyaWQiICAgICAgIGlkPSJncmlkNTk4OSIgLz4gIDwvc29kaXBvZGk6bmFtZWR2aWV3PiAgPHBhdGggICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAgICAgZD0iTSAyNSAyNCBMIDE2IDMwIEwgMjUgMzYgTCAyNSAyNCB6IE0gMzUgMjQgTCAzNSAzNiBMIDQ0IDMwIEwgMzUgMjQgeiAiICAgICBpZD0icGF0aDU5OTUiIC8+PC9zdmc+); 22 | } 23 | 24 | .maplibregl-compare-horizontal { 25 | position: relative; 26 | width: 100%; 27 | height: 2px; 28 | } 29 | .maplibregl-compare .compare-swiper-horizontal { 30 | background-color: #3887be; 31 | box-shadow: inset 0 0 0 2px #fff; 32 | display: inline-block; 33 | border-radius: 50%; 34 | position: absolute; 35 | width: 60px; 36 | height: 60px; 37 | top: 50%; 38 | left: 50%; 39 | margin: -30px 1px 0; 40 | color: #fff; 41 | cursor: ns-resize; 42 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiICAgd2lkdGg9IjYwIiAgIGhlaWdodD0iNjAiICAgdmVyc2lvbj0iMS4xIiAgIHZpZXdCb3g9IjAgMCA2MCA2MCIgICBpZD0ic3ZnNTQzNCIgICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjkxK2RldmVsK29zeG1lbnUgcjEyOTExIiAgIHNvZGlwb2RpOmRvY25hbWU9Imwtci5zdmciPiAgPG1ldGFkYXRhICAgICBpZD0ibWV0YWRhdGE1NDQ0Ij4gICAgPHJkZjpSREY+ICAgICAgPGNjOldvcmsgICAgICAgICByZGY6YWJvdXQ9IiI+ICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4gICAgICAgIDxkYzp0eXBlICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+ICAgICAgPC9jYzpXb3JrPiAgICA8L3JkZjpSREY+ICA8L21ldGFkYXRhPiAgPGRlZnMgICAgIGlkPSJkZWZzNTQ0MiIgLz4gIDxzb2RpcG9kaTpuYW1lZHZpZXcgICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIgICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IiAgICAgYm9yZGVyb3BhY2l0eT0iMSIgICAgIG9iamVjdHRvbGVyYW5jZT0iMTAiICAgICBncmlkdG9sZXJhbmNlPSIxMCIgICAgIGd1aWRldG9sZXJhbmNlPSIxMCIgICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIgICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTI4NiIgICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijc1MSIgICAgIGlkPSJuYW1lZHZpZXc1NDQwIiAgICAgc2hvd2dyaWQ9InRydWUiICAgICBpbmtzY2FwZTp6b29tPSI0IiAgICAgaW5rc2NhcGU6Y3g9IjI1Ljg4OTgzMSIgICAgIGlua3NjYXBlOmN5PSIzNC4zODE4MzMiICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMCIgICAgIGlua3NjYXBlOndpbmRvdy15PSIyMyIgICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjAiICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJzdmc1NDM0IiAgICAgaW5rc2NhcGU6b2JqZWN0LW5vZGVzPSJ0cnVlIiAgICAgaW5rc2NhcGU6c25hcC1zbW9vdGgtbm9kZXM9InRydWUiPiAgICA8aW5rc2NhcGU6Z3JpZCAgICAgICB0eXBlPSJ4eWdyaWQiICAgICAgIGlkPSJncmlkNTk4OSIgLz4gIDwvc29kaXBvZGk6bmFtZWR2aWV3PiAgPHBhdGggICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAgICAgZD0iTSAyNSAyNCBMIDE2IDMwIEwgMjUgMzYgTCAyNSAyNCB6IE0gMzUgMjQgTCAzNSAzNiBMIDQ0IDMwIEwgMzUgMjQgeiAiICAgICBpZD0icGF0aDU5OTUiIC8+PC9zdmc+); 43 | transform: rotate(90deg); 44 | } 45 | -------------------------------------------------------------------------------- /dist/maplibre-gl-compare.js: -------------------------------------------------------------------------------- 1 | !function i(o,r,s){function h(t,e){if(!r[t]){if(!o[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(u)return u(t,!0);throw(e=new Error("Cannot find module '"+t+"'")).code="MODULE_NOT_FOUND",e}n=r[t]={exports:{}},o[t][0].call(n.exports,function(e){return h(o[t][1][e]||e)},n,n.exports,i,o,r,s)}return r[t].exports}for(var u="function"==typeof require&&require,e=0;ethis._bounds.width?this._bounds.width:e},_getY:function(e){e=(e=e.touches?e.touches[0]:e).clientY-this._bounds.top;return e=(e=e<0?0:e)>this._bounds.height?this._bounds.height:e},setSlider:function(e){this._setPosition(e)},on:function(e,t){return this._ev.on(e,t),this},fire:function(e,t){return this._ev.emit(e,t),this},off:function(e,t){return this._ev.removeListener(e,t),this},remove:function(){this._clearSync(),this._mapB.off("resize",this._onResize);var e=this._mapA.getContainer(),e=(e&&(e.style.clip=null,e.removeEventListener("mousemove",this._onMove)),this._mapB.getContainer());e&&(e.style.clip=null,e.removeEventListener("mousemove",this._onMove)),this._swiper.removeEventListener("mousedown",this._onDown),this._swiper.removeEventListener("touchstart",this._onDown),this._controlContainer.remove()}},window.maplibregl?maplibregl.Compare=i:void 0!==t&&(t.exports=i)},{"@mapbox/mapbox-gl-sync-move":2,events:3}],2:[function(e,t,n){t.exports=function(){var e=arguments.length;if(1===e)t=arguments[0];else for(var t=[],n=0;no&&!r.warned&&(r.warned=!0,(i=new Error("Possible EventEmitter memory leak detected. "+r.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit")).name="MaxListenersExceededWarning",i.emitter=e,i.type=t,i.count=r.length,n=i,console&&console.warn&&console.warn(n))),e}function h(e,t,n){e={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},t=function(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}.bind(e);return t.listener=n,e.wrapFn=t}function p(e,t,n){e=e._events;if(void 0===e)return[];e=e[t];{if(void 0===e)return[];if("function"==typeof e)return n?[e.listener||e]:[e];if(n){for(var i=e,o=new Array(i.length),r=0;r 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 16 | 41 | 42 | 43 |
44 |
45 |
46 |
47 | 48 |
49 |
50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("../"); 4 | 5 | // 'Before' style from https://github.com/lukasmartinelli/naturalearthtiles 6 | var before = new maplibregl.Map({ 7 | container: "before", 8 | style: "https://raw.githubusercontent.com/lukasmartinelli/naturalearthtiles/gh-pages/maps/natural_earth.vector.json", 9 | zoom: 2 10 | }); 11 | 12 | // 'After' style from https://github.com/maplibre/demotiles 13 | var after = new maplibregl.Map({ 14 | container: "after", 15 | style: "https://demotiles.maplibre.org/style.json", 16 | zoom: 2 17 | }); 18 | 19 | // Use either of these patterns to select a container for the compare widget 20 | var wrapperSelector = "#wrapper"; 21 | var wrapperElement = document.body.querySelectorAll("#wrapper")[0]; 22 | 23 | // available options 24 | var options = { 25 | mousemove: true, 26 | orientation: "horizontal", 27 | }; 28 | 29 | window.compare = new maplibregl.Compare( 30 | before, 31 | after, 32 | wrapperSelector 33 | // options 34 | ); 35 | 36 | var closeButton = document.getElementById("close-button"); 37 | 38 | closeButton.addEventListener("click", function (e) { 39 | after.getContainer().style.display = "none"; 40 | window.compare.remove(); 41 | after.remove(); 42 | }); 43 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var syncMove = require("@mapbox/mapbox-gl-sync-move"); 4 | var EventEmitter = require("events").EventEmitter; 5 | 6 | /** 7 | * @param {Object} a The first MapLibre GL Map 8 | * @param {Object} b The second MapLibre GL Map 9 | * @param {string|HTMLElement} container An HTML Element, or an element selector string for the compare container. It should be a wrapper around the two map Elements. 10 | * @param {Object} options 11 | * @param {string} [options.orientation=vertical] The orientation of the compare slider. `vertical` creates a vertical slider bar to compare one map on the left (map A) with another map on the right (map B). `horizontal` creates a horizontal slider bar to compare on mop on the top (map A) and another map on the bottom (map B). 12 | * @param {boolean} [options.mousemove=false] If `true` the compare slider will move with the cursor, otherwise the slider will need to be dragged to move. 13 | * @example 14 | * var compare = new maplibregl.Compare(beforeMap, afterMap, '#wrapper', { 15 | * orientation: 'vertical', 16 | * mousemove: true 17 | * }); 18 | * @see [Swipe between maps](https://maplibre.org/maplibre-gl-js-docs/plugins/) 19 | */ 20 | function Compare(a, b, container, options) { 21 | this.options = options ? options : {}; 22 | this._mapA = a; 23 | this._mapB = b; 24 | this._horizontal = this.options.orientation === "horizontal"; 25 | this._onDown = this._onDown.bind(this); 26 | this._onMove = this._onMove.bind(this); 27 | this._onMouseUp = this._onMouseUp.bind(this); 28 | this._onTouchEnd = this._onTouchEnd.bind(this); 29 | this._ev = new EventEmitter(); 30 | this._swiper = document.createElement("div"); 31 | this._swiper.className = this._horizontal 32 | ? "compare-swiper-horizontal" 33 | : "compare-swiper-vertical"; 34 | 35 | this._controlContainer = document.createElement("div"); 36 | this._controlContainer.className = this._horizontal 37 | ? "maplibregl-compare maplibregl-compare-horizontal" 38 | : "maplibregl-compare"; 39 | this._controlContainer.className = this._controlContainer.className; 40 | this._controlContainer.appendChild(this._swiper); 41 | 42 | if (typeof container === "string" && document.body.querySelectorAll) { 43 | // get container with a selector 44 | var appendTarget = document.body.querySelectorAll(container)[0]; 45 | if (!appendTarget) { 46 | throw new Error("Cannot find element with specified container selector."); 47 | } 48 | appendTarget.appendChild(this._controlContainer); 49 | } else if (container instanceof Element && container.appendChild) { 50 | // get container directly 51 | container.appendChild(this._controlContainer); 52 | } else { 53 | throw new Error( 54 | "Invalid container specified. Must be CSS selector or HTML element." 55 | ); 56 | } 57 | 58 | this._bounds = b.getContainer().getBoundingClientRect(); 59 | var swiperPosition = 60 | (this._horizontal ? this._bounds.height : this._bounds.width) / 2; 61 | 62 | this._setPosition(swiperPosition); 63 | 64 | this._clearSync = syncMove(a, b); 65 | this._onResize = function () { 66 | this._bounds = b.getContainer().getBoundingClientRect(); 67 | if (this.currentPosition) this._setPosition(this.currentPosition); 68 | }.bind(this); 69 | 70 | b.on("resize", this._onResize); 71 | 72 | if (this.options && this.options.mousemove) { 73 | a.getContainer().addEventListener("mousemove", this._onMove); 74 | b.getContainer().addEventListener("mousemove", this._onMove); 75 | } 76 | 77 | this._swiper.addEventListener("mousedown", this._onDown); 78 | this._swiper.addEventListener("touchstart", this._onDown); 79 | } 80 | 81 | Compare.prototype = { 82 | _setPointerEvents: function (v) { 83 | this._controlContainer.style.pointerEvents = v; 84 | this._swiper.style.pointerEvents = v; 85 | }, 86 | 87 | _onDown: function (e) { 88 | if (e.touches) { 89 | document.addEventListener("touchmove", this._onMove); 90 | document.addEventListener("touchend", this._onTouchEnd); 91 | } else { 92 | document.addEventListener("mousemove", this._onMove); 93 | document.addEventListener("mouseup", this._onMouseUp); 94 | } 95 | }, 96 | 97 | _setPosition: function (x) { 98 | x = Math.min( 99 | x, 100 | this._horizontal ? this._bounds.height : this._bounds.width 101 | ); 102 | var pos = this._horizontal 103 | ? "translate(0, " + x + "px)" 104 | : "translate(" + x + "px, 0)"; 105 | this._controlContainer.style.transform = pos; 106 | this._controlContainer.style.WebkitTransform = pos; 107 | var clipA = this._horizontal 108 | ? "rect(0, 999em, " + x + "px, 0)" 109 | : "rect(0, " + x + "px, " + this._bounds.height + "px, 0)"; 110 | var clipB = this._horizontal 111 | ? "rect(" + x + "px, 999em, " + this._bounds.height + "px,0)" 112 | : "rect(0, 999em, " + this._bounds.height + "px," + x + "px)"; 113 | 114 | this._mapA.getContainer().style.clip = clipA; 115 | this._mapB.getContainer().style.clip = clipB; 116 | this.currentPosition = x; 117 | }, 118 | 119 | _onMove: function (e) { 120 | if (this.options && this.options.mousemove) { 121 | this._setPointerEvents(e.touches ? "auto" : "none"); 122 | } 123 | 124 | this._horizontal 125 | ? this._setPosition(this._getY(e)) 126 | : this._setPosition(this._getX(e)); 127 | }, 128 | 129 | _onMouseUp: function () { 130 | document.removeEventListener("mousemove", this._onMove); 131 | document.removeEventListener("mouseup", this._onMouseUp); 132 | this.fire("slideend", { currentPosition: this.currentPosition }); 133 | }, 134 | 135 | _onTouchEnd: function () { 136 | document.removeEventListener("touchmove", this._onMove); 137 | document.removeEventListener("touchend", this._onTouchEnd); 138 | this.fire("slideend", { currentPosition: this.currentPosition }); 139 | }, 140 | 141 | _getX: function (e) { 142 | e = e.touches ? e.touches[0] : e; 143 | var x = e.clientX - this._bounds.left; 144 | if (x < 0) x = 0; 145 | if (x > this._bounds.width) x = this._bounds.width; 146 | return x; 147 | }, 148 | 149 | _getY: function (e) { 150 | e = e.touches ? e.touches[0] : e; 151 | var y = e.clientY - this._bounds.top; 152 | if (y < 0) y = 0; 153 | if (y > this._bounds.height) y = this._bounds.height; 154 | return y; 155 | }, 156 | 157 | /** 158 | * Set the position of the slider. 159 | * 160 | * @param {number} x Slider position in pixels from left/top. 161 | */ 162 | setSlider: function (x) { 163 | this._setPosition(x); 164 | }, 165 | 166 | /** 167 | * Adds a listener for events of a specified type. 168 | * 169 | * @param {string} type The event type to listen for; one of `slideend`. 170 | * @param {Function} listener The function to be called when the event is fired. 171 | * @returns {Compare} `this` 172 | */ 173 | on: function (type, fn) { 174 | this._ev.on(type, fn); 175 | return this; 176 | }, 177 | 178 | /** 179 | * Fire an event of a specified type. 180 | * 181 | * @param {string} type The event type to fire; one of `slideend`. 182 | * @param {Object} data Data passed to the event listener. 183 | * @returns {Compare} `this` 184 | */ 185 | fire: function (type, data) { 186 | this._ev.emit(type, data); 187 | return this; 188 | }, 189 | 190 | /** 191 | * Removes an event listener previously added with `Compare#on`. 192 | * 193 | * @param {string} type The event type previously used to install the listener. 194 | * @param {Function} listener The function previously installed as a listener. 195 | * @returns {Compare} `this` 196 | */ 197 | off: function (type, fn) { 198 | this._ev.removeListener(type, fn); 199 | return this; 200 | }, 201 | 202 | remove: function () { 203 | this._clearSync(); 204 | this._mapB.off("resize", this._onResize); 205 | var aContainer = this._mapA.getContainer(); 206 | 207 | if (!!aContainer) { 208 | aContainer.style.clip = null; 209 | aContainer.removeEventListener("mousemove", this._onMove); 210 | } 211 | 212 | var bContainer = this._mapB.getContainer(); 213 | 214 | if (!!bContainer) { 215 | bContainer.style.clip = null; 216 | bContainer.removeEventListener("mousemove", this._onMove); 217 | } 218 | 219 | this._swiper.removeEventListener("mousedown", this._onDown); 220 | this._swiper.removeEventListener("touchstart", this._onDown); 221 | this._controlContainer.remove(); 222 | }, 223 | }; 224 | 225 | if (window.maplibregl) { 226 | maplibregl.Compare = Compare; 227 | } else if (typeof module !== "undefined") { 228 | module.exports = Compare; 229 | } 230 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@maplibre/maplibre-gl-compare", 3 | "version": "0.6.0-dev", 4 | "description": "Swipe and sync between two MapLibre maps", 5 | "funding": "https://github.com/maplibre/maplibre-gl-js?sponsor=1", 6 | "main": "index.js", 7 | "directories": { 8 | "example": "example" 9 | }, 10 | "scripts": { 11 | "start": "cp style.css example/style.css && budo example/index.js --serve example/bundle.js --dir example --live", 12 | "build": "cp style.css dist/maplibre-gl-compare.css && NODE_ENV=production && browserify index.js | uglifyjs -c -m > dist/maplibre-gl-compare.js", 13 | "test": "npm run lint && browserify -t envify test/index.js | smokestack -b firefox | tap-status", 14 | "lint": "eslint --no-eslintrc -c .eslintrc index.js example/index.js test/index.js", 15 | "docs": "documentation build index.js --format=md > API.md" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/maplibre/maplibre-gl-compare.git" 20 | }, 21 | "keywords": [ 22 | "maplibre", 23 | "maplibregl", 24 | "maps", 25 | "gl", 26 | "ui" 27 | ], 28 | "license": "ISC", 29 | "devDependencies": { 30 | "browserify": "^17.0.0", 31 | "budo": "^11.6.4", 32 | "documentation": "^13.2.5", 33 | "envify": "^4.1.0", 34 | "eslint": "^8.9.0", 35 | "maplibre-gl": "^2.1.6", 36 | "smokestack": "^3.6.0", 37 | "tap-status": "^1.0.1", 38 | "tape": "^5.5.2", 39 | "uglify-js": "^3.15.1" 40 | }, 41 | "dependencies": { 42 | "@mapbox/mapbox-gl-sync-move": "^0.3.0" 43 | }, 44 | "peerDependencies": { 45 | "maplibre-gl": ">=2.1.6" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | .maplibregl-compare { 2 | background-color: #fff; 3 | position: absolute; 4 | width: 2px; 5 | height: 100%; 6 | z-index: 1; 7 | } 8 | .maplibregl-compare .compare-swiper-vertical { 9 | background-color: #3887be; 10 | box-shadow: inset 0 0 0 2px #fff; 11 | display: inline-block; 12 | border-radius: 50%; 13 | position: absolute; 14 | width: 60px; 15 | height: 60px; 16 | top: 50%; 17 | left: -30px; 18 | margin: -30px 1px 0; 19 | color: #fff; 20 | cursor: ew-resize; 21 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiICAgd2lkdGg9IjYwIiAgIGhlaWdodD0iNjAiICAgdmVyc2lvbj0iMS4xIiAgIHZpZXdCb3g9IjAgMCA2MCA2MCIgICBpZD0ic3ZnNTQzNCIgICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjkxK2RldmVsK29zeG1lbnUgcjEyOTExIiAgIHNvZGlwb2RpOmRvY25hbWU9Imwtci5zdmciPiAgPG1ldGFkYXRhICAgICBpZD0ibWV0YWRhdGE1NDQ0Ij4gICAgPHJkZjpSREY+ICAgICAgPGNjOldvcmsgICAgICAgICByZGY6YWJvdXQ9IiI+ICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4gICAgICAgIDxkYzp0eXBlICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+ICAgICAgPC9jYzpXb3JrPiAgICA8L3JkZjpSREY+ICA8L21ldGFkYXRhPiAgPGRlZnMgICAgIGlkPSJkZWZzNTQ0MiIgLz4gIDxzb2RpcG9kaTpuYW1lZHZpZXcgICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIgICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IiAgICAgYm9yZGVyb3BhY2l0eT0iMSIgICAgIG9iamVjdHRvbGVyYW5jZT0iMTAiICAgICBncmlkdG9sZXJhbmNlPSIxMCIgICAgIGd1aWRldG9sZXJhbmNlPSIxMCIgICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIgICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTI4NiIgICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijc1MSIgICAgIGlkPSJuYW1lZHZpZXc1NDQwIiAgICAgc2hvd2dyaWQ9InRydWUiICAgICBpbmtzY2FwZTp6b29tPSI0IiAgICAgaW5rc2NhcGU6Y3g9IjI1Ljg4OTgzMSIgICAgIGlua3NjYXBlOmN5PSIzNC4zODE4MzMiICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMCIgICAgIGlua3NjYXBlOndpbmRvdy15PSIyMyIgICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjAiICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJzdmc1NDM0IiAgICAgaW5rc2NhcGU6b2JqZWN0LW5vZGVzPSJ0cnVlIiAgICAgaW5rc2NhcGU6c25hcC1zbW9vdGgtbm9kZXM9InRydWUiPiAgICA8aW5rc2NhcGU6Z3JpZCAgICAgICB0eXBlPSJ4eWdyaWQiICAgICAgIGlkPSJncmlkNTk4OSIgLz4gIDwvc29kaXBvZGk6bmFtZWR2aWV3PiAgPHBhdGggICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAgICAgZD0iTSAyNSAyNCBMIDE2IDMwIEwgMjUgMzYgTCAyNSAyNCB6IE0gMzUgMjQgTCAzNSAzNiBMIDQ0IDMwIEwgMzUgMjQgeiAiICAgICBpZD0icGF0aDU5OTUiIC8+PC9zdmc+); 22 | } 23 | 24 | .maplibregl-compare-horizontal { 25 | position: relative; 26 | width: 100%; 27 | height: 2px; 28 | } 29 | .maplibregl-compare .compare-swiper-horizontal { 30 | background-color: #3887be; 31 | box-shadow: inset 0 0 0 2px #fff; 32 | display: inline-block; 33 | border-radius: 50%; 34 | position: absolute; 35 | width: 60px; 36 | height: 60px; 37 | top: 50%; 38 | left: 50%; 39 | margin: -30px 1px 0; 40 | color: #fff; 41 | cursor: ns-resize; 42 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiICAgd2lkdGg9IjYwIiAgIGhlaWdodD0iNjAiICAgdmVyc2lvbj0iMS4xIiAgIHZpZXdCb3g9IjAgMCA2MCA2MCIgICBpZD0ic3ZnNTQzNCIgICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjkxK2RldmVsK29zeG1lbnUgcjEyOTExIiAgIHNvZGlwb2RpOmRvY25hbWU9Imwtci5zdmciPiAgPG1ldGFkYXRhICAgICBpZD0ibWV0YWRhdGE1NDQ0Ij4gICAgPHJkZjpSREY+ICAgICAgPGNjOldvcmsgICAgICAgICByZGY6YWJvdXQ9IiI+ICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4gICAgICAgIDxkYzp0eXBlICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+ICAgICAgPC9jYzpXb3JrPiAgICA8L3JkZjpSREY+ICA8L21ldGFkYXRhPiAgPGRlZnMgICAgIGlkPSJkZWZzNTQ0MiIgLz4gIDxzb2RpcG9kaTpuYW1lZHZpZXcgICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIgICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IiAgICAgYm9yZGVyb3BhY2l0eT0iMSIgICAgIG9iamVjdHRvbGVyYW5jZT0iMTAiICAgICBncmlkdG9sZXJhbmNlPSIxMCIgICAgIGd1aWRldG9sZXJhbmNlPSIxMCIgICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIgICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTI4NiIgICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijc1MSIgICAgIGlkPSJuYW1lZHZpZXc1NDQwIiAgICAgc2hvd2dyaWQ9InRydWUiICAgICBpbmtzY2FwZTp6b29tPSI0IiAgICAgaW5rc2NhcGU6Y3g9IjI1Ljg4OTgzMSIgICAgIGlua3NjYXBlOmN5PSIzNC4zODE4MzMiICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMCIgICAgIGlua3NjYXBlOndpbmRvdy15PSIyMyIgICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjAiICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJzdmc1NDM0IiAgICAgaW5rc2NhcGU6b2JqZWN0LW5vZGVzPSJ0cnVlIiAgICAgaW5rc2NhcGU6c25hcC1zbW9vdGgtbm9kZXM9InRydWUiPiAgICA8aW5rc2NhcGU6Z3JpZCAgICAgICB0eXBlPSJ4eWdyaWQiICAgICAgIGlkPSJncmlkNTk4OSIgLz4gIDwvc29kaXBvZGk6bmFtZWR2aWV3PiAgPHBhdGggICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAgICAgZD0iTSAyNSAyNCBMIDE2IDMwIEwgMjUgMzYgTCAyNSAyNCB6IE0gMzUgMjQgTCAzNSAzNiBMIDQ0IDMwIEwgMzUgMjQgeiAiICAgICBpZD0icGF0aDU5OTUiIC8+PC9zdmc+); 43 | transform: rotate(90deg); 44 | } 45 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | window.maplibregl = require('maplibre-gl'); 5 | require('../'); 6 | 7 | // Tests 8 | test('Compare', function (t) { 9 | var a = new maplibregl.Map({ 10 | container: document.createElement('div'), 11 | style: 'https://demotiles.maplibre.org/style.json', 12 | }); 13 | 14 | var b = new maplibregl.Map({ 15 | container: document.createElement('div'), 16 | style: 'https://demotiles.maplibre.org/style.json', 17 | }); 18 | 19 | // insert the container's into the document so compare.setSlider test works 20 | document.body.appendChild(a.getContainer()); 21 | document.body.appendChild(b.getContainer()); 22 | 23 | var container = document.createElement('div'); 24 | 25 | var compare = new maplibregl.Compare(a, b, container); 26 | 27 | t.ok(!!a.getContainer().style.clip, 'Map A is clipped'); 28 | t.ok(!!b.getContainer().style.clip, 'Map B is clipped'); 29 | 30 | b.jumpTo({ 31 | bearing: 20, 32 | center: { 33 | lat: 16, 34 | lng: -155, 35 | }, 36 | pitch: 20, 37 | zoom: 3, 38 | }); 39 | 40 | t.equals(a.getZoom(), 3, 'Zoom is synched'); 41 | t.equals(a.getPitch(), 20, 'Pitch is synched'); 42 | t.equals(a.getBearing(), 20, 'Bearing is synched'); 43 | t.equals(a.getCenter().lng, -155, 'Lng is synched'); 44 | t.equals(a.getCenter().lat, 16, 'Lat is synched'); 45 | 46 | compare.setSlider(20); 47 | 48 | t.equals(compare.currentPosition, 20, 'Slider has been moved'); 49 | 50 | compare.remove(); 51 | 52 | t.ok(!a.getContainer().style.clip, 'Map A is no longer clipped'); 53 | t.ok(!b.getContainer().style.clip, 'Map B is no longer clipped'); 54 | 55 | b.jumpTo({ 56 | bearing: 10, 57 | center: { 58 | lat: 26, 59 | lng: -105, 60 | }, 61 | pitch: 30, 62 | zoom: 5, 63 | }); 64 | 65 | t.equals(a.getZoom(), 3, 'Zoom is no longer synched'); 66 | t.equals(a.getPitch(), 20, 'Pitch is no longer synched'); 67 | t.equals(a.getBearing(), 20, 'Bearing is no longer synched'); 68 | t.equals(a.getCenter().lng, -155, 'Lng is no longer synched'); 69 | t.equals(a.getCenter().lat, 16, 'Lat is no longer synched'); 70 | 71 | t.end(); 72 | }); 73 | 74 | // close the smokestack window once tests are complete 75 | test('shutdown', function (t) { 76 | t.end(); 77 | setTimeout(function () { 78 | window.close(); 79 | }); 80 | }); 81 | --------------------------------------------------------------------------------