diff --git a/src/ol/control/ZoomSlider.js b/src/ol/control/ZoomSlider.js index 5563b7527f..70f783d9fb 100644 --- a/src/ol/control/ZoomSlider.js +++ b/src/ol/control/ZoomSlider.js @@ -5,7 +5,7 @@ import ViewHint from '../ViewHint.js'; import Control from '../control/Control.js'; import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js'; import {easeOut} from '../easing.js'; -import {listen} from '../events.js'; +import {listen, unlistenByKey} from '../events.js'; import {stopPropagation} from '../events/Event.js'; import EventType from '../events/EventType.js'; import {clamp} from '../math.js'; @@ -57,6 +57,12 @@ class ZoomSlider extends Control { render: options.render || render }); + /** + * @type {!Array.} + * @private + */ + this.dragListenerKeys_ = []; + /** * Will hold the current resolution of the view. * @@ -231,6 +237,17 @@ class ZoomSlider extends Control { this.previousX_ = event.clientX; this.previousY_ = event.clientY; this.dragging_ = true; + + if (this.dragListenerKeys_.length === 0) { + const drag = this.handleDraggerDrag_; + const end = this.handleDraggerEnd_; + this.dragListenerKeys_.push( + listen(document, EventType.MOUSEMOVE, drag, this), + listen(document, PointerEventType.POINTERMOVE, drag, this), + listen(document, EventType.MOUSEUP, end, this), + listen(document, PointerEventType.POINTERUP, end, this) + ); + } } } @@ -273,6 +290,8 @@ class ZoomSlider extends Control { this.dragging_ = false; this.previousX_ = undefined; this.previousY_ = undefined; + this.dragListenerKeys_.forEach(unlistenByKey); + this.dragListenerKeys_.length = 0; } } diff --git a/test/spec/ol/control/zoomslider.test.js b/test/spec/ol/control/zoomslider.test.js index 6af8a03cba..52ab1bcafa 100644 --- a/test/spec/ol/control/zoomslider.test.js +++ b/test/spec/ol/control/zoomslider.test.js @@ -119,6 +119,7 @@ describe('ol.control.ZoomSlider', function() { dragger.dispatchEvent(event); expect(control.currentResolution_).to.be(16); expect(control.dragging_).to.be(true); + expect(control.dragListenerKeys_.length).to.be(4); event.type = 'pointermove'; event.clientX = 6 * control.widthLimit_ / 8; event.clientY = 0; @@ -131,8 +132,44 @@ describe('ol.control.ZoomSlider', function() { event.type = 'pointerup'; dragger.dispatchEvent(event); expect(control.currentResolution_).to.be(1); + expect(control.dragListenerKeys_.length).to.be(0); expect(control.dragging_).to.be(false); }); + it('[horizontal] handles a drag sequence ending outside its bounds', function() { + const control = new ZoomSlider(); + map.addControl(control); + map.getView().setZoom(0); + control.element.style.width = '500px'; + control.element.style.height = '10px'; + control.element.firstChild.style.width = '100px'; + control.element.firstChild.style.height = '10px'; + map.renderSync(); + const dragger = control.dragger_; + const event = new PointerEvent('pointerdown', { + target: control.element.firstElementChild + }); + event.clientX = control.widthLimit_; + event.clientY = 0; + dragger.dispatchEvent(event); + expect(control.currentResolution_).to.be(16); + expect(control.dragging_).to.be(true); + expect(control.dragListenerKeys_.length).to.be(4); + event.type = 'pointermove'; + event.clientX = 6 * control.widthLimit_ / 8; + event.clientY = 0; + dragger.dispatchEvent(event); + expect(control.currentResolution_).to.be(4); + event.type = 'pointermove'; + event.clientX = 12 * control.widthLimit_ / 8; + event.clientY = 0; + dragger.dispatchEvent(event); + event.type = 'pointerup'; + event.target = 'document'; + dragger.dispatchEvent(event); + expect(control.dragListenerKeys_.length).to.be(0); + expect(control.dragging_).to.be(false); + expect(control.currentResolution_).to.be(16); + }); it('[vertical] handles a drag sequence', function() { const control = new ZoomSlider(); control.element.style.width = '10px'; @@ -151,6 +188,7 @@ describe('ol.control.ZoomSlider', function() { dragger.dispatchEvent(event); expect(control.currentResolution_).to.be(0.0625); expect(control.dragging_).to.be(true); + expect(control.dragListenerKeys_.length).to.be(4); event.type = 'pointermove'; event.clientX = 0; event.clientY = 2 * control.heightLimit_ / 8; @@ -163,6 +201,41 @@ describe('ol.control.ZoomSlider', function() { event.type = 'pointerup'; dragger.dispatchEvent(event); expect(control.currentResolution_).to.be(1); + expect(control.dragListenerKeys_.length).to.be(0); + expect(control.dragging_).to.be(false); + }); + it('[vertical] handles a drag sequence ending outside its bounds', function() { + const control = new ZoomSlider(); + control.element.style.width = '10px'; + control.element.style.height = '100px'; + control.element.firstChild.style.width = '10px'; + control.element.firstChild.style.height = '20px'; + map.addControl(control); + map.getView().setZoom(8); + map.renderSync(); + const dragger = control.dragger_; + const event = new PointerEvent('pointerdown', { + target: control.element.firstElementChild + }); + event.clientX = 0; + event.clientY = 0; + dragger.dispatchEvent(event); + expect(control.currentResolution_).to.be(0.0625); + expect(control.dragging_).to.be(true); + expect(control.dragListenerKeys_.length).to.be(4); + event.type = 'pointermove'; + event.clientX = 0; + event.clientY = 2 * control.heightLimit_ / 8; + dragger.dispatchEvent(event); + expect(control.currentResolution_).to.be(0.25); + event.type = 'pointermove'; + event.clientX = 0; + event.clientY = 12 * control.heightLimit_ / 8; + dragger.dispatchEvent(event); + event.type = 'pointerup'; + dragger.dispatchEvent(event); + expect(control.currentResolution_).to.be(16); + expect(control.dragListenerKeys_.length).to.be(0); expect(control.dragging_).to.be(false); }); });