UNPKG

@maplibre/maplibre-gl-compare

Version:
230 lines (199 loc) 7.71 kB
"use strict"; var syncMove = require("@mapbox/mapbox-gl-sync-move"); var EventEmitter = require("events").EventEmitter; /** * @param {Object} a The first MapLibre GL Map * @param {Object} b The second MapLibre GL Map * @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. * @param {Object} options * @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). * @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. * @example * var compare = new maplibregl.Compare(beforeMap, afterMap, '#wrapper', { * orientation: 'vertical', * mousemove: true * }); * @see [Swipe between maps](https://maplibre.org/maplibre-gl-js-docs/plugins/) */ function Compare(a, b, container, options) { this.options = options ? options : {}; this._mapA = a; this._mapB = b; this._horizontal = this.options.orientation === "horizontal"; 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._ev = new EventEmitter(); this._swiper = document.createElement("div"); this._swiper.className = this._horizontal ? "compare-swiper-horizontal" : "compare-swiper-vertical"; this._controlContainer = document.createElement("div"); this._controlContainer.className = this._horizontal ? "maplibregl-compare maplibregl-compare-horizontal" : "maplibregl-compare"; this._controlContainer.className = this._controlContainer.className; this._controlContainer.appendChild(this._swiper); if (typeof container === "string" && document.body.querySelectorAll) { // get container with a selector var appendTarget = document.body.querySelectorAll(container)[0]; if (!appendTarget) { throw new Error("Cannot find element with specified container selector."); } appendTarget.appendChild(this._controlContainer); } else if (container instanceof Element && container.appendChild) { // get container directly container.appendChild(this._controlContainer); } else { throw new Error( "Invalid container specified. Must be CSS selector or HTML element." ); } this._bounds = b.getContainer().getBoundingClientRect(); var swiperPosition = (this._horizontal ? this._bounds.height : this._bounds.width) / 2; this._setPosition(swiperPosition); this._clearSync = syncMove(a, b); this._onResize = function () { this._bounds = b.getContainer().getBoundingClientRect(); if (this.currentPosition) this._setPosition(this.currentPosition); }.bind(this); b.on("resize", this._onResize); 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._controlContainer.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) { x = Math.min( x, this._horizontal ? this._bounds.height : this._bounds.width ); var pos = this._horizontal ? "translate(0, " + x + "px)" : "translate(" + x + "px, 0)"; this._controlContainer.style.transform = pos; this._controlContainer.style.WebkitTransform = pos; var clipA = this._horizontal ? "rect(0, 999em, " + x + "px, 0)" : "rect(0, " + x + "px, " + this._bounds.height + "px, 0)"; var clipB = this._horizontal ? "rect(" + x + "px, 999em, " + this._bounds.height + "px,0)" : "rect(0, 999em, " + this._bounds.height + "px," + x + "px)"; this._mapA.getContainer().style.clip = clipA; this._mapB.getContainer().style.clip = clipB; this.currentPosition = x; }, _onMove: function (e) { if (this.options && this.options.mousemove) { this._setPointerEvents(e.touches ? "auto" : "none"); } this._horizontal ? this._setPosition(this._getY(e)) : this._setPosition(this._getX(e)); }, _onMouseUp: function () { document.removeEventListener("mousemove", this._onMove); document.removeEventListener("mouseup", this._onMouseUp); this.fire("slideend", { currentPosition: this.currentPosition }); }, _onTouchEnd: function () { document.removeEventListener("touchmove", this._onMove); document.removeEventListener("touchend", this._onTouchEnd); this.fire("slideend", { currentPosition: this.currentPosition }); }, _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; }, _getY: function (e) { e = e.touches ? e.touches[0] : e; var y = e.clientY - this._bounds.top; if (y < 0) y = 0; if (y > this._bounds.height) y = this._bounds.height; return y; }, /** * Set the position of the slider. * * @param {number} x Slider position in pixels from left/top. */ setSlider: function (x) { this._setPosition(x); }, /** * Adds a listener for events of a specified type. * * @param {string} type The event type to listen for; one of `slideend`. * @param {Function} listener The function to be called when the event is fired. * @returns {Compare} `this` */ on: function (type, fn) { this._ev.on(type, fn); return this; }, /** * Fire an event of a specified type. * * @param {string} type The event type to fire; one of `slideend`. * @param {Object} data Data passed to the event listener. * @returns {Compare} `this` */ fire: function (type, data) { this._ev.emit(type, data); return this; }, /** * Removes an event listener previously added with `Compare#on`. * * @param {string} type The event type previously used to install the listener. * @param {Function} listener The function previously installed as a listener. * @returns {Compare} `this` */ off: function (type, fn) { this._ev.removeListener(type, fn); return this; }, remove: function () { this._clearSync(); this._mapB.off("resize", this._onResize); var aContainer = this._mapA.getContainer(); if (!!aContainer) { aContainer.style.clip = null; aContainer.removeEventListener("mousemove", this._onMove); } var bContainer = this._mapB.getContainer(); if (!!bContainer) { bContainer.style.clip = null; bContainer.removeEventListener("mousemove", this._onMove); } this._swiper.removeEventListener("mousedown", this._onDown); this._swiper.removeEventListener("touchstart", this._onDown); this._controlContainer.remove(); }, }; if (window.maplibregl) { maplibregl.Compare = Compare; } else if (typeof module !== "undefined") { module.exports = Compare; }