提交 23648f85 编写于 作者: DCloud-WZF's avatar DCloud-WZF 💬

feat(h5 map): map 组件支持高德地图

上级 42fe6a65
......@@ -2,6 +2,9 @@ import { inject, onUnmounted, watch } from 'vue'
import { defineSystemComponent, useCustomEvent } from '@dcloudio/uni-components'
import { Maps, Map, Circle, CircleOptions } from './maps'
import { hexToRgba } from '../../../helpers/hexToRgba'
import { getIsAMap } from '../../../helpers/location'
import { QQMaps } from './maps/qq/types'
import { GoogleMaps } from './maps/google/types'
const props = {
latitude: { type: [Number, String], require: true },
......@@ -39,7 +42,12 @@ export default /*#__PURE__*/ defineSystemComponent({
addCircle(option)
}
function addCircle(option: Props) {
const center = new maps.LatLng(option.latitude, option.longitude)
const center = getIsAMap()
? [option.longitude, option.latitude]
: new (maps as QQMaps | GoogleMaps).LatLng(
option.latitude,
option.longitude
)
const circleOptions: CircleOptions = {
map: map as any,
center: center as any,
......@@ -48,18 +56,27 @@ export default /*#__PURE__*/ defineSystemComponent({
strokeWeight: Number(option.strokeWidth) || 1,
strokeDashStyle: 'solid',
}
const { r: fr, g: fg, b: fb, a: fa } = hexToRgba(option.fillColor)
const { r: sr, g: sg, b: sb, a: sa } = hexToRgba(option.color)
if ('Color' in maps) {
circleOptions.fillColor = new maps.Color(fr, fg, fb, fa) as any
circleOptions.strokeColor = new maps.Color(sr, sg, sb, sa) as any
if (getIsAMap()) {
circleOptions.strokeColor = option.color
circleOptions.fillColor = option.fillColor || '#000'
circleOptions.fillOpacity = 1
} else {
circleOptions.fillColor = `rgb(${fr}, ${fg}, ${fb})`
circleOptions.fillOpacity = fa
circleOptions.strokeColor = `rgb(${sr}, ${sg}, ${sb})`
circleOptions.strokeOpacity = sa
const { r: fr, g: fg, b: fb, a: fa } = hexToRgba(option.fillColor)
const { r: sr, g: sg, b: sb, a: sa } = hexToRgba(option.color)
if ('Color' in maps) {
circleOptions.fillColor = new maps.Color(fr, fg, fb, fa) as any
circleOptions.strokeColor = new maps.Color(sr, sg, sb, sa) as any
} else {
circleOptions.fillColor = `rgb(${fr}, ${fg}, ${fb})`
circleOptions.fillOpacity = fa
circleOptions.strokeColor = `rgb(${sr}, ${sg}, ${sb})`
circleOptions.strokeOpacity = sa
}
}
circle = new maps.Circle(circleOptions)
if (getIsAMap()) {
;(map as AMap.Map).add(circle as any)
}
}
addCircle(props as Props)
watch(props, updateCircle)
......
import { inject, onUnmounted, watch, PropType } from 'vue'
import { getRealPath } from '@dcloudio/uni-platform'
import { defineSystemComponent, useCustomEvent } from '@dcloudio/uni-components'
import { Maps, Map } from './maps'
import { Maps, Map, QQMap, GoogleMap } from './maps'
import { getIsAMap } from '../../../helpers/location'
import { QQMaps } from './maps/qq/types'
import { GoogleMaps } from './maps/google/types'
interface Position {
left: number | string
......@@ -15,6 +18,7 @@ const props = {
position: { type: Object as PropType<Position>, require: true },
iconPath: { type: String, require: true },
clickable: { type: [Boolean, String], default: '' },
rootRef: { type: Object, default: null },
}
export type Props = Partial<Record<keyof typeof props, any>>
......@@ -51,6 +55,8 @@ export default /*#__PURE__*/ defineSystemComponent({
style.position = 'absolute'
style.width = '0'
style.height = '0'
style.top = '0'
style.left = '0'
img.onload = () => {
if (option.position.width) {
img.width = option.position.width
......@@ -72,7 +78,13 @@ export default /*#__PURE__*/ defineSystemComponent({
})
}
}
map.controls[maps.ControlPosition.TOP_LEFT].push(control)
if (getIsAMap()) {
props.rootRef.value && props.rootRef.value.appendChild(control)
} else {
;(map as QQMap | GoogleMap).controls[
(maps as QQMaps | GoogleMaps).ControlPosition.TOP_LEFT
].push(control)
}
}
addControl(props as Props)
watch(props, updateControl)
......
......@@ -3,12 +3,13 @@ import { isFunction } from '@vue/shared'
import { getRealPath } from '@dcloudio/uni-platform'
import { defineSystemComponent, useCustomEvent } from '@dcloudio/uni-components'
import { Maps, Map, LatLng, Callout, CalloutOptions } from './maps'
import { getIsAMap } from '../../../helpers/location'
import {
Map as GMap,
LatLng as GLatLng,
Marker as GMarker,
Label as GLabel,
Icon,
GoogleMaps,
} from './maps/google/types'
import {
Map as QMap,
......@@ -16,6 +17,7 @@ import {
Marker as QMarker,
Label as QLabel,
MarkerImage,
QQMaps,
} from './maps/qq/types'
const props = {
......@@ -114,7 +116,8 @@ interface MarkerExt {
}
interface GMarkerExt extends GMarker, MarkerExt {}
interface QMarkerExt extends QMarker, MarkerExt {}
type Marker = GMarkerExt | QMarkerExt
interface AMarkerExt extends AMap.Marker, MarkerExt {}
type Marker = GMarkerExt | QMarkerExt | AMarkerExt
type MarkerLabelStyle = Partial<
Pick<
CSSStyleDeclaration,
......@@ -172,15 +175,30 @@ export default /*#__PURE__*/ defineSystemComponent({
;(marker.label as QLabel).setMap(null)
}
if (marker.callout) {
marker.callout.setMap(null)
removeMarkerCallout(marker.callout)
}
marker.setMap(null)
}
}
function removeMarkerCallout(callout: Marker['callout']) {
if (getIsAMap()) {
callout!.removeAMapText()
} else {
callout!.setMap(null)
}
}
onMapReady((map, maps, trigger) => {
function updateMarker(option: Props) {
const title = option.title
const position = new maps.LatLng(option.latitude, option.longitude)
const position = getIsAMap()
? new (maps as AMap.NameSpace).LngLat(
option.longitude,
option.latitude
)
: new (maps as QQMaps | GoogleMaps).LatLng(
option.latitude,
option.longitude
)
const img = new Image()
img.onload = () => {
const anchor = option.anchor || {}
......@@ -203,14 +221,22 @@ export default /*#__PURE__*/ defineSystemComponent({
img.src,
null,
null,
new maps.Point(x * w, y * h),
new maps.Size(w, h)
new (maps as QQMaps).Point(x * w, y * h),
new (maps as QQMaps).Size(w, h)
)
} else if ('Icon' in maps) {
// 高德
icon = new (maps as AMap.NameSpace).Icon({
image: img.src,
size: new (maps as AMap.NameSpace).Size(w, h),
imageSize: new (maps as AMap.NameSpace).Size(w, h),
imageOffset: new (maps as AMap.NameSpace).Pixel(x * w, y * h),
})
} else {
icon = {
url: img.src,
anchor: new maps.Point(x, y),
size: new maps.Size(w, h),
anchor: new (maps as GoogleMaps).Point(x, y),
size: new (maps as GoogleMaps).Size(w, h),
}
}
marker.setPosition(position as any)
......@@ -247,13 +273,33 @@ export default /*#__PURE__*/ defineSystemComponent({
})
marker.label = label
} else if ('setLabel' in marker) {
const className = updateMarkerLabelStyle(labelStyle)
marker.setLabel({
text: labelOpt.content,
color: labelStyle.color,
fontSize: labelStyle.fontSize,
className,
})
if (getIsAMap()) {
const content = `<div style="
margin-left:${labelStyle.marginLeft};
margin-top:${labelStyle.marginTop};
padding:${labelStyle.padding};
background-color:${labelStyle.backgroundColor};
border-radius:${labelStyle.borderRadius};
line-height:${labelStyle.lineHeight};
color:${labelStyle.color};
font-size:${labelStyle.fontSize};
">
${labelOpt.content}
<div>`
marker.setLabel({
content,
direction: 'bottom-right',
} as any)
} else {
const className = updateMarkerLabelStyle(labelStyle)
;(marker as GMarker).setLabel({
text: labelOpt.content,
color: labelStyle.color,
fontSize: labelStyle.fontSize,
className,
})
}
}
}
const calloutOpt = option.callout || {}
......@@ -266,6 +312,8 @@ export default /*#__PURE__*/ defineSystemComponent({
position,
map,
top,
// handle AMap callout offset
offsetY: -option.height / 2,
content: calloutOpt.content,
color: calloutOpt.color,
fontSize: calloutOpt.fontSize,
......@@ -279,26 +327,42 @@ export default /*#__PURE__*/ defineSystemComponent({
position,
map,
top,
// handle AMap callout offset
offsetY: -option.height / 2,
content: title,
boxShadow,
}
if (callout) {
callout.setOption(calloutStyle)
} else {
callout = marker.callout = new maps.Callout(calloutStyle)
callout.div.onclick = function ($event) {
if (id !== '') {
trigger('callouttap', $event, {
markerId: Number(id),
})
if (getIsAMap()) {
const callback = (id: number | string) => {
if (id !== '') {
trigger('callouttap', {} as Event, {
markerId: Number(id),
})
}
}
callout = marker.callout = new maps.Callout(
calloutStyle,
callback
)
} else {
callout = marker.callout = new maps.Callout(calloutStyle)
callout.div!.onclick = function ($event: Event) {
if (id !== '') {
trigger('callouttap', $event, {
markerId: Number(id),
})
}
$event.stopPropagation()
$event.preventDefault()
}
$event.stopPropagation()
$event.preventDefault()
}
}
} else {
if (callout) {
callout.setMap(null)
removeMarkerCallout(callout)
delete marker.callout
}
}
......@@ -311,24 +375,34 @@ export default /*#__PURE__*/ defineSystemComponent({
}
function addMarker(props: Props) {
marker = new maps.Marker({
map: map as GMap & QMap,
map: map as any,
flat: true,
autoRotation: false,
})
updateMarker(props)
maps.event.addListener(marker, 'click', () => {
const MapsEvent =
(maps as QQMaps | GoogleMaps).event || (maps as AMap.NameSpace).Event
MapsEvent.addListener(marker, 'click', () => {
const callout = marker.callout
if (callout) {
const div = callout.div
const parent = div.parentNode as HTMLElement
if (!callout.alwaysVisible) {
if (callout && !callout.alwaysVisible) {
if (getIsAMap()) {
callout.visible = !callout.visible
if (callout.visible) {
marker.callout!.createAMapText()
} else {
marker.callout!.removeAMapText()
}
} else {
callout.set('visible', !callout.visible)
}
if (callout.visible) {
parent.removeChild(div)
parent.appendChild(div)
if (callout.visible) {
const div = callout.div!
const parent = div.parentNode!
parent.removeChild(div)
parent.appendChild(div)
}
}
}
if (id) {
trigger('markertap', {} as Event, {
markerId: Number(id),
......@@ -361,20 +435,23 @@ export default /*#__PURE__*/ defineSystemComponent({
rotation = marker.getRotation()
}
const a = marker.getPosition()
const b = new maps.LatLng(
const b = new (maps as QQMaps | GoogleMaps).LatLng(
destination.latitude,
destination.longitude
)
const distance =
maps.geometry.spherical.computeDistanceBetween(
a as any,
b as any
) / 1000
(
maps as QQMaps | GoogleMaps
).geometry.spherical.computeDistanceBetween(a as any, b as any) /
1000
const time =
(typeof duration === 'number' ? duration : 1000) /
(1000 * 60 * 60)
const speed = distance / time
const movingEvent = maps.event.addListener(
const MapsEvent =
(maps as QQMaps | GoogleMaps).event ||
(maps as AMap.NameSpace).Event
const movingEvent = MapsEvent.addListener(
marker,
'moving',
(e: any) => {
......@@ -389,10 +466,10 @@ export default /*#__PURE__*/ defineSystemComponent({
}
}
)
const event = maps.event.addListener(marker, 'moveend', () => {
const event = MapsEvent.addListener(marker, 'moveend', () => {
event.remove()
movingEvent.remove()
marker.lastPosition = a!
marker.lastPosition = a as QLatLng
marker.setPosition(b as any)
const label = marker.label
if (label) {
......@@ -410,14 +487,18 @@ export default /*#__PURE__*/ defineSystemComponent({
let lastRtate = 0
if (autoRotate) {
if (marker.lastPosition) {
lastRtate = maps.geometry.spherical.computeHeading(
lastRtate = (
maps as QQMaps | GoogleMaps
).geometry.spherical.computeHeading(
marker.lastPosition as any,
a as any
)
}
rotate =
maps.geometry.spherical.computeHeading(a as any, b as any) -
lastRtate
(maps as QQMaps | GoogleMaps).geometry.spherical.computeHeading(
a as any,
b as any
) - lastRtate
}
if ('setRotation' in marker) {
marker.setRotation(rotation + rotate)
......@@ -426,7 +507,7 @@ export default /*#__PURE__*/ defineSystemComponent({
marker.moveTo(b as QLatLng, speed)
} else {
marker.setPosition(b as GLatLng)
maps.event.trigger(marker, 'moveend', {})
MapsEvent.trigger(marker, 'moveend', {})
}
})
},
......
......@@ -2,6 +2,7 @@ import { inject, PropType, onUnmounted, watch } from 'vue'
import { defineSystemComponent, useCustomEvent } from '@dcloudio/uni-components'
import { Maps, Map, LatLng, Polyline, PolylineOptions } from './maps'
import { hexToRgba } from '../../../helpers/hexToRgba'
import { getIsAMap } from '../../../helpers/location'
interface Point {
latitude: number
......@@ -56,9 +57,15 @@ export default /*#__PURE__*/ defineSystemComponent({
addPolyline(option)
}
function addPolyline(option: Props) {
const path: LatLng[] = []
const path: LatLng | any[] = []
option.points.forEach((point: Point) => {
path.push(new maps.LatLng(point.latitude, point.longitude))
const pointPosition = getIsAMap()
? [point.longitude, point.latitude]
: new (maps as typeof google.maps).LatLng(
point.latitude,
point.longitude
)
path.push(pointPosition)
})
const strokeWeight = Number(option.width) || 1
const { r: sr, g: sg, b: sb, a: sa } = hexToRgba(option.color)
......
......@@ -15,9 +15,12 @@ import {
useSubscribe,
useCustomEvent,
} from '@dcloudio/uni-components'
import '@amap/amap-jsapi-types'
import { callOptions } from '@dcloudio/uni-shared'
import { Point } from '../../../helpers/location'
import { Maps, Map, loadMaps, LatLng } from './maps'
import { Maps, Map, loadMaps, LatLng, QQMap, GoogleMap } from './maps'
import { QQMaps } from './maps/qq/types'
import { GoogleMaps } from './maps/google/types'
import MapMarker, {
Props as MapMarkerProps,
Context as MapMarkerContext,
......@@ -31,6 +34,7 @@ import MapLocation, {
} from './MapLocation'
import MapPolygon from './map-polygon/index'
import { Polygon } from './map-polygon/interface'
import { getIsAMap } from '../../../helpers/location'
const props = {
id: {
......@@ -117,6 +121,26 @@ function getPoints(points: Point[]): Point[] {
return newPoints
}
function getAMapPosition(
maps: AMap.NameSpace,
latitude: number,
longitude: number
) {
return new maps.LngLat(longitude, latitude)
}
function getGoogleOrQQMapPosition(
maps: QQMaps | GoogleMaps,
latitude: number,
longitude: number
) {
return new maps.LatLng(latitude, longitude)
}
function getMapPosition(maps: Maps, latitude: number, longitude: number) {
return getIsAMap()
? getAMapPosition(maps as AMap.NameSpace, latitude, longitude)
: getGoogleOrQQMapPosition(maps as QQMaps | GoogleMaps, latitude, longitude)
}
function getLat(latLng: LatLng) {
if ('getLat' in latLng) {
return latLng.getLat()
......@@ -195,7 +219,12 @@ function useMap(
state.latitude = latitude
state.longitude = longitude
if (map) {
map.setCenter(new maps.LatLng(latitude, longitude) as any)
const centerPosition = getMapPosition(
maps,
state.latitude,
state.longitude
)
map.setCenter(centerPosition as any)
}
}
}
......@@ -222,25 +251,40 @@ function useMap(
return {
scale: map.getZoom(),
centerLocation: {
latitude: getLat(center),
longitude: getLng(center),
latitude: getLat(center as LatLng),
longitude: getLng(center as LatLng),
},
}
}
function updateCenter() {
map.setCenter(new maps.LatLng(state.latitude, state.longitude) as any)
const centerPosition = getMapPosition(maps, state.latitude, state.longitude)
map.setCenter(centerPosition as any)
}
function updateBounds() {
const bounds = new maps.LatLngBounds()
state.includePoints.forEach(({ latitude, longitude }) => {
const latLng = new maps.LatLng(latitude, longitude)
bounds.extend(latLng as any)
})
map.fitBounds(bounds as any)
if (getIsAMap()) {
const points: [number, number][] = []
state.includePoints.forEach((point) => {
points.push([point.longitude, point.latitude])
})
const bounds = new (maps as AMap.NameSpace).Bounds(...points)
;(map as AMap.Map).setBounds(bounds)
} else {
const bounds = new (maps as QQMaps | GoogleMaps).LatLngBounds()
state.includePoints.forEach(({ latitude, longitude }) => {
const latLng = new (maps as QQMaps | GoogleMaps).LatLng(
latitude,
longitude
)
bounds.extend(latLng as any)
})
;(map as QQMap | GoogleMap).fitBounds(bounds as any)
}
}
function initMap() {
const mapEl = mapRef.value as HTMLDivElement
const center = new maps.LatLng(state.latitude, state.longitude)
const center = getMapPosition(maps, state.latitude, state.longitude)
const event =
(maps as QQMaps | GoogleMaps).event || (maps as AMap.NameSpace).Event
const map = new maps.Map(mapEl, {
center: center as any,
zoom: Number(props.scale),
......@@ -271,26 +315,22 @@ function useMap(
}
})
// 需在 bounds_changed 后触发 BoundsReady
const boundsChangedEvent = maps.event.addListener(
map,
'bounds_changed',
() => {
boundsChangedEvent.remove()
emitBoundsReady()
}
)
maps.event.addListener(map, 'click', () => {
const boundsChangedEvent = event.addListener(map, 'bounds_changed', () => {
boundsChangedEvent.remove()
emitBoundsReady()
})
event.addListener(map, 'click', () => {
// TODO 编译器将 tap 转换为 click
trigger('tap', {} as Event, {})
trigger('click', {} as Event, {})
})
maps.event.addListener(map, 'dragstart', () => {
event.addListener(map, 'dragstart', () => {
trigger('regionchange', {} as Event, {
type: 'begin',
causedBy: 'gesture',
})
})
maps.event.addListener(map, 'dragend', () => {
event.addListener(map, 'dragend', () => {
trigger(
'regionchange',
{} as Event,
......@@ -303,7 +343,7 @@ function useMap(
)
)
})
maps.event.addListener(map, 'zoom_changed', () => {
event.addListener(map, 'zoom_changed', () => {
emit('update:scale', map.getZoom())
trigger(
'regionchange',
......@@ -317,15 +357,16 @@ function useMap(
)
)
})
maps.event.addListener(map, 'center_changed', () => {
event.addListener(map, 'center_changed', () => {
const center = map.getCenter()!
const latitude = getLat(center)
const longitude = getLng(center)
const latitude = getLat(center as LatLng)
const longitude = getLng(center as LatLng)
emit('update:latitude', latitude)
emit('update:longitude', longitude)
})
return map
}
try {
// TODO 支持在页面外使用
const id = useContextInfo()
......@@ -336,8 +377,8 @@ function useMap(
onMapReady(() => {
const center = map.getCenter()!
callOptions(data, {
latitude: getLat(center),
longitude: getLng(center),
latitude: getLat(center as LatLng),
longitude: getLng(center as LatLng),
errMsg: `${type}:ok`,
})
})
......@@ -359,7 +400,12 @@ function useMap(
state.latitude = latitude
state.longitude = longitude
if (map) {
map.setCenter(new maps.LatLng(latitude, longitude) as any)
const centerPosition = getMapPosition(
maps,
latitude,
longitude
)
map.setCenter(centerPosition as any)
}
onMapReady(() => {
callOptions(data, `${type}:ok`)
......@@ -402,12 +448,12 @@ function useMap(
const northeast = latLngBounds.getNorthEast()
callOptions(data, {
southwest: {
latitude: getLat(southwest),
longitude: getLng(southwest),
latitude: getLat(southwest as LatLng),
longitude: getLng(southwest as LatLng),
},
northeast: {
latitude: getLat(northeast),
longitude: getLng(northeast),
latitude: getLat(northeast as LatLng),
longitude: getLng(northeast as LatLng),
},
errMsg: `${type}:ok`,
})
......@@ -430,7 +476,7 @@ function useMap(
onMounted(() => {
loadMaps(props.libraries, (result) => {
maps = result
map = initMap()
map = initMap() as Map
emitMapReady()
trigger('updated', {} as Event, {})
})
......@@ -480,7 +526,7 @@ export default /*#__PURE__*/ defineBuiltInComponent({
<MapCircle {...item} />
))}
{props.controls.map((item) => (
<MapControl {...item} />
<MapControl {...item} rootRef={rootRef} />
))}
{props.showLocation && <MapLocation />}
{props.polygons.map((item) => (
......
......@@ -9,9 +9,11 @@ import {
CustomEventTrigger,
PolygonOptions,
} from './interface'
import { Map, Maps } from '../maps'
import { QQMaps } from '../maps/qq/types'
import { GoogleMap, QQMap } from '../maps'
import { hexToRgba } from '../../../../helpers/hexToRgba'
import { getIsAMap } from '../../../../helpers/location'
import { QQMaps } from '../maps/qq/types'
import { GoogleMaps } from '../maps/google/types'
export default /*#__PURE__*/ defineSystemComponent({
name: 'MapPolygon',
......@@ -22,103 +24,111 @@ export default /*#__PURE__*/ defineSystemComponent({
// 当地图准备好以后调用指定的回调函数
const onMapReady = inject<OnMapReady>('onMapReady') as OnMapReady
onMapReady((map: Map, maps: Maps, trigger: CustomEventTrigger) => {
// 绘制区域
function drawPolygon() {
const {
points,
strokeWidth,
strokeColor,
dashArray,
fillColor,
zIndex,
} = props
const path = points.map((item: Point) => {
const { latitude, longitude } = item
return new maps.LatLng(latitude, longitude)
})
const { r: fcR, g: fcG, b: fcB, a: fcA } = hexToRgba(fillColor)
const { r: scR, g: scG, b: scB, a: scA } = hexToRgba(strokeColor)
const polygonOptions: PolygonOptions = {
//多边形是否可点击。
clickable: true,
//鼠标在多边形内的光标样式。
cursor: 'crosshair',
//多边形是否可编辑。
editable: false,
// 地图实例,即要显示多边形的地图
// @ts-ignore
map,
// 区域填充色
fillColor: '',
//多边形的路径,以经纬度坐标数组构成。
path,
// 区域边框
strokeColor: '',
//多边形的边框样式。实线是solid,虚线是dash。
strokeDashStyle: dashArray.some((item: number) => item > 0)
? 'dash'
: 'solid',
//多边形的边框线宽。
strokeWeight: strokeWidth,
//多边形是否可见。
visible: true,
//多边形的zIndex值。
zIndex: zIndex,
}
// 多边形的填充色、边框以及相应的透明度
if ((maps as QQMaps).Color) {
// 说明是 腾讯地图,google map 实例没有 Color 属性
// 将类型转为两者共有的 string,避免 ts 报错
polygonOptions.fillColor = new (maps as QQMaps).Color(
fcR,
fcG,
fcB,
fcA
) as unknown as string
polygonOptions.strokeColor = new (maps as QQMaps).Color(
scR,
scG,
scB,
scA
) as unknown as string
} else {
// google map
polygonOptions.fillColor = `rgb(${fcR}, ${fcG}, ${fcB})`
polygonOptions.fillOpacity = fcA
polygonOptions.strokeColor = `rgb(${scR}, ${scG}, ${scB})`
polygonOptions.strokeOpacity = scA
}
if (polygonIns) {
// 更新区域属性
polygonIns.setOptions(polygonOptions)
return
onMapReady(
(
map: QQMap | GoogleMap,
maps: QQMaps | GoogleMaps,
trigger: CustomEventTrigger
) => {
// 绘制区域
function drawPolygon() {
const {
points,
strokeWidth,
strokeColor,
dashArray,
fillColor,
zIndex,
} = props
const path = points.map((item: Point) => {
const { latitude, longitude } = item
return getIsAMap()
? [longitude, latitude]
: new (maps as QQMaps | GoogleMaps).LatLng(latitude, longitude)
})
const { r: fcR, g: fcG, b: fcB, a: fcA } = hexToRgba(fillColor)
const { r: scR, g: scG, b: scB, a: scA } = hexToRgba(strokeColor)
const polygonOptions: PolygonOptions = {
//多边形是否可点击。
clickable: true,
//鼠标在多边形内的光标样式。
cursor: 'crosshair',
//多边形是否可编辑。
editable: false,
// 地图实例,即要显示多边形的地图
// @ts-ignore
map,
// 区域填充色
fillColor: '',
//多边形的路径,以经纬度坐标数组构成。
path,
// 区域边框
strokeColor: '',
//多边形的边框样式。实线是solid,虚线是dash。
strokeDashStyle: dashArray.some((item: number) => item > 0)
? 'dash'
: 'solid',
//多边形的边框线宽。
strokeWeight: strokeWidth,
//多边形是否可见。
visible: true,
//多边形的zIndex值。
zIndex: zIndex,
}
// 多边形的填充色、边框以及相应的透明度
if ((maps as QQMaps).Color) {
// 说明是 腾讯地图,google map 实例没有 Color 属性
// 将类型转为两者共有的 string,避免 ts 报错
polygonOptions.fillColor = new (maps as QQMaps).Color(
fcR,
fcG,
fcB,
fcA
) as unknown as string
polygonOptions.strokeColor = new (maps as QQMaps).Color(
scR,
scG,
scB,
scA
) as unknown as string
} else {
// google & 高德 map
polygonOptions.fillColor = `rgb(${fcR}, ${fcG}, ${fcB})`
polygonOptions.fillOpacity = fcA
polygonOptions.strokeColor = `rgb(${scR}, ${scG}, ${scB})`
polygonOptions.strokeOpacity = scA
}
if (polygonIns) {
// 更新区域属性
polygonIns.setOptions(polygonOptions)
return
}
// 说明是新增区域
polygonIns = new maps.Polygon(polygonOptions)
}
// 说明是新增区域
polygonIns = new maps.Polygon(polygonOptions)
// 给地图添加区域
drawPolygon()
// 监听 props
watch(props, drawPolygon)
}
// 给地图添加区域
drawPolygon()
// 监听 props
watch(props, drawPolygon)
})
)
onUnmounted(() => {
// 卸载时清除地图上绘制的 polygon
......
import { Maps, Map } from '../maps'
import { QQMap, GoogleMap } from '../maps'
import {
Polygon as QQPolygon,
PolygonOptions as QQPolygonOptions,
QQMaps,
} from '../maps/qq/types'
import { Polygon as GPolygon } from '../maps/google/types'
import { GoogleMaps, Polygon as GPolygon } from '../maps/google/types'
import { useCustomEvent } from '@dcloudio/uni-components'
import props from './props'
......@@ -15,8 +16,8 @@ export interface Point {
export type CustomEventTrigger = ReturnType<typeof useCustomEvent>
type OnMapReadyCallback = (
map: Map,
maps: Maps,
map: QQMap | GoogleMap,
maps: QQMaps | GoogleMaps,
trigger: CustomEventTrigger
) => void
......
......@@ -3,7 +3,7 @@ import { Point } from './interface'
// MapPolygon 组件的 props 属性配置
export default {
// 边框虚线,腾讯地图支持,google 地图不支持,默认值为[0, 0] 为实线,非 [0, 0] 为虚线,H5 端无法像微信小程序一样控制虚线的间隔像素大小
// 边框虚线,腾讯地图支持,google 高德 地图不支持,默认值为[0, 0] 为实线,非 [0, 0] 为虚线,H5 端无法像微信小程序一样控制虚线的间隔像素大小
dashArray: {
type: Array as PropType<number[]>,
default: () => [0, 0],
......
import { getIsAMap } from '../../../../helpers/location'
import { QQMaps, Overlay, LatLng as QLatLng } from './qq/types'
import { GoogleMaps, OverlayView, LatLng as GLatLng } from './google/types'
export interface CalloutOptions {
map?: any
position?: GLatLng | QLatLng
position?: GLatLng | QLatLng | AMap.LngLat
display?: 'ALWAYS'
boxShadow?: string
content?: string
......@@ -13,100 +13,165 @@ export interface CalloutOptions {
borderRadius?: number
bgColor?: string
top?: number
offsetY?: number
}
export function createCallout(maps: QQMaps | GoogleMaps) {
const overlay: OverlayView | Overlay = new ((maps as GoogleMaps)
.OverlayView || (maps as QQMaps).Overlay)()
export function createCallout(maps: QQMaps | GoogleMaps | AMap.NameSpace) {
function onAdd(this: Callout) {
const div = this.div
const panes = this.getPanes()!
panes.floatPane.appendChild(div)
}
function onRemove(this: Callout) {
const parentNode = this.div.parentNode
const parentNode = this.div!.parentNode
if (parentNode) {
parentNode.removeChild(this.div)
parentNode.removeChild(this.div!)
}
}
function createAMapText(this: Callout) {
const option = this.option
this.Text = new (maps as AMap.NameSpace).Text({
text: option.content,
anchor: 'bottom-center', // 设置文本标记锚点
offset: new (maps as AMap.NameSpace).Pixel(0, option.offsetY!),
style: {
'margin-bottom': '1rem',
padding: (option.padding || 8) + 'px',
'line-height': (option.fontSize || 14) + 'px',
'border-radius': (option.borderRadius || 0) + 'px',
'border-color': `${option.bgColor || '#fff'} transparent transparent`,
'background-color': option.bgColor || '#fff',
'box-shadow': '0 2px 6px 0 rgba(114, 124, 245, .5)',
'text-align': 'center',
'font-size': (option.fontSize || 14) + 'px',
color: option.color || '#000',
},
position: option.position as any,
})
const event =
(maps as QQMaps | GoogleMaps).event || (maps as AMap.NameSpace).Event
event.addListener(this.Text, 'click', () => {
this.callback!()
})
this.Text.setMap(option.map)
}
function removeAMapText(this: Callout) {
if (this.Text) {
this.option.map.remove(this.Text)
}
}
class Callout implements OverlayView, Overlay {
option: CalloutOptions
position?: GLatLng | QLatLng
position?: GLatLng | QLatLng | AMap.LngLat
index?: number
visible?: boolean
alwaysVisible?: boolean
div: HTMLDivElement
triangle: HTMLDivElement
div?: HTMLDivElement
triangle?: HTMLDivElement
callback?: Function
Text?: AMap.Text
// @ts-ignore
setMap
// @ts-ignore
getMap
// @ts-ignore
setMap = overlay.setMap
getPanes
// @ts-ignore
getMap = overlay.getMap
getProjection
// @ts-ignore
getPanes = overlay.getPanes
map_changed
// @ts-ignore
getProjection = overlay.getProjection
map_changed = (overlay as any).map_changed
set = overlay.set
get = overlay.get
setOptions = overlay.setValues
bindTo = overlay.bindTo
bindsTo = (overlay as any).bindsTo
notify = overlay.notify
setValues = overlay.setValues
set: (key: string, value: any) => void
// @ts-ignore
unbind = overlay.unbind
get: (key: string) => any
// @ts-ignore
unbindAll = overlay.unbindAll
addListener = (overlay as any).addListener
setOptions: (values?: object | null | undefined) => void
// @ts-ignore
bindTo
// @ts-ignore
bindsTo
// @ts-ignore
notify
// @ts-ignore
setValues
// @ts-ignore
unbind
// @ts-ignore
unbindAll
// @ts-ignore
addListener
set onclick(callback: any) {
this.div.onclick = callback
this.div!.onclick = callback
}
get onclick(): any {
return this.div.onclick
return this.div!.onclick
}
constructor(option: CalloutOptions = {}) {
constructor(option: CalloutOptions = {}, callback?: Function) {
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)
if (getIsAMap()) {
this.callback = callback
if (this.visible) {
this.createAMapText()
}
} else {
const map = option.map
this.position = option.position
this.index = 1
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)
}
}
}
createAMapText = createAMapText
removeAMapText = removeAMapText
onAdd = onAdd
construct = onAdd
setOption(option: CalloutOptions) {
this.option = option
this.setPosition(option.position)
if (option.display === 'ALWAYS') {
this.alwaysVisible = this.visible = true
} else {
this.alwaysVisible = false
}
this.setStyle(option)
if (getIsAMap()) {
if (this.visible) {
this.createAMapText()
}
} else {
this.setPosition(option.position)
this.setStyle(option)
}
}
setStyle(option: CalloutOptions) {
const div = this.div
const div = this.div!
const divStyle = div.style
div.innerText = option.content || ''
divStyle.lineHeight = (option.fontSize || 14) + 'px'
......@@ -116,11 +181,11 @@ export function createCallout(maps: QQMaps | GoogleMaps) {
divStyle.borderRadius = (option.borderRadius || 0) + 'px'
divStyle.backgroundColor = option.bgColor || '#fff'
divStyle.marginTop = '-' + ((option.top || 0) + 5) + 'px'
this.triangle.style.borderColor = `${
this.triangle!.style.borderColor = `${
option.bgColor || '#fff'
} transparent transparent`
}
setPosition(position?: GLatLng | QLatLng) {
setPosition(position?: GLatLng | QLatLng | AMap.LngLat) {
this.position = position
this.draw()
}
......@@ -137,11 +202,38 @@ export function createCallout(maps: QQMaps | GoogleMaps) {
divStyle.top = pixel.y + 'px'
}
changed() {
const divStyle = this.div.style
const divStyle = this.div!.style
divStyle.display = this.visible ? 'block' : 'none'
}
onRemove = onRemove
destroy = onRemove
}
if (!getIsAMap()) {
const overlay: OverlayView | Overlay = new ((maps as GoogleMaps)
.OverlayView || (maps as QQMaps).Overlay)()
// @ts-ignore
Callout.prototype.setMap = overlay.setMap
// @ts-ignore
Callout.prototype.getMap = overlay.getMap
// @ts-ignore
Callout.prototype.getPanes = overlay.getPanes
// @ts-ignore
Callout.prototype.getProjection = overlay.getProjection
Callout.prototype.map_changed = (overlay as any).map_changed
Callout.prototype.set = overlay.set
Callout.prototype.get = overlay.get
Callout.prototype.setOptions = overlay.setValues
Callout.prototype.bindTo = overlay.bindTo
Callout.prototype.bindsTo = (overlay as any).bindsTo
Callout.prototype.notify = overlay.notify
Callout.prototype.setValues = overlay.setValues
// @ts-ignore
Callout.prototype.unbind = overlay.unbind
// @ts-ignore
Callout.prototype.unbindAll = overlay.unbindAll
Callout.prototype.addListener = (overlay as any).addListener
}
return Callout
}
import { MapType, getMapInfo } from '../../../../helpers/location'
import { MapType, getMapInfo, getIsAMap } from '../../../../helpers/location'
export * from './types'
import { QQMaps } from './qq/types'
import { GoogleMaps } from './google/types'
......@@ -16,7 +16,12 @@ interface GoogleMapsWithCallout extends GoogleMaps, MapsWithCallout {}
interface QQMapsWithCallout extends QQMaps, MapsWithCallout {}
export type Maps = GoogleMapsWithCallout | QQMapsWithCallout
interface AMapMapsWithCallout extends AMap.NameSpace, MapsWithCallout {}
export type Maps =
| GoogleMapsWithCallout
| QQMapsWithCallout
| AMapMapsWithCallout
let maps: Maps
const callbacksMap: Partial<Record<MapType, Array<(maps: Maps) => void>>> = {}
......@@ -39,7 +44,9 @@ export function loadMaps(libraries: string[], callback: (maps: Maps) => void) {
(window as WindowExt)[mapInfo.type] &&
(window as WindowExt)[mapInfo.type].maps
) {
maps = (window as WindowExt)[mapInfo.type].maps
maps = getIsAMap()
? (window as WindowExt)[mapInfo.type]
: (window as WindowExt)[mapInfo.type].maps
maps.Callout = maps.Callout || createCallout(maps)
callback(maps)
} else if (callbacks.length) {
......@@ -50,16 +57,21 @@ export function loadMaps(libraries: string[], callback: (maps: Maps) => void) {
const callbackName = GOOGLE_MAP_CALLBACKNAME + mapInfo.type
globalExt[callbackName] = function () {
delete globalExt[callbackName]
maps = (window as WindowExt)[mapInfo.type].maps
maps = getIsAMap()
? (window as WindowExt)[mapInfo.type]
: (window as WindowExt)[mapInfo.type].maps
maps.Callout = createCallout(maps)
callbacks.forEach((callback) => callback(maps))
callbacks.length = 0
}
if (getIsAMap()) {
handleAMapSecurityPolicy(mapInfo)
}
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&'
let src = getScriptBaseUrl(mapInfo.type)
if (mapInfo.type === MapType.QQ) {
libraries.push('geometry')
}
......@@ -73,3 +85,20 @@ export function loadMaps(libraries: string[], callback: (maps: Maps) => void) {
document.body.appendChild(script)
}
}
const getScriptBaseUrl = (mapType: string): string => {
const urlMap: any = {
qq: 'https://map.qq.com/api/js?v=2.exp&',
google: 'https://maps.googleapis.com/maps/api/js?',
AMap: 'https://webapi.amap.com/maps?v=2.0&',
}
return urlMap[mapType]
}
function handleAMapSecurityPolicy(mapInfo: AnyObject) {
;(window as any)._AMapSecurityConfig = {
securityJsCode: mapInfo.securityJsCode || '',
serviceHost: mapInfo.serviceHost || '',
}
}
......@@ -13,11 +13,17 @@ import {
PolylineOptions as QPolylineOptions,
Circle as QCircle,
CircleOptions as QCircleOptions,
Point as QPoint,
} from './qq/types'
export type Map = GMap | QMap
export type GoogleMap = GMap
export type QQMap = QMap
export type Map = GMap | QMap | AMap.Map
export type LatLng = GLatLng | QLatLng
export type Polyline = GPolyline | QPolyline
export type PolylineOptions = GPolylineOptions & QPolylineOptions
export type Circle = GCircle | QCircle
export type CircleOptions = GCircleOptions & QCircleOptions
export type Polyline = GPolyline | QPolyline | AMap.Polyline
export type PolylineOptions = GPolylineOptions &
QPolylineOptions &
AMap.PolylineOptions
export type Circle = GCircle | QCircle | AMap.Circle
export type CircleOptions = GCircleOptions & QCircleOptions & AMap.CircleOptions
export type Point = QPoint
......@@ -8,3 +8,14 @@ uni-map {
uni-map[hidden] {
display: none;
}
/* 处理高德地图 marker label 默认样式 */
.amap-marker-label{
padding:0;
border:none;
background-color: transparent;
}
/* 处理高德地图 open-location icon 被遮挡问题 */
.amap-marker>.amap-icon>img{
left:0!important;
top:0!important;
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册