leaflet.label.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. /*
  2. Leaflet.label, a plugin that adds labels to markers and vectors for Leaflet powered maps.
  3. (c) 2012-2013, Jacob Toye, Smartrak
  4. https://github.com/Leaflet/Leaflet.label
  5. http://leafletjs.com
  6. https://github.com/jacobtoye
  7. */
  8. (function () {
  9. //var L = window.L;
  10. /*
  11. * Leaflet.label assumes that you have already included the Leaflet library.
  12. */
  13. //L.labelVersion = '0.2.2-dev';
  14. L.Label = (L.Layer ? L.Layer : L.Class).extend({
  15. includes: L.Mixin.Events,
  16. options: {
  17. className: '',
  18. clickable: false,
  19. direction: 'auto',
  20. noHide: false,
  21. offset: [13, -16], // 6 (width of the label triangle) + 6 (padding)
  22. opacity: 1,
  23. zoomAnimation: true
  24. },
  25. initialize: function (options, source) {
  26. L.setOptions(this, options);
  27. this._source = source;
  28. this._animated = L.Browser.any3d && this.options.zoomAnimation;
  29. this._isOpen = false;
  30. },
  31. onAdd: function (map) {
  32. this._map = map;
  33. this._pane = this.options.pane ? map._panes[this.options.pane] :
  34. this._source instanceof L.Marker ? map._panes.markerPane : map._panes.popupPane;
  35. if (!this._container) {
  36. this._initLayout();
  37. }
  38. this._pane.appendChild(this._container);
  39. this._initInteraction();
  40. this._update();
  41. this.setOpacity(this.options.opacity);
  42. map
  43. .on('moveend', this._onMoveEnd, this)
  44. .on('viewreset', this._onViewReset, this);
  45. if (this._animated) {
  46. map.on('zoomanim', this._zoomAnimation, this);
  47. }
  48. if (L.Browser.touch && !this.options.noHide) {
  49. L.DomEvent.on(this._container, 'click', this.close, this);
  50. map.on('click', this.close, this);
  51. }
  52. },
  53. onRemove: function (map) {
  54. this._pane.removeChild(this._container);
  55. map.off({
  56. zoomanim: this._zoomAnimation,
  57. moveend: this._onMoveEnd,
  58. viewreset: this._onViewReset
  59. }, this);
  60. this._removeInteraction();
  61. this._map = null;
  62. },
  63. setLatLng: function (latlng) {
  64. this._latlng = L.latLng(latlng);
  65. if (this._map) {
  66. this._updatePosition();
  67. }
  68. return this;
  69. },
  70. setContent: function (content) {
  71. // Backup previous content and store new content
  72. this._previousContent = this._content;
  73. this._content = content;
  74. this._updateContent();
  75. return this;
  76. },
  77. close: function () {
  78. var map = this._map;
  79. if (map) {
  80. if (L.Browser.touch && !this.options.noHide) {
  81. L.DomEvent.off(this._container, 'click', this.close);
  82. map.off('click', this.close, this);
  83. }
  84. map.removeLayer(this);
  85. }
  86. },
  87. updateZIndex: function (zIndex) {
  88. this._zIndex = zIndex;
  89. if (this._container && this._zIndex) {
  90. this._container.style.zIndex = zIndex;
  91. }
  92. },
  93. setOpacity: function (opacity) {
  94. this.options.opacity = opacity;
  95. if (this._container) {
  96. L.DomUtil.setOpacity(this._container, opacity);
  97. }
  98. },
  99. _initLayout: function () {
  100. this._container = L.DomUtil.create('div', 'leaflet-label ' + this.options.className + ' leaflet-zoom-animated');
  101. this.updateZIndex(this._zIndex);
  102. },
  103. _update: function () {
  104. if (!this._map) { return; }
  105. this._container.style.visibility = 'hidden';
  106. this._updateContent();
  107. this._updatePosition();
  108. this._container.style.visibility = '';
  109. },
  110. _updateContent: function () {
  111. if (!this._content || !this._map || this._prevContent === this._content) {
  112. return;
  113. }
  114. if (typeof this._content === 'string') {
  115. this._container.innerHTML = this._content;
  116. this._prevContent = this._content;
  117. this._labelWidth = this._container.offsetWidth;
  118. }else {
  119. this._container.innerHTML = "";
  120. this._container.appendChild(this._content);
  121. this._prevContent = this._content;
  122. this._labelWidth = this._container.offsetWidth;
  123. }
  124. //==S== 修改标记
  125. L.DomUtil.create('div','leaflet-label-tips',this._container);
  126. //==E== 修改标记
  127. },
  128. _updatePosition: function () {
  129. var pos = this._map.latLngToLayerPoint(this._latlng);
  130. this._setPosition(pos);
  131. },
  132. _setPosition: function (pos) {
  133. var map = this._map,
  134. container = this._container,
  135. centerPoint = map.latLngToContainerPoint(map.getCenter()),
  136. labelPoint = map.layerPointToContainerPoint(pos),
  137. direction = this.options.direction,
  138. labelWidth = this._labelWidth,
  139. offset = L.point(this.options.offset);
  140. // position to the right (right or auto & needs to)
  141. if (direction === 'right' || direction === 'auto' && labelPoint.x < centerPoint.x) {
  142. L.DomUtil.addClass(container, 'leaflet-label-right');
  143. L.DomUtil.removeClass(container, 'leaflet-label-left');
  144. pos = pos.add(offset);
  145. } else { // position to the left
  146. L.DomUtil.addClass(container, 'leaflet-label-left');
  147. L.DomUtil.removeClass(container, 'leaflet-label-right');
  148. pos = pos.add(L.point(-offset.x - labelWidth, offset.y));
  149. }
  150. L.DomUtil.setPosition(container, pos);
  151. },
  152. _zoomAnimation: function (opt) {
  153. var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
  154. this._setPosition(pos);
  155. },
  156. _onMoveEnd: function () {
  157. if (!this._animated || this.options.direction === 'auto') {
  158. this._updatePosition();
  159. }
  160. },
  161. _onViewReset: function (e) {
  162. /* if map resets hard, we must update the label */
  163. if (e && e.hard) {
  164. this._update();
  165. }
  166. },
  167. _initInteraction: function () {
  168. if (!this.options.clickable) { return; }
  169. var container = this._container,
  170. events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];
  171. L.DomUtil.addClass(container, 'leaflet-clickable');
  172. L.DomEvent.on(container, 'click', this._onMouseClick, this);
  173. for (var i = 0; i < events.length; i++) {
  174. L.DomEvent.on(container, events[i], this._fireMouseEvent, this);
  175. }
  176. },
  177. _removeInteraction: function () {
  178. if (!this.options.clickable) { return; }
  179. var container = this._container,
  180. events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];
  181. L.DomUtil.removeClass(container, 'leaflet-clickable');
  182. L.DomEvent.off(container, 'click', this._onMouseClick, this);
  183. for (var i = 0; i < events.length; i++) {
  184. L.DomEvent.off(container, events[i], this._fireMouseEvent, this);
  185. }
  186. },
  187. _onMouseClick: function (e) {
  188. if (this.hasEventListeners(e.type)) {
  189. L.DomEvent.stopPropagation(e);
  190. }
  191. this.fire(e.type, {
  192. originalEvent: e
  193. });
  194. },
  195. _fireMouseEvent: function (e) {
  196. this.fire(e.type, {
  197. originalEvent: e
  198. });
  199. // TODO proper custom event propagation
  200. // this line will always be called if marker is in a FeatureGroup
  201. if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) {
  202. L.DomEvent.preventDefault(e);
  203. }
  204. if (e.type !== 'mousedown') {
  205. L.DomEvent.stopPropagation(e);
  206. } else {
  207. L.DomEvent.preventDefault(e);
  208. }
  209. }
  210. });
  211. // This object is a mixin for L.Marker and L.CircleMarker. We declare it here as both need to include the contents.
  212. L.BaseMarkerMethods = {
  213. showLabel: function () {
  214. if (this.label && this._map) {
  215. this.label.setLatLng(this._latlng);
  216. this._map.showLabel(this.label);
  217. }
  218. return this;
  219. },
  220. hideLabel: function () {
  221. if (this.label) {
  222. this.label.close();
  223. }
  224. return this;
  225. },
  226. setLabelNoHide: function (noHide) {
  227. if (this._labelNoHide === noHide) {
  228. return;
  229. }
  230. this._labelNoHide = noHide;
  231. if (noHide) {
  232. this._removeLabelRevealHandlers();
  233. this.showLabel();
  234. } else {
  235. this._addLabelRevealHandlers();
  236. this.hideLabel();
  237. }
  238. },
  239. bindLabel: function (content, options) {
  240. if(this.label){
  241. return false;
  242. }
  243. var labelAnchor = this.options.icon ? this.options.icon.options.labelAnchor : this.options.labelAnchor,
  244. anchor = L.point(labelAnchor) || L.point(0, 0);
  245. anchor = anchor.add(L.Label.prototype.options.offset);
  246. if (options && options.offset) {
  247. anchor = anchor.add(options.offset);
  248. }
  249. options = L.Util.extend({offset: anchor}, options);
  250. this._labelNoHide = options.noHide;
  251. if (!this.label) {
  252. if (!this._labelNoHide) {
  253. this._addLabelRevealHandlers();
  254. }
  255. this
  256. .on('remove', this.hideLabel, this)
  257. .on('move', this._moveLabel, this)
  258. .on('add', this._onMarkerAdd, this);
  259. this._hasLabelHandlers = true;
  260. }
  261. this.label = new L.Label(options, this)
  262. .setContent(content);
  263. return this;
  264. },
  265. unbindLabel: function () {
  266. if (this.label) {
  267. this.hideLabel();
  268. this.label = null;
  269. if (this._hasLabelHandlers) {
  270. if (!this._labelNoHide) {
  271. this._removeLabelRevealHandlers();
  272. }
  273. this
  274. .off('remove', this.hideLabel, this)
  275. .off('move', this._moveLabel, this)
  276. .off('add', this._onMarkerAdd, this);
  277. }
  278. this._hasLabelHandlers = false;
  279. }
  280. return this;
  281. },
  282. updateLabelContent: function (content) {
  283. if (this.label) {
  284. this.label.setContent(content);
  285. }
  286. },
  287. getLabel: function () {
  288. return this.label;
  289. },
  290. _onMarkerAdd: function () {
  291. if (this._labelNoHide) {
  292. this.showLabel();
  293. }
  294. },
  295. _addLabelRevealHandlers: function () {
  296. this
  297. .on('mouseover', this.showLabel, this)
  298. .on('mouseout', this.hideLabel, this);
  299. if (L.Browser.touch) {
  300. this.on('click', this.showLabel, this);
  301. }
  302. },
  303. _removeLabelRevealHandlers: function () {
  304. this
  305. .off('mouseover', this.showLabel, this)
  306. .off('mouseout', this.hideLabel, this);
  307. if (L.Browser.touch) {
  308. this.off('click', this.showLabel, this);
  309. }
  310. },
  311. _moveLabel: function (e) {
  312. this.label.setLatLng(e.latlng);
  313. }
  314. };
  315. // Add in an option to icon that is used to set where the label anchor is
  316. L.Icon.Default.mergeOptions({
  317. labelAnchor: new L.Point(4, -15)
  318. });
  319. // Have to do this since Leaflet is loaded before this plugin and initializes
  320. // L.Marker.options.icon therefore missing our mixin above.
  321. L.Marker.mergeOptions({
  322. icon: new L.Icon.Default()
  323. });
  324. L.Marker.include(L.BaseMarkerMethods);
  325. L.Marker.include({
  326. _originalUpdateZIndex: L.Marker.prototype._updateZIndex,
  327. _updateZIndex: function (offset) {
  328. var zIndex = this._zIndex + offset;
  329. this._originalUpdateZIndex(offset);
  330. if (this.label) {
  331. this.label.updateZIndex(zIndex);
  332. }
  333. },
  334. _originalSetOpacity: L.Marker.prototype.setOpacity,
  335. setOpacity: function (opacity, labelHasSemiTransparency) {
  336. this.options.labelHasSemiTransparency = labelHasSemiTransparency;
  337. this._originalSetOpacity(opacity);
  338. },
  339. _originalUpdateOpacity: L.Marker.prototype._updateOpacity,
  340. _updateOpacity: function () {
  341. var absoluteOpacity = this.options.opacity === 0 ? 0 : 1;
  342. this._originalUpdateOpacity();
  343. if (this.label) {
  344. this.label.setOpacity(this.options.labelHasSemiTransparency ? this.options.opacity : absoluteOpacity);
  345. }
  346. },
  347. _originalSetLatLng: L.Marker.prototype.setLatLng,
  348. setLatLng: function (latlng) {
  349. if (this.label && !this._labelNoHide) {
  350. this.hideLabel();
  351. }
  352. return this._originalSetLatLng(latlng);
  353. }
  354. });
  355. // Add in an option to icon that is used to set where the label anchor is
  356. L.CircleMarker.mergeOptions({
  357. labelAnchor: new L.Point(-5, 5)
  358. });
  359. L.CircleMarker.include(L.BaseMarkerMethods);
  360. L.Path.include({
  361. bindLabel: function (content, options) {
  362. if (!this.label || this.label.options !== options) {
  363. this.label = new L.Label(options, this);
  364. }
  365. this.label.setContent(content);
  366. if (!this._showLabelAdded) {
  367. this
  368. .on('mouseover', this._showLabel, this)
  369. .on('mousemove', this._moveLabel, this)
  370. .on('mouseout remove', this._hideLabel, this);
  371. if (L.Browser.touch) {
  372. this.on('click', this._showLabel, this);
  373. }
  374. this._showLabelAdded = true;
  375. }
  376. return this;
  377. },
  378. unbindLabel: function () {
  379. if (this.label) {
  380. this._hideLabel();
  381. this.label = null;
  382. this._showLabelAdded = false;
  383. this
  384. .off('mouseover', this._showLabel, this)
  385. .off('mousemove', this._moveLabel, this)
  386. .off('mouseout remove', this._hideLabel, this);
  387. }
  388. return this;
  389. },
  390. updateLabelContent: function (content) {
  391. if (this.label) {
  392. this.label.setContent(content);
  393. }
  394. },
  395. _showLabel: function (e) {
  396. this.label.setLatLng(e.latlng);
  397. this._map.showLabel(this.label);
  398. },
  399. _moveLabel: function (e) {
  400. this.label.setLatLng(e.latlng);
  401. },
  402. _hideLabel: function () {
  403. this.label.close();
  404. }
  405. });
  406. L.Map.include({
  407. showLabel: function (label) {
  408. return this.addLayer(label);
  409. }
  410. });
  411. L.FeatureGroup.include({
  412. // TODO: remove this when AOP is supported in Leaflet, need this as we cannot put code in removeLayer()
  413. clearLayers: function () {
  414. this.unbindLabel();
  415. this.eachLayer(this.removeLayer, this);
  416. return this;
  417. },
  418. bindLabel: function (content, options) {
  419. return this.invoke('bindLabel', content, options);
  420. },
  421. unbindLabel: function () {
  422. return this.invoke('unbindLabel');
  423. },
  424. updateLabelContent: function (content) {
  425. this.invoke('updateLabelContent', content);
  426. }
  427. });
  428. }(window, document));