diff --git a/packages/webpack-uni-pages-loader/lib/platforms/h5.js b/packages/webpack-uni-pages-loader/lib/platforms/h5.js index af5131e8f6462a5fdd82d57f4f1528d146fce453..6c62e30fbb7daf1fc1249544dad71b27bd884cc2 100644 --- a/packages/webpack-uni-pages-loader/lib/platforms/h5.js +++ b/packages/webpack-uni-pages-loader/lib/platforms/h5.js @@ -427,16 +427,10 @@ module.exports = function (pagesJson, manifestJson, loader) { const networkTimeoutConfig = getNetworkTimeout(manifestJson) - let qqMapKey = 'XVXBZ-NDMC4-JOGUS-XGIEE-QVHDZ-AMFV2' - const sdkConfigs = h5.sdkConfigs || {} - if ( - sdkConfigs.maps && - sdkConfigs.maps.qqmap && - sdkConfigs.maps.qqmap.key - ) { - qqMapKey = sdkConfigs.maps.qqmap.key - } + + const qqMapKey = sdkConfigs.maps && sdkConfigs.maps.qqmap && sdkConfigs.maps.qqmap.key + const googleMapKey = sdkConfigs.maps && sdkConfigs.maps.google && sdkConfigs.maps.google.key let locale = manifestJson.locale locale = locale && locale.toUpperCase() !== 'AUTO' ? locale : '' @@ -456,6 +450,7 @@ global.__uniConfig.debug = ${manifestJson.debug === true}; global.__uniConfig.networkTimeout = ${JSON.stringify(networkTimeoutConfig)}; global.__uniConfig.sdkConfigs = ${JSON.stringify(sdkConfigs)}; global.__uniConfig.qqMapKey = ${JSON.stringify(qqMapKey)}; +global.__uniConfig.googleMapKey = ${JSON.stringify(googleMapKey)}; global.__uniConfig.locale = ${JSON.stringify(locale)}; global.__uniConfig.fallbackLocale = ${JSON.stringify(manifestJson.fallbackLocale)}; global.__uniConfig.locales = locales.keys().reduce((res,key)=>{const locale=key.replace(/\\.\\/(uni-app.)?(.*).json/,'$2');const messages = locales(key);Object.assign(res[locale]||(res[locale]={}),messages.common||messages);return res},{}); diff --git a/src/platforms/h5/helpers/location.js b/src/platforms/h5/helpers/location.js new file mode 100644 index 0000000000000000000000000000000000000000..827e89c4a5b99bf45097dade1106a74ab624cf73 --- /dev/null +++ b/src/platforms/h5/helpers/location.js @@ -0,0 +1,21 @@ +export const MapType = { + QQ: 'qq', + GOOGLE: 'google', + UNKNOWN: '' +} + +export function getMapInfo () { + let type = MapType.UNKNOWN + let key = '' + if (__uniConfig.qqMapKey) { + type = MapType.QQ + key = __uniConfig.qqMapKey + } else if (__uniConfig.googleMapKey) { + type = MapType.GOOGLE + key = __uniConfig.googleMapKey + } + return { + type, + key + } +} diff --git a/src/platforms/h5/view/components/map/index.vue b/src/platforms/h5/view/components/map/index.vue index c6248fd2e70f71518e03c245d7539a84353ecaf3..9088eda478c0b84b85b017cdfa366ea6f2775aa0 100644 --- a/src/platforms/h5/view/components/map/index.vue +++ b/src/platforms/h5/view/components/map/index.vue @@ -3,11 +3,25 @@ :id="id" v-on="$listeners" > +
-
+
@@ -19,124 +33,32 @@ import { } from 'uni-mixins' import { - hasOwn -} from 'uni-shared' + loadMaps +} from './maps' + +import mapMarker from './map-marker' -let maps -let callbacks -function loadMap (callback) { - if (maps) { - callback() - } else if (window.qq && window.qq.maps) { - maps = window.qq.maps - callback() - } else if (callbacks) { - callbacks.push(callback) +function getLat (latLng) { + if ('getLat' in latLng) { + return latLng.getLat() } else { - callbacks = [callback] - const key = __uniConfig.qqMapKey - const callbackName = '_callback' + Date.now() - window[callbackName] = function () { - delete window[callbackName] - maps = window.qq.maps - var Callout = maps.Callout = function (option = {}) { - this.option = option - var map = option.map - this.position = option.position - this.index = 1 - this.visible = this.alwaysVisible = option.display === 'ALWAYS' - this.init() - Object.defineProperty(this, 'onclick', { - setter (callback) { - this.div.onclick = callback - }, - getter () { - return this.div.onclick - } - }) - if (map) { - this.setMap(map) - } - } - Callout.prototype = new maps.Overlay() - Callout.prototype.init = function () { - var option = this.option - var div = this.div = document.createElement('div') - var divStyle = div.style - divStyle.position = 'absolute' - divStyle.whiteSpace = 'nowrap' - divStyle.transform = 'translateX(-50%) translateY(-100%)' - divStyle.zIndex = 1 - divStyle.boxShadow = option.boxShadow || 'none' - divStyle.display = this.visible ? 'block' : 'none' - var triangle = this.triangle = document.createElement('div') - triangle.setAttribute('style', - 'position: absolute;white-space: nowrap;border-width: 4px;border-style: solid;border-color: #fff transparent transparent;border-image: initial;font-size: 12px;padding: 0px;background-color: transparent;width: 0px;height: 0px;transform: translate(-50%, 100%);left: 50%;bottom: 0;' - ) - this.setStyle(option) - this.changed = function (key) { - divStyle.display = this.visible ? 'block' : 'none' - } - div.appendChild(triangle) - } - Callout.prototype.construct = function () { - var div = this.div - var panes = this.getPanes() - panes.floatPane.appendChild(div) - } - Callout.prototype.draw = function () { - var overlayProjection = this.getProjection() - if (!this.position || !this.div || !overlayProjection) { - return - } - var pixel = overlayProjection.fromLatLngToDivPixel(this.position) - var divStyle = this.div.style - divStyle.left = pixel.x + 'px' - divStyle.top = pixel.y + 'px' - } - Callout.prototype.destroy = function () { - this.div.parentNode.removeChild(this.div) - this.div = null - this.triangle = null - } - Callout.prototype.setOption = function (option) { - this.option = option - this.setPosition(option.position) - if (option.display === 'ALWAYS') { - this.alwaysVisible = this.visible = true - } else { - this.alwaysVisible = false - } - this.setStyle(option) - } - Callout.prototype.setStyle = function (option) { - var div = this.div - var divStyle = div.style - div.innerText = option.content - divStyle.lineHeight = (option.fontSize || 14) + 'px' - divStyle.fontSize = (option.fontSize || 14) + 'px' - divStyle.padding = (option.padding || 8) + 'px' - divStyle.color = option.color || '#000' - divStyle.borderRadius = (option.borderRadius || 0) + 'px' - divStyle.backgroundColor = option.bgColor || '#fff' - divStyle.marginTop = '-' + (option.top + 5) + 'px' - this.triangle.style.borderColor = `${option.bgColor || '#fff'} transparent transparent` - } - Callout.prototype.setPosition = function (position) { - this.position = position - this.draw() - } - callbacks.forEach(callback => callback()) - callbacks = null - } - const script = document.createElement('script') - script.src = `https://map.qq.com/api/js?v=2.exp&key=${key}&callback=${callbackName}&libraries=geometry` - document.body.appendChild(script) + return latLng.lat() + } +} + +function getLng (latLng) { + if ('getLng' in latLng) { + return latLng.getLng() + } else { + return latLng.lng() } } export default { name: 'Map', + components: { + mapMarker + }, mixins: [subscriber], props: { id: { @@ -194,6 +116,12 @@ export default { showLocation: { type: [Boolean, String], default: false + }, + libraries: { + type: Array, + default () { + return [] + } } }, data () { @@ -204,7 +132,6 @@ export default { }, isMapReady: false, isBoundsReady: false, - markersSync: [], polylineSync: [], circlesSync: [], controlsSync: [] @@ -222,56 +149,6 @@ export default { this._map.setZoom(Number(val) || 16) }) }, - markers (val, old) { - this.mapReady(() => { - var add = [] - var has = [] - var changed = [] - var changedOption = [] - var remove = [] - val.forEach(option => { - if (!('id' in option)) { - add.push(option) - } else { - var isOld = false - for (var index = 0; index < old.length; index++) { - var element = old[index] - if (!('id' in element)) { - old.splice(index--, 1) - continue - } - if (element.id !== option.id) { - continue - } - isOld = true - has.push(element.id) - if (JSON.stringify(element) !== JSON.stringify(option)) { - changed.push(element.id) - changedOption.push(option) - } - old.splice(index--, 1) - } - if (!isOld) { - add.push(option) - } - } - }) - var markers = this.markersSync - markers.forEach(marker => { - var id = marker.id - var index - if (has.indexOf(id) >= 0) { - if ((index = changed.indexOf(id)) >= 0) { - this.changeMarker(marker, changedOption[index]) - } - } else { - remove.push(marker) - } - }) - this.removeMarkers(remove) - this.createMarkers(add) - }) - }, polyline (val) { this.mapReady(() => { this.createPolyline() @@ -299,6 +176,7 @@ export default { } }, created () { + this._markers = {} var latitude = this.latitude var longitude = this.longitude if (latitude && longitude) { @@ -307,12 +185,12 @@ export default { } }, mounted () { - loadMap(() => { + loadMaps(this.libraries, result => { + this._maps = result this.init() }) }, beforeDestroy () { - this.removeMarkers(this.markersSync) this.removePolyline() this.removeCircles() this.removeControls() @@ -323,6 +201,7 @@ export default { type, data = {} }) { + const maps = this._maps function callback (res, err) { res = res || {} res.errMsg = `${type}:${err ? 'fail' + err : 'ok'}` @@ -341,8 +220,8 @@ export default { var latitude var longitude var center = this._map.getCenter() - latitude = center.getLat() - longitude = center.getLng() + latitude = getLat(center) + longitude = getLng(center) callback({ latitude: latitude, @@ -368,7 +247,10 @@ export default { var duration = data.duration var autoRotate = !!data.autoRotate var rotate = Number(data.rotate) ? data.rotate : 0 - var rotation = marker.getRotation() + let rotation = 0 + if ('getRotation' in marker) { + rotation = marker.getRotation() + } var a = marker.getPosition() var b = new maps.LatLng(destination.latitude, destination.longitude) var distance = maps.geometry.spherical.computeDistanceBetween(a, b) / 1000 @@ -410,8 +292,15 @@ export default { } rotate = maps.geometry.spherical.computeHeading(a, b) - lastRtate } - marker.setRotation(rotation + rotate) - marker.moveTo(b, speed) + if ('setRotation' in marker) { + marker.setRotation(rotation + rotate) + } + if ('moveTo' in marker) { + marker.moveTo(b, speed) + } else { + marker.setPosition(b) + maps.event.trigger(marker, 'moveend', {}) + } } catch (error) { callback(null, error) } @@ -427,12 +316,12 @@ export default { var northeast = latLngBounds.getNorthEast() callback({ southwest: { - latitude: southwest.getLat(), - longitude: southwest.getLng() + latitude: getLat(southwest), + longitude: getLng(southwest) }, northeast: { - latitude: northeast.getLat(), - longitude: northeast.getLng() + latitude: getLat(northeast), + longitude: getLng(northeast) } }) }) @@ -447,6 +336,7 @@ export default { } }, init () { + const maps = this._maps var center = new maps.LatLng(this.center.latitude, this.center.longitude) var map = this._map = new maps.Map(this.$refs.map, { center, @@ -457,6 +347,9 @@ export default { zoomControl: false, scaleControl: false, panControl: false, + fullscreenControl: false, + streetViewControl: false, + keyboardShortcuts: false, minZoom: 5, maxZoom: 18, draggable: true @@ -481,8 +374,8 @@ export default { return { scale: map.getZoom(), centerLocation: { - latitude: center.getLat(), - longitude: center.getLng() + latitude: getLat(center), + longitude: getLng(center) } } } @@ -503,14 +396,11 @@ export default { var latitude var longitude var center = map.getCenter() - latitude = center.getLat() - longitude = center.getLng() + latitude = getLat(center) + longitude = getLng(center) this.$emit('update:latitude', latitude) this.$emit('update:longitude', longitude) }) - if (this.markers && Array.isArray(this.markers) && this.markers.length) { - this.createMarkers(this.markers) - } if (this.polyline && Array.isArray(this.polyline) && this.polyline.length) { this.createPolyline() } @@ -532,6 +422,7 @@ export default { this.$emit('mapready') }, centerChange () { + const maps = this._maps var latitude = Number(this.latitude) var longitude = Number(this.longitude) if (latitude !== this.center.latitude || longitude !== this.center.longitude) { @@ -544,156 +435,8 @@ export default { } } }, - createMarkers (markerOptions) { - var map = this._map - var markers = this.markersSync - markerOptions.forEach(option => { - var marker = new maps.Marker({ - map, - flat: true, - autoRotation: false - }) - marker.id = option.id - this.changeMarker(marker, option) - maps.event.addListener(marker, 'click', e => { - var callout = marker.callout - if (callout) { - var div = callout.div - var parent = div.parentNode - if (!callout.alwaysVisible) { - callout.set('visible', !callout.visible) - } - if (callout.visible) { - parent.removeChild(div) - parent.appendChild(div) - } - } - hasOwn(option, 'id') && this.$trigger('markertap', {}, { - markerId: option.id - }) - }) - markers.push(marker) - }) - }, - changeMarker (marker, option) { - var self = this - var map = this._map - var title = option.title || option.name - var position = new maps.LatLng(option.latitude, option.longitude) - var img = new Image() - img.onload = () => { - var anchor = option.anchor || {} - var icon - var w - var h - var top - var x = anchor.x - var y = anchor.y - if (option.iconPath && (option.width || option.height)) { - w = option.width || img.width / img.height * option.height - h = option.height || img.height / img.width * option.width - } else { - w = img.width / 2 - h = img.height / 2 - } - x = (typeof x === 'number' ? x : 0.5) * w - y = (typeof y === 'number' ? y : 1) * h - top = h - (h - y) - icon = new maps.MarkerImage(img.src, null, null, new maps.Point(x, y), new maps.Size(w, h)) - marker.setPosition(position) - marker.setIcon(icon) - marker.setRotation(option.rotate || 0) - var labelOpt = option.label || {} - if (marker.label) { - marker.label.setMap(null) - delete (marker.label) - } - var label - if (labelOpt.content) { - label = new maps.Label({ - position, - map, - clickable: false, - content: labelOpt.content, - style: { - border: 'none', - padding: '8px', - background: 'none', - color: labelOpt.color, - fontSize: (labelOpt.fontSize || 14) + 'px', - lineHeight: (labelOpt.fontSize || 14) + 'px', - marginLeft: labelOpt.x, - marginTop: labelOpt.y - } - }) - marker.label = label - } - var calloutOpt = option.callout || {} - var callout = marker.callout - var calloutStyle - if (calloutOpt.content) { - calloutStyle = { - id: option.id, - position, - map, - top, - content: calloutOpt.content, - color: calloutOpt.color, - fontSize: calloutOpt.fontSize, - borderRadius: calloutOpt.borderRadius, - bgColor: calloutOpt.bgColor, - padding: calloutOpt.padding, - boxShadow: calloutOpt.boxShadow, - display: calloutOpt.display - } - } else if (title) { - calloutStyle = { - id: option.id, - position, - map, - top, - content: title, - boxShadow: '0px 0px 3px 1px rgba(0,0,0,0.5)' - } - } - if (calloutStyle) { - if (callout) { - callout.setOption(calloutStyle) - } else { - callout = marker.callout = new maps.Callout(calloutStyle) - - callout.div.onclick = function ($event) { - hasOwn(option, 'id') && self.$trigger('callouttap', $event, { - markerId: option.id - }) - $event.stopPropagation() - $event.preventDefault() - } - } - } else { - if (callout) { - callout.setMap(null) - delete (marker.callout) - } - } - } - img.src = option.iconPath ? this.$getRealPath(option.iconPath) - : 'data:image/png;base64,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' - }, - removeMarkers (markers) { - for (var index = 0; index < markers.length; index++) { - var marker = markers[index] - if (marker.label) { - marker.label.setMap(null) - } - if (marker.callout) { - marker.callout.setMap(null) - } - marker.setMap(null) - markers.splice(index--, 1) - } - }, createPolyline () { + const maps = this._maps var map = this._map var polyline = this.polylineSync this.removePolyline() @@ -733,6 +476,7 @@ export default { polyline.splice(0, polyline.length) }, createCircles () { + const maps = this._maps var map = this._map var circles = this.circlesSync this.removeCircles() @@ -740,12 +484,15 @@ export default { var center = new maps.LatLng(option.latitude, option.longitude) function getColor (color) { - var c = color.match(/#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/) - if (c && c.length) { - return maps.Color.fromHex(c[0], Number('0x' + c[1] || 255) / 255) - } else { - return undefined + var c = color && color.match(/#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/) + if ('Color' in maps) { + if (c && c.length) { + return maps.Color.fromHex(c[0], Number('0x' + c[1] || 255) / 255) + } else { + return undefined + } } + return color } var circle = new maps.Circle({ map, @@ -768,6 +515,7 @@ export default { circles.splice(0, circles.length) }, createControls () { + const maps = this._maps var _self = this var map = this._map var controls = this.controlsSync @@ -814,6 +562,7 @@ export default { controls.splice(0, controls.length) }, createLocation () { + const maps = this._maps var map = this._map var location = this._location if (location) { @@ -879,6 +628,7 @@ export default { } }, fitBounds (points, cb) { + const maps = this._maps this.boundsReady(() => { var map = this._map var bounds = new maps.LatLngBounds() @@ -914,14 +664,11 @@ export default { } }, getMarker (id) { - var markers = this.markersSync - for (var index = 0; index < markers.length; index++) { - var element = markers[index] - if (element.id === id) { - return element - } + var marker = this._markers[id] + if (!marker) { + throw new Error('translateMarker: fail cannot find marker with id ' + id) } - throw new Error('translateMarker: fail cannot find marker with id ' + id) + return marker } } } diff --git a/src/platforms/h5/view/components/map/map-marker.js b/src/platforms/h5/view/components/map/map-marker.js new file mode 100644 index 0000000000000000000000000000000000000000..3d47d00f48a521ab6edd618c1d883c38f2e10acc --- /dev/null +++ b/src/platforms/h5/view/components/map/map-marker.js @@ -0,0 +1,254 @@ +import getRealPath from 'uni-platform/helpers/get-real-path' + +export default { + props: { + id: { + type: [Number, String], + default: '' + }, + latitude: { + type: [Number, String], + require: true + }, + longitude: { + type: [Number, String], + require: true + }, + title: { + type: String, + default: '' + }, + iconPath: { + type: String, + require: true + }, + rotate: { + type: [Number, String], + default: 0 + }, + alpha: { + type: [Number, String], + default: 1 + }, + width: { + type: [Number, String], + default: '' + }, + height: { + type: [Number, String], + default: '' + }, + callout: { + type: Object, + default: null + }, + label: { + type: Object, + default: null + }, + anchor: { + type: Object, + default: null + }, + clusterId: { + type: [Number, String], + default: '' + }, + customCallout: { + type: Object, + default: null + }, + ariaLabel: { + type: String, + default: '' + } + }, + mounted () { + const $parent = this.$parent + $parent.mapReady(() => { + this._maps = $parent._maps + this._map = $parent._map + this.addMarker(this.$props) + Object.keys(this.$props).forEach(key => { + this.$watch(key, () => { + this.updateMarker(this.$props) + }) + }) + }) + }, + beforeDestroy () { + this.removeMarker() + }, + methods: { + addMarker (props) { + const maps = this._maps + const map = this._map + const marker = this._marker = new maps.Marker({ + map, + flat: true, + autoRotation: false + }) + this.$parent._markers[this.id] = marker + this.updateMarker(props) + maps.event.addListener(marker, 'click', () => { + const callout = marker.callout + if (callout) { + const div = callout.div + const parent = div.parentNode + if (!callout.alwaysVisible) { + callout.set('visible', !callout.visible) + } + if (callout.visible) { + parent.removeChild(div) + parent.appendChild(div) + } + } + if (this.id) { + this.$parent.$trigger('markertap', {}, { + markerId: this.id + }) + } + }) + }, + updateMarker (option) { + const map = this._map + const maps = this._maps + const marker = this._marker + const title = option.title + const position = new maps.LatLng(option.latitude, option.longitude) + const img = new Image() + img.onload = () => { + const anchor = option.anchor || {} + let icon + let w + let h + const x = typeof anchor.x === 'number' ? anchor.x : 0.5 + const y = typeof anchor.y === 'number' ? anchor.y : 1 + if (option.iconPath && (option.width || option.height)) { + w = option.width || (img.width / img.height) * option.height + h = option.height || (img.height / img.width) * option.width + } else { + w = img.width / 2 + h = img.height / 2 + } + const top = h - (h - y) + if ('MarkerImage' in maps) { + icon = new maps.MarkerImage( + img.src, + null, + null, + new maps.Point(x * w, y * h), + new maps.Size(w, h) + ) + } else { + icon = { + url: img.src, + anchor: new maps.Point(x, y), + size: new maps.Size(w, h) + } + } + marker.setPosition(position) + marker.setIcon(icon) + if ('setRotation' in marker) { + marker.setRotation(option.rotate || 0) + } + const labelOpt = option.label || {} + if ('label' in marker) { + marker.label.setMap(null) + delete marker.label + } + let label + if (labelOpt.content) { + if ('Label' in maps) { + label = new maps.Label({ + position: position, + map: map, + clickable: false, + content: labelOpt.content, + style: { + border: 'none', + padding: '8px', + background: 'none', + color: labelOpt.color, + fontSize: (labelOpt.fontSize || 14) + 'px', + lineHeight: (labelOpt.fontSize || 14) + 'px', + marginLeft: labelOpt.x, + marginTop: labelOpt.y + } + }) + marker.label = label + } else if ('setLabel' in marker) { + marker.setLabel({ + text: labelOpt.content, + color: labelOpt.color, + fontSize: (labelOpt.fontSize || 14) + 'px' + }) + } + } + const calloutOpt = option.callout || {} + let callout = marker.callout + let calloutStyle + if (calloutOpt.content || title) { + calloutStyle = calloutOpt.content + ? { + position, + map, + top, + content: calloutOpt.content, + color: calloutOpt.color, + fontSize: calloutOpt.fontSize, + borderRadius: calloutOpt.borderRadius, + bgColor: calloutOpt.bgColor, + padding: calloutOpt.padding, + boxShadow: calloutOpt.boxShadow, + display: calloutOpt.display + } + : { + position, + map, + top, + content: title, + boxShadow: '0px 0px 3px 1px rgba(0,0,0,0.5)' + } + if (callout) { + callout.setOption(calloutStyle) + } else { + callout = marker.callout = new maps.Callout(calloutStyle) + callout.div.onclick = function ($event) { + if (this.id !== '') { + this.$parent.$trigger('callouttap', $event, { + markerId: this.id + }) + } + $event.stopPropagation() + $event.preventDefault() + } + } + } else { + if (callout) { + callout.setMap(null) + delete marker.callout + } + } + } + img.src = getRealPath(option.iconPath) + }, + removeMarker () { + const marker = this._marker + if (marker) { + if (marker.label) { + marker.label.setMap(null) + } + if (marker.callout) { + marker.callout.setMap(null) + } + marker.setMap(null) + } + delete this.$parent._markers[this.id] + this._marker = null + } + }, + render () { + return null + } +} diff --git a/src/platforms/h5/view/components/map/maps/callout.js b/src/platforms/h5/view/components/map/maps/callout.js new file mode 100644 index 0000000000000000000000000000000000000000..3e9f81327b62cc89aa63a1d4a071356635d6d581 --- /dev/null +++ b/src/platforms/h5/view/components/map/maps/callout.js @@ -0,0 +1,112 @@ +export function createCallout (maps) { + const overlay = new (maps.OverlayView || maps.Overlay)() + function onAdd () { + const div = this.div + const panes = this.getPanes() + panes.floatPane.appendChild(div) + } + function onRemove () { + const parentNode = this.div.parentNode + if (parentNode) { + parentNode.removeChild(this.div) + } + } + + class Callout { + option + position + index + visible + alwaysVisible + div + triangle + + set onclick (callback) { + this.div.onclick = callback + } + + get onclick () { + return this.div.onclick + } + + constructor (option = {}) { + this.option = option || {} + const map = option.map + this.position = option.position + this.index = 1 + const visible = (this.visible = this.alwaysVisible = option.display === 'ALWAYS') + const div = (this.div = document.createElement('div')) + const divStyle = div.style + divStyle.position = 'absolute' + divStyle.whiteSpace = 'nowrap' + divStyle.transform = 'translateX(-50%) translateY(-100%)' + divStyle.zIndex = '1' + divStyle.boxShadow = option.boxShadow || 'none' + divStyle.display = visible ? 'block' : 'none' + const triangle = (this.triangle = document.createElement('div')) + triangle.setAttribute( + 'style', + 'position: absolute;white-space: nowrap;border-width: 4px;border-style: solid;border-color: #fff transparent transparent;border-image: initial;font-size: 12px;padding: 0px;background-color: transparent;width: 0px;height: 0px;transform: translate(-50%, 100%);left: 50%;bottom: 0;' + ) + this.setStyle(option) + div.appendChild(triangle) + if (map) { + this.setMap(map) + } + } + + onAdd = onAdd + construct = onAdd + setOption (option) { + this.option = option + this.setPosition(option.position) + if (option.display === 'ALWAYS') { + this.alwaysVisible = this.visible = true + } else { + this.alwaysVisible = false + } + this.setStyle(option) + } + + setStyle (option) { + const div = this.div + const divStyle = div.style + div.innerText = option.content || '' + divStyle.lineHeight = (option.fontSize || 14) + 'px' + divStyle.fontSize = (option.fontSize || 14) + 'px' + divStyle.padding = (option.padding || 8) + 'px' + divStyle.color = option.color || '#000' + divStyle.borderRadius = (option.borderRadius || 0) + 'px' + divStyle.backgroundColor = option.bgColor || '#fff' + divStyle.marginTop = '-' + ((option.top || 0) + 5) + 'px' + this.triangle.style.borderColor = `${option.bgColor || '#fff'} transparent transparent` + } + + setPosition (position) { + this.position = position + this.draw() + } + + draw () { + const overlayProjection = this.getProjection() + if (!this.position || !this.div || !overlayProjection) { + return + } + const pixel = overlayProjection.fromLatLngToDivPixel(this.position) + const divStyle = this.div.style + divStyle.left = pixel.x + 'px' + divStyle.top = pixel.y + 'px' + } + + changed () { + const divStyle = this.div.style + divStyle.display = this.visible ? 'block' : 'none' + } + + onRemove = onRemove + + destroy = onRemove + } + Callout.prototype = overlay + return Callout +} diff --git a/src/platforms/h5/view/components/map/maps/index.js b/src/platforms/h5/view/components/map/maps/index.js new file mode 100644 index 0000000000000000000000000000000000000000..b284ee4c4c5805d54e020b0ebc330c1dd7c7c144 --- /dev/null +++ b/src/platforms/h5/view/components/map/maps/index.js @@ -0,0 +1,55 @@ +import { + MapType, + getMapInfo +} from '../../../../helpers/location' +import { createCallout } from './callout' + +let maps +const callbacksMap = {} +const GOOGLE_MAP_CALLBACKNAME = '__map_callback__' + +export function loadMaps (libraries, callback) { + const mapInfo = getMapInfo() + if (!mapInfo.key) { + console.error('Map key not configured.') + return + } + const callbacks = (callbacksMap[mapInfo.type] = callbacksMap[mapInfo.type] || []) + if (maps) { + callback(maps) + } else if ( + window[mapInfo.type] && + window[mapInfo.type].maps + ) { + maps = window[mapInfo.type].maps + maps.Callout = maps.Callout || createCallout(maps) + callback(maps) + } else if (callbacks.length) { + callbacks.push(callback) + } else { + callbacks.push(callback) + const globalExt = window + const callbackName = GOOGLE_MAP_CALLBACKNAME + mapInfo.type + globalExt[callbackName] = function () { + delete globalExt[callbackName] + maps = window[mapInfo.type].maps + maps.Callout = createCallout(maps) + callbacks.forEach((callback) => callback(maps)) + callbacks.length = 0 + } + const script = document.createElement('script') + let src = + mapInfo.type === MapType.GOOGLE ? 'https://maps.googleapis.com/maps/api/js?' : 'https://map.qq.com/api/js?v=2.exp&' + if (mapInfo.type === MapType.QQ) { + libraries.push('geometry') + } + if (libraries.length) { + src += `libraries=${libraries.join('%2C')}&` + } + script.src = `${src}key=${mapInfo.key}&callback=${callbackName}` + script.onerror = function () { + console.error('Map load failed.') + } + document.body.appendChild(script) + } +}