/* Copyright (c) 2015 Jean-Marc VIGLINO, released under the CeCILL-B license (French BSD license) (http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt). */ import ol_control_Control from 'ol/control/Control.js' /** * @classdesc Swipe Control. * @fires moving * @constructor * @extends {ol_control_Control} * @param {Object=} Control options. * @param {ol.layer|Array} options.layers layers to swipe * @param {ol.layer|Array} options.rightLayers layers to swipe on right side * @param {string} options.className control class name * @param {number} options.position position property of the swipe [0,1], default 0.5 * @param {string} options.orientation orientation property (vertical|horizontal), default vertical */ var ol_control_Swipe = class olcontrolSwipe extends ol_control_Control { constructor(options) { options = options || {}; var element = document.createElement('div'); super({ element: element }); element.className = (options.className || 'ol-swipe') + ' ol-unselectable ol-control'; var button = document.createElement('button'); element.appendChild(button); element.addEventListener('mousedown', this.move.bind(this)); element.addEventListener('touchstart', this.move.bind(this)); // An array of listener on layer postcompose this.precomposeRight_ = this.precomposeRight.bind(this); this.precomposeLeft_ = this.precomposeLeft.bind(this); this.postcompose_ = this.postcompose.bind(this); this.layers = []; if (options.layers) this.addLayer(options.layers, false); if (options.rightLayers) this.addLayer(options.rightLayers, true); this.on('propertychange', function (e) { if (this.getMap()) { try { this.getMap().renderSync(); } catch (e) { /* ok */ } } if (this.get('orientation') === "horizontal") { this.element.style.top = this.get('position') * 100 + "%"; this.element.style.left = ""; } else { if (this.get('orientation') !== "vertical") this.set('orientation', "vertical"); this.element.style.left = this.get('position') * 100 + "%"; this.element.style.top = ""; } if (e.key === 'orientation') { this.element.classList.remove("horizontal", "vertical"); this.element.classList.add(this.get('orientation')); } // Force VectorImage to refresh if (!this.isMoving) { this.layers.forEach(function (l) { if (l.layer.getImageRatio) l.layer.changed(); }); } }.bind(this)); this.set('position', options.position || 0.5); this.set('orientation', options.orientation || 'vertical'); } /** * Set the map instance the control associated with. * @param {_ol_Map_} map The map instance. */ setMap(map) { var i; var l; if (this.getMap()) { for (i = 0; i < this.layers.length; i++) { l = this.layers[i]; if (l.right) l.layer.un(['precompose', 'prerender'], this.precomposeRight_); else l.layer.un(['precompose', 'prerender'], this.precomposeLeft_); l.layer.un(['postcompose', 'postrender'], this.postcompose_); } try { this.getMap().renderSync(); } catch (e) { /* ok */ } } super.setMap(map); if (map) { this._listener = []; for (i = 0; i < this.layers.length; i++) { l = this.layers[i]; if (l.right) l.layer.on(['precompose', 'prerender'], this.precomposeRight_); else l.layer.on(['precompose', 'prerender'], this.precomposeLeft_); l.layer.on(['postcompose', 'postrender'], this.postcompose_); } try { map.renderSync(); } catch (e) { /* ok */ } } } /** @private */ isLayer_(layer) { for (var k = 0; k < this.layers.length; k++) { if (this.layers[k].layer === layer) return k; } return -1; } /** Add a layer to clip * @param {ol.layer|Array} layer to clip * @param {bool} add layer in the right part of the map, default left. */ addLayer(layers, right) { if (!(layers instanceof Array)) layers = [layers]; for (var i = 0; i < layers.length; i++) { var l = layers[i]; if (this.isLayer_(l) < 0) { this.layers.push({ layer: l, right: right }); if (this.getMap()) { if (right) l.on(['precompose', 'prerender'], this.precomposeRight_); else l.on(['precompose', 'prerender'], this.precomposeLeft_); l.on(['postcompose', 'postrender'], this.postcompose_); try { this.getMap().renderSync(); } catch (e) { /* ok */ } } } } } /** Remove all layers */ removeLayers() { var layers = []; this.layers.forEach(function (l) { layers.push(l.layer); }); this.removeLayer(layers); } /** Remove a layer to clip * @param {ol.layer|Array} layer to clip */ removeLayer(layers) { if (!(layers instanceof Array)) layers = [layers]; for (var i = 0; i < layers.length; i++) { var k = this.isLayer_(layers[i]); if (k >= 0 && this.getMap()) { if (this.layers[k].right) layers[i].un(['precompose', 'prerender'], this.precomposeRight_); else layers[i].un(['precompose', 'prerender'], this.precomposeLeft_); layers[i].un(['postcompose', 'postrender'], this.postcompose_); this.layers.splice(k, 1); } } if (this.getMap()) { try { this.getMap().renderSync(); } catch (e) { /* ok */ } } } /** Get visible rectangle * @returns {ol.extent} */ getRectangle() { var s; if (this.get('orientation') === 'vertical') { s = this.getMap().getSize(); return [0, 0, s[0] * this.get('position'), s[1]]; } else { s = this.getMap().getSize(); return [0, 0, s[0], s[1] * this.get('position')]; } } /** @private */ move(e) { var self = this; var l; if (!this._movefn) this._movefn = this.move.bind(this); switch (e.type) { case 'touchcancel': case 'touchend': case 'mouseup': { self.isMoving = false; ["mouseup", "mousemove", "touchend", "touchcancel", "touchmove"] .forEach(function (eventName) { document.removeEventListener(eventName, self._movefn); }); // Force VectorImage to refresh this.layers.forEach(function (l) { if (l.layer.getImageRatio) l.layer.changed(); }); break; } case 'mousedown': case 'touchstart': { self.isMoving = true; ["mouseup", "mousemove", "touchend", "touchcancel", "touchmove"] .forEach(function (eventName) { document.addEventListener(eventName, self._movefn); }); } // fallthrough case 'mousemove': case 'touchmove': { if (self.isMoving) { if (self.get('orientation') === 'vertical') { var pageX = e.pageX || (e.touches && e.touches.length && e.touches[0].pageX) || (e.changedTouches && e.changedTouches.length && e.changedTouches[0].pageX); if (!pageX) break; pageX -= self.getMap().getTargetElement().getBoundingClientRect().left + window.pageXOffset - document.documentElement.clientLeft; l = self.getMap().getSize()[0]; var w = l - Math.min(Math.max(0, l - pageX), l); l = w / l; self.set('position', l); self.dispatchEvent({ type: 'moving', size: [w, self.getMap().getSize()[1]], position: [l, 0] }); } else { var pageY = e.pageY || (e.touches && e.touches.length && e.touches[0].pageY) || (e.changedTouches && e.changedTouches.length && e.changedTouches[0].pageY); if (!pageY) break; pageY -= self.getMap().getTargetElement().getBoundingClientRect().top + window.pageYOffset - document.documentElement.clientTop; l = self.getMap().getSize()[1]; var h = l - Math.min(Math.max(0, l - pageY), l); l = h / l; self.set('position', l); self.dispatchEvent({ type: 'moving', size: [self.getMap().getSize()[0], h], position: [0, l] }); } } break; } default: break; } } /** @private */ _transformPt(e, pt) { var tr = e.inversePixelTransform; var x = pt[0]; var y = pt[1]; pt[0] = tr[0] * x + tr[2] * y + tr[4]; pt[1] = tr[1] * x + tr[3] * y + tr[5]; return pt; } /** @private */ _drawRect(e, pts) { var tr = e.inversePixelTransform; if (tr) { var r = [ [pts[0][0], pts[0][1]], [pts[0][0], pts[1][1]], [pts[1][0], pts[1][1]], [pts[1][0], pts[0][1]], [pts[0][0], pts[0][1]] ]; e.context.save(); // Rotate VectorImages if (e.target.getImageRatio) { var rot = -Math.atan2(e.frameState.pixelToCoordinateTransform[1], e.frameState.pixelToCoordinateTransform[0]); e.context.translate(e.frameState.size[0] / 2, e.frameState.size[1] / 2); e.context.rotate(rot); e.context.translate(-e.frameState.size[0] / 2, -e.frameState.size[1] / 2); } r.forEach(function (pt, i) { pt = [ (pt[0] * tr[0] - pt[1] * tr[1] + tr[4]), (-pt[0] * tr[2] + pt[1] * tr[3] + tr[5]) ]; if (!i) { e.context.moveTo(pt[0], pt[1]); } else { e.context.lineTo(pt[0], pt[1]); } }); e.context.restore(); } else { var ratio = e.frameState.pixelRatio; e.context.rect(pts[0][0] * ratio, pts[0][1] * ratio, pts[1][0] * ratio, pts[1][1] * ratio); } } /** @private */ precomposeLeft(e) { var ctx = e.context; if (ctx instanceof WebGLRenderingContext) { if (e.type === 'prerender') { // Clear if (this._lefttime != e.frameState.time) { ctx.clearColor(0, 0, 0, 0); ctx.clear(ctx.COLOR_BUFFER_BIT); this._lefttime = e.frameState.time; } // Clip ctx.enable(ctx.SCISSOR_TEST); var mapSize = this.getMap().getSize(); // [width, height] in CSS pixels // get render coordinates and dimensions given CSS coordinates var bottomLeft = this._transformPt(e, [0, mapSize[1]]); var topRight = this._transformPt(e, [mapSize[0], 0]); var fullWidth = topRight[0] - bottomLeft[0]; var fullHeight = topRight[1] - bottomLeft[1]; var width, height; if (this.get('orientation') === "vertical") { width = Math.round(fullWidth * this.get('position')); height = fullHeight; } else { width = fullWidth; height = Math.round((fullHeight * this.get('position'))); bottomLeft[1] += fullHeight - height; } ctx.scissor(bottomLeft[0], bottomLeft[1], width, height); ctx.clearColor(0, 0, 0, 0); ctx.clear(ctx.COLOR_BUFFER_BIT); } } else { var size = e.frameState.size; ctx.save(); ctx.beginPath(); var pts = [[0, 0], [size[0], size[1]]]; if (this.get('orientation') === "vertical") { pts[1] = [ size[0] * .5 + this.getMap().getSize()[0] * (this.get('position') - .5), size[1] ]; } else { pts[1] = [ size[0], size[1] * .5 + this.getMap().getSize()[1] * (this.get('position') - .5) ]; } this._drawRect(e, pts); ctx.clip(); } } /** @private */ precomposeRight(e) { var ctx = e.context; if (ctx instanceof WebGLRenderingContext) { if (e.type === 'prerender') { // Clear if (this._righttime != e.frameState.time) { ctx.clearColor(0, 0, 0, 0); ctx.clear(ctx.COLOR_BUFFER_BIT); this._righttime = e.frameState.time; } // Clip ctx.enable(ctx.SCISSOR_TEST); var mapSize = this.getMap().getSize(); // [width, height] in CSS pixels // get render coordinates and dimensions given CSS coordinates var bottomLeft = this._transformPt(e, [0, mapSize[1]]); var topRight = this._transformPt(e, [mapSize[0], 0]); var fullWidth = topRight[0] - bottomLeft[0]; var fullHeight = topRight[1] - bottomLeft[1]; var width, height; if (this.get('orientation') === "vertical") { height = fullHeight; width = Math.round(fullWidth * (1 - this.get('position'))); bottomLeft[0] += fullWidth - width; } else { width = fullWidth; height = Math.round(fullHeight * (1 - this.get('position'))); } ctx.scissor(bottomLeft[0], bottomLeft[1], width, height); ctx.clearColor(0, 0, 0, 0); ctx.clear(ctx.COLOR_BUFFER_BIT); } } else { var size = e.frameState.size; ctx.save(); ctx.beginPath(); var pts = [[0, 0], [size[0], size[1]]]; if (this.get('orientation') === "vertical") { pts[0] = [ size[0] * .5 + this.getMap().getSize()[0] * (this.get('position') - .5), 0 ]; } else { pts[0] = [ 0, size[1] * .5 + this.getMap().getSize()[1] * (this.get('position') - .5) ]; } this._drawRect(e, pts); ctx.clip(); } } /** @private */ postcompose(e) { if (e.context instanceof WebGLRenderingContext) { if (e.type === 'postrender') { var gl = e.context; gl.disable(gl.SCISSOR_TEST); } } else { // restore context when decluttering is done (ol>=6) // https://github.com/openlayers/openlayers/issues/10096 if (e.target.getClassName && e.target.getClassName() !== 'ol-layer' && e.target.get('declutter')) { setTimeout(function () { e.context.restore(); }, 0); } else { e.context.restore(); } } } } export default ol_control_Swipe