This change adds a stability value to the api annotation, with 'experimental' as default value. enum, typedef and event annotations are never exportable, but api annotations are needed there to make them appear in the docs. Nested typedefs are no longer inlined recursively, because the resulting tables get too wide with the current template.
305 lines
8.7 KiB
JavaScript
305 lines
8.7 KiB
JavaScript
// FIXME works for View2D only
|
|
// FIXME should possibly show tooltip when dragging?
|
|
|
|
goog.provide('ol.control.ZoomSlider');
|
|
|
|
goog.require('goog.array');
|
|
goog.require('goog.asserts');
|
|
goog.require('goog.dom');
|
|
goog.require('goog.dom.TagName');
|
|
goog.require('goog.events');
|
|
goog.require('goog.events.Event');
|
|
goog.require('goog.events.EventType');
|
|
goog.require('goog.fx.Dragger');
|
|
goog.require('goog.fx.Dragger.EventType');
|
|
goog.require('goog.math');
|
|
goog.require('goog.math.Rect');
|
|
goog.require('goog.style');
|
|
goog.require('ol.animation');
|
|
goog.require('ol.control.Control');
|
|
goog.require('ol.css');
|
|
goog.require('ol.easing');
|
|
|
|
|
|
/**
|
|
* @define {number} Animation duration.
|
|
*/
|
|
ol.control.ZOOMSLIDER_ANIMATION_DURATION = 200;
|
|
|
|
|
|
|
|
/**
|
|
* A slider type of control for zooming.
|
|
*
|
|
* Example:
|
|
*
|
|
* map.addControl(new ol.control.ZoomSlider());
|
|
*
|
|
* @constructor
|
|
* @extends {ol.control.Control}
|
|
* @param {olx.control.ZoomSliderOptions=} opt_options Zoom slider options.
|
|
* @todo api
|
|
*/
|
|
ol.control.ZoomSlider = function(opt_options) {
|
|
|
|
var options = goog.isDef(opt_options) ? opt_options : {};
|
|
|
|
/**
|
|
* Will hold the current resolution of the view.
|
|
*
|
|
* @type {number|undefined}
|
|
* @private
|
|
*/
|
|
this.currentResolution_ = undefined;
|
|
|
|
/**
|
|
* The direction of the slider. Will be determined from actual display of the
|
|
* container and defaults to ol.control.ZoomSlider.direction.VERTICAL.
|
|
*
|
|
* @type {ol.control.ZoomSlider.direction}
|
|
* @private
|
|
*/
|
|
this.direction_ = ol.control.ZoomSlider.direction.VERTICAL;
|
|
|
|
/**
|
|
* Whether the slider is initialized.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
this.sliderInitialized_ = false;
|
|
|
|
var className = goog.isDef(options.className) ?
|
|
options.className : 'ol-zoomslider';
|
|
var thumbElement = goog.dom.createDom(goog.dom.TagName.DIV,
|
|
[className + '-thumb', ol.css.CLASS_UNSELECTABLE]);
|
|
var sliderElement = goog.dom.createDom(goog.dom.TagName.DIV,
|
|
[className, ol.css.CLASS_UNSELECTABLE], thumbElement);
|
|
|
|
/**
|
|
* @type {goog.fx.Dragger}
|
|
* @private
|
|
*/
|
|
this.dragger_ = new goog.fx.Dragger(thumbElement);
|
|
this.registerDisposable(this.dragger_);
|
|
|
|
goog.events.listen(this.dragger_, [
|
|
goog.fx.Dragger.EventType.DRAG,
|
|
goog.fx.Dragger.EventType.END
|
|
], this.handleSliderChange_, undefined, this);
|
|
|
|
goog.events.listen(sliderElement, goog.events.EventType.CLICK,
|
|
this.handleContainerClick_, false, this);
|
|
goog.events.listen(thumbElement, goog.events.EventType.CLICK,
|
|
goog.events.Event.stopPropagation);
|
|
|
|
goog.base(this, {
|
|
element: sliderElement
|
|
});
|
|
};
|
|
goog.inherits(ol.control.ZoomSlider, ol.control.Control);
|
|
|
|
|
|
/**
|
|
* The enum for available directions.
|
|
*
|
|
* @enum {number}
|
|
*/
|
|
ol.control.ZoomSlider.direction = {
|
|
VERTICAL: 0,
|
|
HORIZONTAL: 1
|
|
};
|
|
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
ol.control.ZoomSlider.prototype.setMap = function(map) {
|
|
goog.base(this, 'setMap', map);
|
|
if (!goog.isNull(map)) {
|
|
map.render();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Initializes the slider element. This will determine and set this controls
|
|
* direction_ and also constrain the dragging of the thumb to always be within
|
|
* the bounds of the container.
|
|
*
|
|
* @private
|
|
*/
|
|
ol.control.ZoomSlider.prototype.initSlider_ = function() {
|
|
var container = this.element,
|
|
thumb = goog.dom.getFirstElementChild(container),
|
|
elemSize = goog.style.getContentBoxSize(container),
|
|
thumbBounds = goog.style.getBounds(thumb),
|
|
thumbMargins = goog.style.getMarginBox(thumb),
|
|
thumbBorderBox = goog.style.getBorderBox(thumb),
|
|
w = elemSize.width -
|
|
thumbMargins.left - thumbMargins.right -
|
|
thumbBorderBox.left - thumbBorderBox.right -
|
|
thumbBounds.width,
|
|
h = elemSize.height -
|
|
thumbMargins.top - thumbMargins.bottom -
|
|
thumbBorderBox.top - thumbBorderBox.bottom -
|
|
thumbBounds.height,
|
|
limits;
|
|
if (elemSize.width > elemSize.height) {
|
|
this.direction_ = ol.control.ZoomSlider.direction.HORIZONTAL;
|
|
limits = new goog.math.Rect(0, 0, w, 0);
|
|
} else {
|
|
this.direction_ = ol.control.ZoomSlider.direction.VERTICAL;
|
|
limits = new goog.math.Rect(0, 0, 0, h);
|
|
}
|
|
this.dragger_.setLimits(limits);
|
|
this.sliderInitialized_ = true;
|
|
};
|
|
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
ol.control.ZoomSlider.prototype.handleMapPostrender = function(mapEvent) {
|
|
if (goog.isNull(mapEvent.frameState)) {
|
|
return;
|
|
}
|
|
goog.asserts.assert(
|
|
goog.isDefAndNotNull(mapEvent.frameState.view2DState));
|
|
if (!this.sliderInitialized_) {
|
|
this.initSlider_();
|
|
}
|
|
var res = mapEvent.frameState.view2DState.resolution;
|
|
if (res !== this.currentResolution_) {
|
|
this.currentResolution_ = res;
|
|
this.positionThumbForResolution_(res);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* @param {goog.events.BrowserEvent} browserEvent The browser event to handle.
|
|
* @private
|
|
*/
|
|
ol.control.ZoomSlider.prototype.handleContainerClick_ = function(browserEvent) {
|
|
var map = this.getMap();
|
|
var view = map.getView().getView2D();
|
|
var resolution;
|
|
var amountDragged = this.amountDragged_(browserEvent.offsetX,
|
|
browserEvent.offsetY);
|
|
resolution = this.resolutionForAmount_(amountDragged);
|
|
goog.asserts.assert(goog.isDef(resolution));
|
|
map.beforeRender(ol.animation.zoom({
|
|
resolution: resolution,
|
|
duration: ol.control.ZOOMSLIDER_ANIMATION_DURATION,
|
|
easing: ol.easing.easeOut
|
|
}));
|
|
resolution = view.constrainResolution(resolution);
|
|
view.setResolution(resolution);
|
|
};
|
|
|
|
|
|
/**
|
|
* Positions the thumb inside its container according to the given resolution.
|
|
*
|
|
* @param {number} res The res.
|
|
* @private
|
|
*/
|
|
ol.control.ZoomSlider.prototype.positionThumbForResolution_ = function(res) {
|
|
var amount = this.amountForResolution_(res),
|
|
dragger = this.dragger_,
|
|
thumb = goog.dom.getFirstElementChild(this.element);
|
|
|
|
if (this.direction_ == ol.control.ZoomSlider.direction.HORIZONTAL) {
|
|
var left = dragger.limits.left + dragger.limits.width * amount;
|
|
goog.style.setPosition(thumb, left);
|
|
} else {
|
|
var top = dragger.limits.top + dragger.limits.height * amount;
|
|
goog.style.setPosition(thumb, dragger.limits.left, top);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Calculates the amount the thumb has been dragged to allow for calculation
|
|
* of the corresponding resolution.
|
|
*
|
|
* @param {number} x Pixel position relative to the left of the slider.
|
|
* @param {number} y Pixel position relative to the top of the slider.
|
|
* @return {number} The amount the thumb has been dragged.
|
|
* @private
|
|
*/
|
|
ol.control.ZoomSlider.prototype.amountDragged_ = function(x, y) {
|
|
var draggerLimits = this.dragger_.limits,
|
|
amount = 0;
|
|
if (this.direction_ === ol.control.ZoomSlider.direction.HORIZONTAL) {
|
|
amount = (x - draggerLimits.left) / draggerLimits.width;
|
|
} else {
|
|
amount = (y - draggerLimits.top) / draggerLimits.height;
|
|
}
|
|
return amount;
|
|
};
|
|
|
|
|
|
/**
|
|
* Calculates the corresponding resolution of the thumb by the amount it has
|
|
* been dragged from its minimum.
|
|
*
|
|
* @param {number} amount The amount the thumb has been dragged.
|
|
* @return {number} The corresponding resolution.
|
|
* @private
|
|
*/
|
|
ol.control.ZoomSlider.prototype.resolutionForAmount_ = function(amount) {
|
|
// FIXME do we really need this affine transform?
|
|
amount = (goog.math.clamp(amount, 0, 1) - 1) * -1;
|
|
var fn = this.getMap().getView().getView2D().getResolutionForValueFunction();
|
|
return fn(amount);
|
|
};
|
|
|
|
|
|
/**
|
|
* Determines an amount of dragging relative to this minimum position by the
|
|
* given resolution.
|
|
*
|
|
* @param {number} res The resolution to get the amount for.
|
|
* @return {number} The corresponding value (between 0 and 1).
|
|
* @private
|
|
*/
|
|
ol.control.ZoomSlider.prototype.amountForResolution_ = function(res) {
|
|
var fn = this.getMap().getView().getView2D().getValueForResolutionFunction();
|
|
var value = fn(res);
|
|
// FIXME do we really need this affine transform?
|
|
return (value - 1) * -1;
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles the user caused changes of the slider thumb and adjusts the
|
|
* resolution of our map accordingly. Will be called both while dragging and
|
|
* when dragging ends.
|
|
*
|
|
* @param {goog.fx.DragDropEvent} e The dragdropevent.
|
|
* @private
|
|
*/
|
|
ol.control.ZoomSlider.prototype.handleSliderChange_ = function(e) {
|
|
var map = this.getMap();
|
|
var view = map.getView().getView2D();
|
|
var resolution;
|
|
if (e.type === goog.fx.Dragger.EventType.DRAG) {
|
|
var amountDragged = this.amountDragged_(e.left, e.top);
|
|
resolution = this.resolutionForAmount_(amountDragged);
|
|
if (resolution !== this.currentResolution_) {
|
|
this.currentResolution_ = resolution;
|
|
view.setResolution(resolution);
|
|
}
|
|
} else {
|
|
goog.asserts.assert(goog.isDef(this.currentResolution_));
|
|
map.beforeRender(ol.animation.zoom({
|
|
resolution: this.currentResolution_,
|
|
duration: ol.control.ZOOMSLIDER_ANIMATION_DURATION,
|
|
easing: ol.easing.easeOut
|
|
}));
|
|
resolution = view.constrainResolution(this.currentResolution_);
|
|
view.setResolution(resolution);
|
|
}
|
|
};
|