357 lines
12 KiB
JavaScript
357 lines
12 KiB
JavaScript
/* Copyright (c) 2016 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_interaction_DragOverlay from './DragOverlay.js'
|
|
import {unByKey as ol_Observable_unByKey} from 'ol/Observable.js'
|
|
import ol_interaction_Interaction from 'ol/interaction/Interaction.js'
|
|
|
|
import ol_ext_element from '../util/element.js'
|
|
import ol_Overlay_Fixed from '../overlay/Fixed.js'
|
|
|
|
/** Handle a touch cursor to defer event position on overlay position
|
|
* It can be used as abstract base class used for creating subclasses.
|
|
* The TouchCursor interaction modifies map browser event coordinate and pixel properties to force pointer on the graphic cursor on the screen to any interaction that them.
|
|
* @constructor
|
|
* @extends {ol_interaction_DragOverlay}
|
|
* @param {olx.interaction.InteractionOptions} options Options
|
|
* @param {string} [options.className] cursor class name
|
|
* @param {ol.coordinate} [options.coordinate] position of the cursor
|
|
* @param {strin} [options.anchor="left"] anchor position (left, center or right)
|
|
* @param {Array<*>} [options.buttons] an array of buttons
|
|
* @param {number} [options.maxButtons=5] maximum number of buttons (default 5)
|
|
*/
|
|
var ol_interaction_TouchCursor = class olinteractionTouchCursor extends ol_interaction_DragOverlay {
|
|
constructor(options) {
|
|
options = options || {}
|
|
|
|
// Add Overlay
|
|
var overlay = new ol_Overlay_Fixed({
|
|
className: ('ol-touch-cursor ' + (options.className || '')).trim(),
|
|
positioning: 'top-left',
|
|
element: ol_ext_element.create('DIV', {}),
|
|
stopEvent: false,
|
|
})
|
|
|
|
super({
|
|
centerOnClick: false,
|
|
//offset: [-20,-20],
|
|
overlays: overlay
|
|
})
|
|
|
|
this.overlay = overlay;
|
|
this.setAnchor(options.anchor || 'left')
|
|
|
|
// List of listerner on the object
|
|
this._listeners = {}
|
|
|
|
// Interaction to defer position on top of the interaction
|
|
// this is done to enable other coordinates manipulation inserted after the interaction (snapping)
|
|
this.ctouch = new ol_interaction_Interaction({
|
|
handleEvent: function (e) {
|
|
if (!/drag/.test(e.type) && this.getMap()) {
|
|
e.coordinate = this.overlay.getPosition()
|
|
e.pixel = this.getMap().getPixelFromCoordinate(e.coordinate)
|
|
this._lastEvent = e
|
|
} else {
|
|
var res = e.frameState.viewState.resolution
|
|
var cosa = Math.cos(e.frameState.viewState.rotation)
|
|
var sina = Math.sin(e.frameState.viewState.rotation)
|
|
var width = this.getOverlayElement().clientWidth / 2;
|
|
var style = getComputedStyle(this.getOverlayElement());
|
|
var offset = [-width - parseInt(style.marginLeft), -width - parseInt(style.marginTop)]
|
|
if (this._anchor === 'center') offset[0] = 0;
|
|
e.coordinate = [
|
|
e.coordinate[0] + cosa * offset[0] * res + sina * offset[1] * res,
|
|
e.coordinate[1] + sina * offset[0] * res - cosa * offset[1] * res
|
|
]
|
|
e.pixel = this.getMap().getPixelFromCoordinate(e.coordinate)
|
|
}
|
|
return true
|
|
}.bind(this)
|
|
})
|
|
// Force interaction on top
|
|
this.ctouch.set('onTop', true)
|
|
|
|
this.setPosition(options.coordinate, true)
|
|
this.set('maxButtons', options.maxButtons || 5)
|
|
|
|
if (options.buttons) {
|
|
if (options.buttons.length > this.get('maxButtons')){
|
|
this.set('maxButtons', options.buttons.length)
|
|
}
|
|
var elt = this.overlay.element
|
|
var begin = options.buttons.length > 4 ? 0 : 1
|
|
options.buttons.forEach((function (b, i) {
|
|
if (i < 5) {
|
|
ol_ext_element.create('DIV', {
|
|
className: ((b.className || '') + ' ol-button ol-button-' + (i + begin)).trim(),
|
|
html: ol_ext_element.create('DIV', { html: b.html }),
|
|
click: b.click,
|
|
on: b.on,
|
|
parent: elt
|
|
})
|
|
}
|
|
}))
|
|
}
|
|
|
|
// Replace events to handle click
|
|
var dragging = false
|
|
var start = false
|
|
this.on('dragstart', function (e) {
|
|
this._pixel = this.getMap().getPixelFromCoordinate(this.overlay.getPosition())
|
|
start = e
|
|
return !e.overlay
|
|
})
|
|
this.on('dragend', function (e) {
|
|
this._pixel = this.getMap().getPixelFromCoordinate(this.overlay.getPosition())
|
|
if (!e.overlay)
|
|
return true
|
|
if (dragging) {
|
|
this.dispatchEvent({
|
|
type: 'dragend',
|
|
dragging: dragging,
|
|
originalEvent: e.originalEvent,
|
|
frameState: e.frameState,
|
|
pixel: this._pixel,
|
|
coordinate: this.overlay.getPosition()
|
|
})
|
|
dragging = false
|
|
} else {
|
|
if (e.originalEvent.target === this.overlay.element) {
|
|
this.dispatchEvent({
|
|
type: 'click',
|
|
dragging: dragging,
|
|
originalEvent: e.originalEvent,
|
|
frameState: e.frameState,
|
|
pixel: this._pixel,
|
|
coordinate: this.overlay.getPosition()
|
|
})
|
|
}
|
|
}
|
|
return false
|
|
}.bind(this))
|
|
this.on('dragging', function (e) {
|
|
this._pixel = this.getMap().getPixelFromCoordinate(this.overlay.getPosition())
|
|
if (!e.overlay){
|
|
return true
|
|
}
|
|
dragging = true
|
|
if (start) {
|
|
this.dispatchEvent({
|
|
type: 'dragstart',
|
|
dragging: dragging,
|
|
originalEvent: start.originalEvent,
|
|
frameState: e.frameState,
|
|
pixel: this._pixel,
|
|
coordinate: start.coordinate
|
|
})
|
|
start = false
|
|
}
|
|
this.dispatchEvent({
|
|
type: 'dragging',
|
|
dragging: dragging,
|
|
originalEvent: e.originalEvent,
|
|
frameState: e.frameState,
|
|
pixel: this._pixel,
|
|
coordinate: this.overlay.getPosition()
|
|
})
|
|
return false
|
|
}.bind(this))
|
|
}
|
|
/**
|
|
* Remove the interaction from its current map, if any, and attach it to a new
|
|
* map, if any. Pass `null` to just remove the interaction from the current map.
|
|
* @param {_ol_Map_} map Map.
|
|
* @api stable
|
|
*/
|
|
setMap(map) {
|
|
// Reset
|
|
if (this.getMap()) {
|
|
this.getMap().removeInteraction(this.ctouch)
|
|
if (this.getActive())
|
|
this.getMap().removeOverlay(this.overlay)
|
|
}
|
|
for (let l in this._listeners) {
|
|
ol_Observable_unByKey(this._listeners[l])
|
|
}
|
|
this._listeners = {}
|
|
|
|
super.setMap(map)
|
|
|
|
// Set listeners
|
|
if (map) {
|
|
if (this.getActive()) {
|
|
map.addOverlay(this.overlay)
|
|
setTimeout(function () {
|
|
this.setPosition(this.getPosition() || map.getView().getCenter())
|
|
}.bind(this))
|
|
}
|
|
map.addInteraction(this.ctouch)
|
|
this._listeners.addInteraction = map.getInteractions().on('add', function (e) {
|
|
// Move on top
|
|
if (!e.element.get('onTop')) {
|
|
map.removeInteraction(this.ctouch)
|
|
map.addInteraction(this.ctouch)
|
|
}
|
|
}.bind(this))
|
|
}
|
|
}
|
|
/** Set anchor position
|
|
* @param {string} pos "left", "center" or "right"
|
|
* @api
|
|
*/
|
|
setAnchor(pos) {
|
|
var positions = ['left', 'right', 'center']
|
|
if (positions.indexOf(pos) >= 0) {
|
|
positions.forEach(function(k) {
|
|
this.getOverlayElement().classList.remove('ol-touch-cursor-'+k)
|
|
}.bind(this))
|
|
this.getOverlayElement().classList.add('ol-touch-cursor-'+pos)
|
|
this._anchor = pos;
|
|
}
|
|
}
|
|
/**
|
|
* Activate or deactivate the interaction.
|
|
* @param {boolean} active Active.
|
|
* @param {ol.coordinate|null} position position of the cursor (when activating), default viewport center.
|
|
* @observable
|
|
* @api
|
|
*/
|
|
setActive(b, position) {
|
|
if (b !== this.getActive()) {
|
|
if (this.ctouch) this.ctouch.setActive(b)
|
|
if (!b) {
|
|
this.setPosition()
|
|
this.overlay.element.classList.remove('active')
|
|
if (this._activate)
|
|
clearTimeout(this._activate)
|
|
if (this.getMap())
|
|
this.getMap().removeOverlay(this.overlay)
|
|
} else {
|
|
if (this.getMap()) {
|
|
this.getMap().addOverlay(this.overlay)
|
|
}
|
|
if (position) {
|
|
this.setPosition(position)
|
|
} else if (this.getMap()) {
|
|
this.setPosition(this.getMap().getView().getCenter())
|
|
}
|
|
this._activate = setTimeout(function () {
|
|
this.overlay.element.classList.add('active')
|
|
}.bind(this), 100)
|
|
}
|
|
super.setActive(b)
|
|
} else if (position) {
|
|
this.setPosition(position)
|
|
} else if (this.getMap()) {
|
|
this.setPosition(this.getMap().getView().getCenter())
|
|
}
|
|
}
|
|
/** Set the position of the target
|
|
* @param {ol.coordinate} coord
|
|
*/
|
|
setPosition(coord) {
|
|
this.overlay.setPosition(coord, true)
|
|
if (this.getMap() && coord) {
|
|
this._pixel = this.getMap().getPixelFromCoordinate(coord)
|
|
}
|
|
}
|
|
/** Offset the target position
|
|
* @param {ol.coordinate} coord
|
|
*/
|
|
offsetPosition(coord) {
|
|
var pos = this.overlay.getPosition()
|
|
if (pos) {
|
|
this.overlay.setPosition([pos[0] + coord[0], pos[1] + coord[1]])
|
|
}
|
|
}
|
|
/** Get the position of the target
|
|
* @return {ol.coordinate}
|
|
*/
|
|
getPosition() {
|
|
return this.overlay.getPosition()
|
|
}
|
|
/** Get pixel position
|
|
* @return {ol.pixel}
|
|
*/
|
|
getPixel() {
|
|
if (this.getMap()) {
|
|
return this.getMap().getPixelFromCoordinate(this.getPosition())
|
|
}
|
|
}
|
|
/** Get cursor overlay
|
|
* @return {ol.Overlay}
|
|
*/
|
|
getOverlay() {
|
|
return this.overlay
|
|
}
|
|
/** Get cursor overlay element
|
|
* @return {Element}
|
|
*/
|
|
getOverlayElement() {
|
|
return this.overlay.element
|
|
}
|
|
/** Get cursor button element
|
|
* @param {string|number} button the button className or the button index
|
|
* @return {Element}
|
|
*/
|
|
getButtonElement(button) {
|
|
if (typeof (button) === 'number')
|
|
return this.getOverlayElement().getElementsByClassName('ol-button')[button]
|
|
return this.getOverlayElement().getElementsByClassName(button)[0]
|
|
}
|
|
/** Remove a button element
|
|
* @param {string|number|undefined} button the button className or the button index, if undefined remove all buttons, default remove all
|
|
* @return {Element}
|
|
*/
|
|
removeButton(button) {
|
|
if (button === undefined) {
|
|
var buttons = this.getOverlayElement().getElementsByClassName('ol-button')
|
|
for (var i = buttons.length - 1; i >= 0; i--) {
|
|
this.getOverlayElement().removeChild(buttons[i])
|
|
}
|
|
} else {
|
|
var elt = this.getButtonElement(button)
|
|
if (elt)
|
|
this.getOverlayElement().removeChild(elt)
|
|
}
|
|
}
|
|
/** Add a button element
|
|
* @param {*} button
|
|
* @param {string} options.className button class name
|
|
* @param {DOMElement|string} options.html button content
|
|
* @param {function} options.click onclick function
|
|
* @param {*} options.on an object with
|
|
* @param {boolean} options.before
|
|
*/
|
|
addButton(b) {
|
|
var buttons = this.getOverlayElement().getElementsByClassName('ol-button')
|
|
var max = (this.get('maxButtons') || 5)
|
|
|
|
if (buttons.length >= max) {
|
|
console.error('[ol/interaction/TouchCursor~addButton] too many button on the cursor (max=' + max + ')...')
|
|
return
|
|
}
|
|
var button = ol_ext_element.create('DIV', {
|
|
className: ((b.className || '') + ' ol-button').trim(),
|
|
html: ol_ext_element.create('DIV', { html: b.html }),
|
|
click: b.click,
|
|
on: b.on
|
|
})
|
|
if (!b.before || buttons.length === 0) {
|
|
this.getOverlayElement().appendChild(button)
|
|
} else {
|
|
this.getOverlayElement().insertBefore(button, buttons[0])
|
|
}
|
|
// Reorder buttons
|
|
var start = buttons.length >= max ? 0 : 1
|
|
for (let i = 0; i < buttons.length; i++) {
|
|
buttons[i].className = buttons[i].className.replace(/ol-button-\d/g, '').trim() + ' ol-button-' + (i + start)
|
|
}
|
|
}
|
|
}
|
|
|
|
export default ol_interaction_TouchCursor
|