From 39e870af093cd67aa428dc44ac71e26f072c47e3 Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Fri, 9 Aug 2019 10:16:40 +0800 Subject: [PATCH] feat(control): add marker --- demos/hz.html | 6 +- demos/taxi.html | 15 ++- demos/vector2.html | 5 + src/component/css/l7.css | 145 +++++++++++++++++++++++++ src/component/marker.js | 146 ++++++++++++++++++++++++++ src/component/popup.js | 11 +- src/geom/shader/meshline_frag.glsl | 9 ++ src/geom/shader/meshline_vert.glsl | 10 +- src/index.js | 3 + src/layer/render/line/drawMeshLine.js | 1 + src/util/anchor.js | 18 ++++ src/util/dom.js | 16 +++ 12 files changed, 372 insertions(+), 13 deletions(-) create mode 100644 src/component/marker.js create mode 100644 src/util/anchor.js diff --git a/demos/hz.html b/demos/hz.html index fe2058e2a1..07a0fe490c 100644 --- a/demos/hz.html +++ b/demos/hz.html @@ -36,13 +36,13 @@ zIndex: 2 }) .source(data) - .size(2) + .size(1) .color('#ff893a') .animate({ - enable:false, + enable:true, interval:0.2, duration:5, - trailLength:0.1 + trailLength:0.2 }) .render(); }); diff --git a/demos/taxi.html b/demos/taxi.html index 359263b8bd..205eb1282b 100644 --- a/demos/taxi.html +++ b/demos/taxi.html @@ -31,16 +31,23 @@ maxZoom: 18 }); scene.on('loaded', () => { - $.get('https://gw.alipayobjects.com/os/rmsportal/kNDVHmyUWAKhWmWXmjxM.json', data => { + $.get('https://gw.alipayobjects.com/os/basement_prod/40ef2173-df66-4154-a8c0-785e93a5f18e.json', data => { scene.LineLayer({ zIndex: 2 }) .source(data) - //.color('#F08D41') + .size([2, 0]) + .shape('line') .color('#ff893a') - .animate({enable:true}) - //.render(); + .animate({ + enable:true, + interval:0.2, + duration:5, + trailLength:0.1 + }) + .render(); }); + $.get('https://gw.alipayobjects.com/os/rmsportal/vmvAxgsEwbpoSWbSYvix.json', data => { buildLayer = scene.PolygonLayer({ zIndex: 2 diff --git a/demos/vector2.html b/demos/vector2.html index 30cf610b4d..4c416d6127 100644 --- a/demos/vector2.html +++ b/demos/vector2.html @@ -114,6 +114,11 @@ "标注": layer3, }; const layerContr = new L7.Control.Layers({overlayers}).addTo(scene); + const popup = new L7.Popup({anchor:'left'}).setText('hello world') + const marker = new L7.Marker({color:'red'}) + .setLnglat([104.838088,34.075889 ]) + .setPopup(popup) + .addTo(scene); }); diff --git a/src/component/css/l7.css b/src/component/css/l7.css index f2de698cc0..93356e0a90 100644 --- a/src/component/css/l7.css +++ b/src/component/css/l7.css @@ -1,3 +1,32 @@ +.l7-marker { + position: absolute; + top: 0; + left: 0; + z-index: 5; +} +.l7-popup-anchor-top, +.l7-popup-anchor-top-left, +.l7-popup-anchor-top-right { + -webkit-flex-direction: column; + flex-direction: column; +} + +.l7-popup-anchor-bottom, +.l7-popup-anchor-bottom-left, +.l7-popup-anchor-bottom-right { + -webkit-flex-direction: column-reverse; + flex-direction: column-reverse; +} + +.l7-popup-anchor-left { + -webkit-flex-direction: row; + flex-direction: row; +} + +.l7-popup-anchor-right { + -webkit-flex-direction: row-reverse; + flex-direction: row-reverse; +} .l7-popup { position: absolute; top: 0; @@ -14,6 +43,122 @@ border: 10px solid transparent; z-index: 1; } +.l7-popup-anchor-top .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-top: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-top-left .l7-popup-tip { + -webkit-align-self: flex-start; + align-self: flex-start; + border-top: none; + border-left: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-top-right .l7-popup-tip { + -webkit-align-self: flex-end; + align-self: flex-end; + border-top: none; + border-right: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-bottom .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-bottom: none; + border-top-color: #fff; +} + +.l7-popup-anchor-bottom-left .l7-popup-tip { + -webkit-align-self: flex-start; + align-self: flex-start; + border-bottom: none; + border-left: none; + border-top-color: #fff; +} + +.l7-popup-anchor-bottom-right .l7-popup-tip { + -webkit-align-self: flex-end; + align-self: flex-end; + border-bottom: none; + border-right: none; + border-top-color: #fff; +} + +.l7-popup-anchor-left .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-left: none; + border-right-color: #fff; +} + +.l7-popup-anchor-right .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-right: none; + border-left-color: #fff; +} + +.l7-popup-close-button { + position: absolute; + right: 0; + top: 0; + border: 0; + border-radius: 0 3px 0 0; + cursor: pointer; + background-color: transparent; +} + +.l7-popup-close-button:hover { + background-color: rgba(0, 0, 0, 0.05); +} + +.l7-popup-content { + position: relative; + background: #fff; + border-radius: 3px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + padding: 10px 10px 15px; + pointer-events: auto; +} + +.l7-popup-anchor-top-left .l7-popup-content { + border-top-left-radius: 0; +} + +.l7-popup-anchor-top-right .l7-popup-content { + border-top-right-radius: 0; +} + +.l7-popup-anchor-bottom-left .l7-popup-content { + border-bottom-left-radius: 0; +} + +.l7-popup-anchor-bottom-right .l7-popup-content { + border-bottom-right-radius: 0; +} + +.l7-popup-track-pointer { + display: none; +} + +.l7-popup-track-pointer * { + pointer-events: none; + user-select: none; +} + +.l7-map:hover .l7-popup-track-pointer { + display: flex; +} + +.l7-map:active .l7-popup-track-pointer { + display: none; +} + .l7-popup-close-button { position: absolute; right: 0; diff --git a/src/component/marker.js b/src/component/marker.js new file mode 100644 index 0000000000..22f9b353ad --- /dev/null +++ b/src/component/marker.js @@ -0,0 +1,146 @@ +import Base from '../core/base'; +import { bindAll } from '../util/event'; +import { applyAnchorClass, anchorTranslate } from '../util/anchor'; +import * as DOM from '../util/dom'; +export default class Marker extends Base { + constructor(cfg) { + super({ + element: '', // DOM element + anchor: 'center', + offset: [ 0, 0 ], + color: '#2f54eb', + draggable: false, + ...cfg + + }); + bindAll([ + '_update', + '_onMove', + '_onUp', + '_addDragHandler', + '_onMapClick' + ], this); + this._init(); + } + _init() { + let element = this.get('element'); + if (!element) { + this._defaultMarker = true; + element = DOM.create('div'); + this.set('element', element); + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttributeNS(null, 'display', 'block'); + svg.setAttributeNS(null, 'height', '48px'); + svg.setAttributeNS(null, 'width', '48px'); + svg.setAttributeNS(null, 'viewBox', '0 0 1024 1024'); + + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttributeNS(null, 'd', 'M512 490.666667C453.12 490.666667 405.333333 442.88 405.333333 384 405.333333 325.12 453.12 277.333333 512 277.333333 570.88 277.333333 618.666667 325.12 618.666667 384 618.666667 442.88 570.88 490.666667 512 490.666667M512 85.333333C346.88 85.333333 213.333333 218.88 213.333333 384 213.333333 608 512 938.666667 512 938.666667 512 938.666667 810.666667 608 810.666667 384 810.666667 218.88 677.12 85.333333 512 85.333333Z'); + path.setAttributeNS(null, 'fill', this.get('color')); + svg.appendChild(path); + element.appendChild(svg); + + } + DOM.addClass(element, 'l7-marker'); + element.addEventListener('dragstart', e => { + e.preventDefault(); + }); + element.addEventListener('click', e => { + e.preventDefault(); + this._onMapClick(); + }); + applyAnchorClass(element, this.get('anchor'), 'marker'); + + this._popup = null; + + } + addTo(scene) { + this.remove(); + this._scene = scene; + this._scene.getContainer().appendChild(this.get('element')); + this._scene.on('camerachange', this._update); + this.setDraggable(this.get('draggable')); + this._update(); + // this._scene.on('click', this._onMapClick); + return this; + } + + remove() { + if (this._scene) { + this._scene.off('click', this._onMapClick); + this._scene.off('move', this._update); + this._scene.off('moveend', this._update); + this._scene.off('mousedown', this._addDragHandler); + this._scene.off('touchstart', this._addDragHandler); + this._scene.off('mouseup', this._onUp); + this._scene.off('touchend', this._onUp); + delete this._scene; + } + DOM.remove(this.get('element')); + if (this._popup) this._popup.remove(); + return this; + } + setLnglat(lngLat) { + this._lngLat = lngLat; + if (this._popup) this._popup.setLnglat(this._lngLat); + return this; + } + getLngLat() { + return this._lngLat; + } + + getElement() { + return this.get('element'); + } + + setPopup(popup) { + this._popup = popup; + if (this._lngLat) this._popup.setLnglat(this._lngLat); + return this; + } + + togglePopup() { + const popup = this._popup; + if (!popup) return this; + else if (popup.isOpen()) popup.remove(); + else popup.addTo(this._scene); + return this; + } + + getPopup() { + return this._popup; + } + + getOffset() { + + } + + setDraggable() { + + } + + isDraggable() { + return this._draggable; + } + _update() { + if (!this._scene) return; + this._updatePosition(); + DOM.setTransform(this.get('element'), `${anchorTranslate[ this.get('anchor')]}`); + + } + _onMapClick() { + this._scene.emit('click'); // 触发map点击事件,关闭其他popup + const element = this.get('element'); + + if (this._popup && element) { + this.togglePopup(); + } + } + _updatePosition() { + if (!this._scene) { return; } + const pos = this._pos = this._scene.lngLatToContainer(this._lngLat); + this.get('element').style.left = pos.x + 'px'; + this.get('element').style.top = pos.y + 'px'; + + } +} diff --git a/src/component/popup.js b/src/component/popup.js index 22e99f69e1..9c27d12099 100644 --- a/src/component/popup.js +++ b/src/component/popup.js @@ -1,6 +1,7 @@ import Base from '../core/base'; import { bindAll } from '../util/event'; import * as DOM from '../util/dom'; +import { applyAnchorClass, anchorTranslate } from '../util/anchor'; export default class Popup extends Base { constructor(cfg) { super({ @@ -28,12 +29,13 @@ export default class Popup extends Base { this._update(lngLat); return this; } + _update() { const hasPosition = this.lngLat; if (!this._scene || !hasPosition || !this._content) { return; } if (!this._container) { this._container = this.creatDom('div', 'l7-popup', this._scene.getContainer()); - // this._tip = this.creatDom('div', 'l7-popup-tip', this._container); + this._tip = this.creatDom('div', 'l7-popup-tip', this._container); this._container.appendChild(this._content); if (this.get('className')) { this.get('className').split(' ').forEach(name => @@ -45,13 +47,17 @@ export default class Popup extends Base { } this._updatePosition(); + DOM.setTransform(this._container, `${anchorTranslate[this.get('anchor')]}`); + applyAnchorClass(this._container, this.get('anchor'), 'popup'); } + _updatePosition() { if (!this._scene) { return; } const pos = this._scene.lngLatToContainer(this.lngLat); this._container.style.left = pos.x + 'px'; this._container.style.top = pos.y + 'px'; } + setHTML(html) { const frag = window.document.createDocumentFragment(); const temp = window.document.createElement('body'); @@ -124,4 +130,7 @@ export default class Popup extends Base { this.emit('close'); return this; } + isOpen() { + return !!this._scene; + } } diff --git a/src/geom/shader/meshline_frag.glsl b/src/geom/shader/meshline_frag.glsl index debc84c992..e786fd293b 100644 --- a/src/geom/shader/meshline_frag.glsl +++ b/src/geom/shader/meshline_frag.glsl @@ -20,6 +20,12 @@ varying float v_texture_y; varying float v_texture_percent; #endif +#ifdef ANIMATE +uniform float u_duration : 2.0; +uniform float u_interval : 1.0; +uniform float u_trailLength : 0.2; +#endif + void main() { #ifdef TEXTURE float texture_y_fract = fract(v_texture_y); @@ -40,6 +46,9 @@ void main() { gl_FragColor.a *= u_opacity; #endif #ifdef ANIMATE + float alpha =1.0 - fract( mod(1.0- v_distance_ratio,u_interval)* (1.0/u_interval) + u_time / u_duration); + alpha = (alpha + u_trailLength -1.0) / u_trailLength; + v_time = clamp(alpha,0.,1.); gl_FragColor.a *= v_time; #endif // anti-alias diff --git a/src/geom/shader/meshline_vert.glsl b/src/geom/shader/meshline_vert.glsl index f8031ad4e3..0d742e04c6 100644 --- a/src/geom/shader/meshline_vert.glsl +++ b/src/geom/shader/meshline_vert.glsl @@ -57,11 +57,11 @@ void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position.xy + offset.xy, 0., 1.0); // gl_Position.z -=0.8 * gl_Position.w; - #ifdef ANIMATE - float alpha =1.0 - fract( mod(1.0- distance_ratio,u_interval)* (1.0/u_interval) + u_time / u_duration); - alpha = (alpha + u_trailLength -1.0) / u_trailLength; - v_time = clamp(alpha,0.,1.); - #endif + // #ifdef ANIMATE + // float alpha =1.0 - fract( mod(1.0- distance_ratio,u_interval)* (1.0/u_interval) + u_time / u_duration); + // alpha = (alpha + u_trailLength -1.0) / u_trailLength; + // v_time = clamp(alpha,0.,1.); + // #endif // picking if(pickingId == u_activeId) { diff --git a/src/index.js b/src/index.js index 4f8da9a530..7d52591507 100755 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,9 @@ import { registerParser, registerTransform } from './source'; import { registerInteraction, getInteraction } from './interaction'; import { registerLayer } from './layer'; import Popup from './component/popup'; +import Marker from './component/marker'; import * as Control from './component/control'; + const version = Global.version; const exported = { version, @@ -20,6 +22,7 @@ const exported = { registerInteraction, getInteraction, Popup, + Marker, Control }; export default exported; diff --git a/src/layer/render/line/drawMeshLine.js b/src/layer/render/line/drawMeshLine.js index 6b81e0da3c..953085b537 100644 --- a/src/layer/render/line/drawMeshLine.js +++ b/src/layer/render/line/drawMeshLine.js @@ -70,6 +70,7 @@ export default function DrawLine(layerData, layer, buffer) { u_trailLength: trailLength }); lineMaterial.setDefinesvalue('ANIMATE', true); + lineMaterial.setDefinesvalue('DASHLINE', true); } return lineMesh; } diff --git a/src/util/anchor.js b/src/util/anchor.js new file mode 100644 index 0000000000..98cc65be71 --- /dev/null +++ b/src/util/anchor.js @@ -0,0 +1,18 @@ +export const anchorTranslate = { + center: 'translate(-50%,-50%)', + top: 'translate(-50%,0)', + 'top-left': 'translate(0,0)', + 'top-right': 'translate(-100%,0)', + bottom: 'translate(-50%,-100%)', + 'bottom-left': 'translate(0,-100%)', + 'bottom-right': 'translate(-100%,-100%)', + left: 'translate(0,-50%)', + right: 'translate(-100%,-50%)' +}; +export function applyAnchorClass(element, anchor, prefix) { + const classList = element.classList; + for (const key in anchorTranslate) { + classList.remove(`l7-${prefix}-anchor-${key}`); + } + classList.add(`l7-${prefix}-anchor-${anchor}`); +} diff --git a/src/util/dom.js b/src/util/dom.js index 9c41f1f823..f71fa6c088 100644 --- a/src/util/dom.js +++ b/src/util/dom.js @@ -1,4 +1,14 @@ import * as Util from './util'; +const docStyle = window.document.documentElement.style; +function testProp(props) { + if (!docStyle) return props[0]; + for (let i = 0; i < props.length; i++) { + if (props[i] in docStyle) { + return props[i]; + } + } + return props[0]; +} export function create(tagName, className, container) { const el = document.createElement(tagName); el.className = className || ''; @@ -78,3 +88,9 @@ export function empty(el) { el.removeChild(el.firstChild); } } + +const transformProp = testProp([ 'transform', 'WebkitTransform' ]); + +export function setTransform(el, value) { + el.style[transformProp] = value; +}