feat: LayerPopup 新增自定义内容能力 (#1504)

* feat: 图层气泡 LayerPopup props 中的 config => items,formatField 和 formatValue 支持字符串格式

* fix: 图层气泡组件 LayerPopup 新增包裹 key、value 的组件

* feat: 图层气泡组件新增支持对 field、value、content 的自定义内容

* docs: 修复官网 G2 升级导致无法渲染的问题

* docs: 新增图层气泡自定义内容示例

* fix: 修复重新更新 Popup 内容问题

* feat: 1.气泡组件 Popup 和 LayerPopup 新增自定义标题的能力 2.修复文档中表格内使用 | 的问题

* docs: 修复 LayerPopup 文档中 TS 类型定义顺序问题

Co-authored-by: yanxiong <oujinhui.ojh@antgroup.com>
This commit is contained in:
heiyexing 2022-11-28 11:40:36 +08:00 committed by GitHub
parent 889abf7b65
commit ee3cc51d3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 401 additions and 215 deletions

View File

@ -109,6 +109,11 @@ const Demo: FunctionComponent = () => {
const newPopup = new LayerPopup({
items: [
{
title: (e) => {
const h1 = document.createElement('h1');
h1.innerText = e.name;
return h1;
},
layer: 'pointLayer',
fields: [
{
@ -122,25 +127,27 @@ const Demo: FunctionComponent = () => {
],
},
{
title: '线图层',
layer: 'lineLayer',
fields: ['name'],
},
{
title: '面图层',
layer: 'polygonLayer',
fields: ['name'],
},
],
trigger: 'click',
});
pointLayer.on('mousemove', (e) => {
console.log('point mousemove', e);
});
polygonLayer.on('mousemove', (e) => {
console.log('polygon mousemove', e);
});
lineString.on('mousemove', (e) => {
console.log('line mousemove', e);
trigger: 'hover',
});
// pointLayer.on('mousemove', (e) => {
// console.log('point mousemove', e);
// });
// polygonLayer.on('mousemove', (e) => {
// console.log('polygon mousemove', e);
// });
// lineString.on('mousemove', (e) => {
// console.log('line mousemove', e);
// });
newScene.addPopup(newPopup);
setPopup(newPopup);

View File

@ -220,7 +220,7 @@ const Demo: FunctionComponent = () => {
<button
onClick={() => {
popup?.setOptions({
title: undefined,
title: popup.getOptions().title ? undefined : 'Popup Title',
});
}}
>

View File

@ -6,8 +6,7 @@
"url": "https://github.com/antvis/L7"
},
"devDependencies": {
"@antv/g2": "^3.5.9",
"@antv/g2plot": "^2.3.40",
"@antv/g2": "^4.2.8",
"@antv/l7-district": "^2.3.9",
"@antv/l7-draw": "^3.0.9",
"@antv/l7-react": "^2.3.3",

View File

@ -5,16 +5,20 @@ import { get } from 'lodash';
// import { Container } from 'inversify';
import Popup from './popup';
type ElementType = DOM.ElementType;
export type LayerField = {
field: string;
formatField?: ((field: string) => string) | string;
formatValue?: ((value: any) => any) | string;
formatField?: ElementType | ((field: string, feature: any) => ElementType);
formatValue?: ElementType | ((value: any, feature: any) => ElementType);
getValue?: (feature: any) => any;
};
export type LayerPopupConfigItem = {
layer: ILayer | string;
fields: Array<LayerField | string>;
fields?: Array<LayerField | string>;
title?: ElementType | ((feature: any) => ElementType);
customContent?: ElementType | ((feature: any) => ElementType);
};
export interface ILayerPopupOption extends IPopupOption {
@ -86,6 +90,7 @@ export default class LayerPopup extends Popup<ILayerPopupOption> {
},
offsets: [0, 10],
closeButton: false,
closeOnClick: false,
autoClose: false,
closeOnEsc: false,
};
@ -158,15 +163,13 @@ export default class LayerPopup extends Popup<ILayerPopupOption> {
protected onLayerMouseMove(layer: ILayer, e: any) {
if (!this.isSameFeature(layer, e.featureId)) {
const frag = this.getLayerInfoFrag(layer, e);
this.setDOMContent(frag);
const { title, content } = this.getLayerInfoFrag(layer, e);
this.setDOMContent(content);
this.setTitle(title);
this.displayFeatureInfo = {
layer,
featureId: e.featureId,
};
}
if (!this.isShow) {
this.show();
}
}
@ -183,14 +186,15 @@ export default class LayerPopup extends Popup<ILayerPopupOption> {
if (this.isShow && this.isSameFeature(layer, e.featureId)) {
this.hide();
} else {
const frag = this.getLayerInfoFrag(layer, e);
this.setDOMContent(frag);
const { title, content } = this.getLayerInfoFrag(layer, e);
this.setDOMContent(content);
this.setLnglat(e.lngLat);
this.show();
this.setTitle(title);
this.displayFeatureInfo = {
layer,
featureId: e.featureId,
};
this.show();
}
}
@ -207,9 +211,10 @@ export default class LayerPopup extends Popup<ILayerPopupOption> {
* @param e
* @protected
*/
protected getLayerInfoFrag(layer: ILayer, e: any): DocumentFragment {
protected getLayerInfoFrag(layer: ILayer, e: any) {
const layerInfo = this.layerConfigMap.get(layer);
const frag = document.createDocumentFragment();
let titleFrag: DocumentFragment | undefined;
const contentFrag = document.createDocumentFragment();
if (layerInfo) {
let feature = e.feature;
if (
@ -219,29 +224,54 @@ export default class LayerPopup extends Popup<ILayerPopupOption> {
) {
feature = feature.properties;
}
const { fields } = layerInfo;
fields?.forEach((fieldConfig) => {
const { field, formatField, formatValue, getValue } =
typeof fieldConfig === 'string'
? // tslint:disable-next-line:no-object-literal-type-assertion
({ field: fieldConfig } as LayerField)
: fieldConfig;
const row = DOM.create('div', 'l7-layer-popup__row');
const value = getValue ? getValue(e.feature) : get(feature, field);
const { title, fields, customContent } = layerInfo;
const fieldText =
(formatField instanceof Function
? formatField(field)
: formatField) ?? field;
const valueText =
(formatValue instanceof Function
? formatValue(value)
: formatValue) ?? value;
row.innerHTML = `<span class="l7-layer-popup__key">${fieldText}</span>: <span class="l7-layer-popup__value">${valueText}</span>`;
frag.appendChild(row);
});
if (title) {
titleFrag = document.createDocumentFragment();
const titleElement = title instanceof Function ? title(feature) : title;
DOM.appendElementType(titleFrag, titleElement);
}
if (customContent) {
const content =
customContent instanceof Function
? customContent(feature)
: customContent;
DOM.appendElementType(contentFrag, content);
} else if (fields?.length) {
fields?.forEach((fieldConfig) => {
const { field, formatField, formatValue, getValue } =
typeof fieldConfig === 'string'
? // tslint:disable-next-line:no-object-literal-type-assertion
({ field: fieldConfig } as LayerField)
: fieldConfig;
const row = DOM.create('div', 'l7-layer-popup__row');
const value = getValue ? getValue(e.feature) : get(feature, field);
const fieldElement =
(formatField instanceof Function
? formatField(field, feature)
: formatField) ?? field;
const valueElement =
(formatValue instanceof Function
? formatValue(value, feature)
: formatValue) ?? value;
const fieldSpan = DOM.create('span', 'l7-layer-popup__key', row);
DOM.appendElementType(fieldSpan, fieldElement);
DOM.appendElementType(fieldSpan, document.createTextNode(''));
const valueSpan = DOM.create('span', 'l7-layer-popup__value', row);
DOM.appendElementType(valueSpan, valueElement);
contentFrag.appendChild(row);
});
}
}
return frag;
return {
title: titleFrag,
content: contentFrag,
};
}
/**

View File

@ -5,7 +5,6 @@ import {
IPopup,
IPopupOption,
ISceneService,
PopupHTML,
TYPES,
} from '@antv/l7-core';
import {
@ -18,6 +17,8 @@ import { EventEmitter } from 'eventemitter3';
import { Container } from 'inversify';
import { createL7Icon } from '../utils/icon';
type ElementType = DOM.ElementType;
export { Popup };
export default class Popup<O extends IPopupOption = IPopupOption>
@ -64,12 +65,6 @@ export default class Popup<O extends IPopupOption = IPopupOption>
*/
protected contentPanel: HTMLElement;
/**
* popup
* @protected
*/
protected title: HTMLElement;
/**
* DOM
* @protected
@ -124,12 +119,15 @@ export default class Popup<O extends IPopupOption = IPopupOption>
this.updateCloseOnEsc();
this.updateFollowCursor();
const { html, text } = this.popupOption;
const { html, text, title } = this.popupOption;
if (html) {
this.setHTML(html);
} else if (text) {
this.setText(text);
}
if (title) {
this.setTitle(title);
}
this.emit('open');
return this;
}
@ -187,7 +185,6 @@ export default class Popup<O extends IPopupOption = IPopupOption>
'style',
'lngLat',
'offsets',
'title',
])
) {
if (this.container) {
@ -195,6 +192,9 @@ export default class Popup<O extends IPopupOption = IPopupOption>
// @ts-ignore
this.container = undefined;
}
if (this.popupOption.title) {
this.setTitle(this.popupOption.title);
}
if (this.popupOption.html) {
this.setHTML(this.popupOption.html);
} else if (this.popupOption.text) {
@ -215,6 +215,9 @@ export default class Popup<O extends IPopupOption = IPopupOption>
} else if (this.checkUpdateOption(option, ['text']) && option.text) {
this.setText(option.text);
}
if (this.checkUpdateOption(option, ['title'])) {
this.setTitle(option.title);
}
if (this.checkUpdateOption(option, ['lngLat']) && option.lngLat) {
this.setLnglat(option.lngLat);
}
@ -259,9 +262,9 @@ export default class Popup<O extends IPopupOption = IPopupOption>
* HTML
* @param html
*/
public setHTML(html: PopupHTML) {
public setHTML(html: ElementType) {
this.popupOption.html = html;
return this.setDOMContent(this.getPopupHTMLFragment(html));
return this.setDOMContent(html);
}
/**
@ -273,6 +276,28 @@ export default class Popup<O extends IPopupOption = IPopupOption>
return this.setDOMContent(window.document.createTextNode(text));
}
public setTitle(title?: ElementType) {
this.show();
if (title) {
if (!this.contentTitle) {
this.contentTitle = DOM.create('div', 'l7-popup-content__title');
if (this.content.firstChild) {
this.content.insertBefore(
this.contentTitle!,
this.content.firstChild,
);
} else {
this.content.append(this.contentTitle!);
}
}
DOM.clearChildren(this.contentTitle!);
DOM.appendElementType(this.contentTitle!, title);
} else if (this.contentTitle) {
DOM.remove(this.contentTitle);
this.contentTitle = undefined;
}
}
/**
*
*/
@ -377,12 +402,12 @@ export default class Popup<O extends IPopupOption = IPopupOption>
/**
* Popup HTML
* @param htmlNode
* @param element
*/
protected setDOMContent(htmlNode: ChildNode | DocumentFragment) {
protected setDOMContent(element: ElementType) {
this.show();
this.createContent();
this.contentPanel.appendChild(htmlNode);
DOM.appendElementType(this.contentPanel, element);
this.update();
return this;
}
@ -434,21 +459,9 @@ export default class Popup<O extends IPopupOption = IPopupOption>
if (this.content) {
DOM.remove(this.content);
}
this.contentTitle = undefined;
this.content = DOM.create('div', 'l7-popup-content', this.container);
if (this.popupOption.title) {
this.contentTitle = DOM.create(
'div',
'l7-popup-content__title',
this.content,
);
this.contentTitle?.append(
this.getPopupHTMLFragment(this.popupOption.title),
);
} else {
this.contentTitle = undefined;
}
if (this.popupOption.closeButton) {
const closeButton = createL7Icon('l7-icon-guanbi');
DOM.addClass(closeButton, 'l7-popup-close-button');
@ -555,30 +568,4 @@ export default class Popup<O extends IPopupOption = IPopupOption>
protected checkUpdateOption(option: Partial<O>, keys: Array<keyof O>) {
return keys.some((key) => key in option);
}
/**
* HTML Fragment
* @param html
* @protected
*/
protected getPopupHTMLFragment(html: PopupHTML) {
const frag = window.document.createDocumentFragment();
const temp = window.document.createElement('body');
let child: ChildNode | null;
if (typeof html === 'string') {
temp.innerHTML = html;
} else if (Array.isArray(html)) {
temp.append(...html);
} else if (html instanceof HTMLElement) {
temp.append(html);
}
while (true) {
child = temp.firstChild;
if (!child) {
break;
}
frag.appendChild(child);
}
return frag;
}
}

View File

@ -1,10 +1,8 @@
import { anchorType } from '@antv/l7-utils';
import { anchorType, DOM } from '@antv/l7-utils';
import EventEmitter from 'eventemitter3';
import { Container } from 'inversify';
import { ILngLat } from '../map/IMapService';
export type PopupHTML = string | HTMLElement | HTMLElement[];
export interface IPopupOption {
/**
*
@ -79,12 +77,12 @@ export interface IPopupOption {
/**
* Popup HTML
*/
html?: PopupHTML;
html?: DOM.ElementType;
/**
* Popup
*/
title?: PopupHTML;
title?: DOM.ElementType;
/**
*
@ -97,7 +95,7 @@ export interface IPopup extends EventEmitter {
remove(): void;
setLnglat(lngLat: ILngLat): this;
getLnglat(): ILngLat;
setHTML(html: PopupHTML): this;
setHTML(html: DOM.ElementType): this;
setText(text: string): this;
setMaxWidth(maxWidth: string): this;
isOpen(): boolean;

View File

@ -61,7 +61,7 @@ scene.on('loaded', () => {
| 名称 | 说明 | 类型 |
| --------- | -------------------------------------------------- | -------------------------- |
| imageType | 截图图片的格式 | `'png'` &#124; `'jpeg'` |
| imageType | 截图图片的格式 | `'png'` \| `'jpeg'` |
| onExport | 截图成功后,用于接收图片 `Base64` 字符串的回调函数 | `(base64: string) => void` |
<embed src="@/docs/common/control/btn-api.md"></embed>

View File

@ -33,7 +33,7 @@ scene.on('loaded', () => {
| 名称 | 说明 | 类型 |
| ----------- | --------------------------------- | --------------------------------- |
| exitBtnIcon | 退出全屏按钮的图标 | `HTMLElement` &#124; `SVGElement` |
| exitBtnIcon | 退出全屏按钮的图标 | `HTMLElement` \| `SVGElement` |
| exitBtnText | 退出全屏按钮的文本 | `string` |
| exitTitle | 退出全屏按钮的文本的 `title` 属性 | `string` |

View File

@ -37,9 +37,9 @@ scene.on('loaded', () => {
## 配置
| 名称 | 说明 | 类型 |
| ------ | ------------------------------------------------------------------------------------------ | ---------------------- |
| layers | 需要被控制的 `layer` 数组,支持传入图层示例或者图层 id不传则默认读取当前 L7 中所有的图层 | `Array<ILayer|string>` |
| 名称 | 说明 | 类型 |
| ------ | ------------------------------------------------------------------------------------------ | ------------------------- |
| layers | 需要被控制的 `layer` 数组,支持传入图层示例或者图层 id不传则默认读取当前 L7 中所有的图层 | `Array<ILayer \| string>` |
<embed src="@/docs/common/control/popper-api.md"></embed>

View File

@ -31,9 +31,9 @@ scene.on('loaded', () => {
| 名称 | 说明 | 类型 |
| ------------ | ----------------------- | ------------------------- |
| zoomInText | 放大按钮的展示内容 | `Element` &#124; `string` |
| zoomInText | 放大按钮的展示内容 | `Element` \| `string` |
| zoomInTitle | 放大按钮的 `title` 属性 | `string` |
| zoomOutText | 缩小按钮的展示内容 | `Element` &#124; `string` |
| zoomOutText | 缩小按钮的展示内容 | `Element` \| `string` |
| zoomOutTitle | 缩小按钮的 `title` 属性 | `string` |
<embed src="@/docs/common/control/api.md"></embed>

View File

@ -16,6 +16,7 @@ LayerPopup 会自行对目标图层的鼠标事件进行监听,当用户点击
## 使用
[示例](/examples/component/popup#layerpopup)
[自定义内容示例](/zh/examples/component/popup/#customContent)
```ts
import { Scene, LayerPopup, PointLayer } from '@antv/l7';
@ -69,37 +70,47 @@ scene.on('loaded', () => {
| 名称 | 说明 | 类型 | 默认值 |
| ------- | --------------------------------------------------------------------------------------------- | ----------------------------- | --------- |
| items | 需要展示 Popup 的图层配置数组,每个选项类型可见 [LayerPopupConfigItem](#layerpopupconfigitem) | `Array<LayerPopupConfigItem>` | `[]` |
| trigger | 鼠标触发 Popup 展示的方式 | `'hover' | 'click'` | `'hover'` |
| trigger | 鼠标触发 Popup 展示的方式 | `'hover' \| 'click'` | `'hover'` |
### LayerPopupConfigItem
| 名称 | 说明 | 类型 |
| ------ | ------------------------------------------------------------------------------------------------- | --------------------- |
| layer | 需要展示 Popup 的目标图层实例,或其的 `id``name` | `BaseLayer | string` |
| fields | 需要展示的字段数组,支持传入字段 key 值字符串,或者针对该字段的详细配置 [LayerField](#layerfield) | `string | LayerField` |
| 名称 | 说明 | 类型 |
| ------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| layer | 需要展示 Popup 的目标图层实例,或其的 `id``name` | `BaseLayer` \| `string` |
| fields | 需要展示的字段数组,支持传入字段 key 值字符串,或者针对该字段的详细配置 [LayerField](#layerfield) | `string` \| `LayerField` |
| customContent | 自定义气泡内容,支持直接传入自定义内容或者通过回调函数返回自定义内容两种方式,与 `fields` 共存下优先读取该配置并渲染 | `ElementType \| ((feature: any) => ElementType)` |
| title | 自定义气泡标题,支持直接传入自定义内容或者通过回调函数返回自定义内容两种方式 | `ElementType \| ((feature: any) => ElementType)` |
### LayerField
| 名称 | 说明 | 类型 |
| ----------- | --------------------------- | --------------------------- |
| field | 字段的 key 值字符串 | `string` |
| formatField | 对展示的 key 字段进行格式化 | `(field: string) => string | string` |
| formatValue | 对展示的 value 值进行格式化 | `(value: any) => any | string` |
| getValue | 自定义获取值的方式 | `(feature: any) => any` |
| 名称 | 说明 | 类型 |
| ----------- | --------------------------- | --------------------------------------------------------------- |
| field | 字段的 key 值字符串 | `string` |
| formatField | 对展示的 key 字段进行格式化 | `ElementType \| ((field: string, feature: any) => ElementType)` |
| formatValue | 对展示的 value 值进行格式化 | `ElementType \| ((value: any, feature: any) => ElementType)` |
| getValue | 自定义获取值的方式 | `(feature: any) => any` |
### ElementType
```ts
type ElementType =
| HTMLElement
| HTMLElement[]
| DocumentFragment
| Text
| string;
```
## 方法
| 名称 | 说明 | 类型 |
| ---------- | --------------------------- | ------------------------------------------------------------------- |
| getOptions | 获取当前 Popup 配置 | `() => IPopupOption` |
| setOptions | 更新当前 Popup 配置 | `(newOption: Partial<IPopupOption>) => this` |
| show | 显示 Popup | `() => this` |
| hide | 隐藏 Popup | `() => this` |
| getIsShow | 判断当前气泡是否展示 | `() => boolean` |
| setHTML | 设置 Popup 内容展示的 HTML | `(html: string | HTMLElement | HTMLElement[]) => this` |
| setText | 设置 Popup 内容展示的文本 | `(text: string) => this` |
| setLngLat | 设置 Popup 锚点所在经纬度 | `(lngLat: { lng: number; lat: number } | [number, number]) => this` |
| panToPopup | 将地图平移至当前 Popup 位置 | `() => this` |
| 名称 | 说明 | 类型 |
| ---------- | --------------------------- | -------------------------------------------------------------------- |
| getOptions | 获取当前 Popup 配置 | `() => IPopupOption` |
| setOptions | 更新当前 Popup 配置 | `(newOption: Partial<IPopupOption>) => this` |
| show | 显示 Popup | `() => this` |
| hide | 隐藏 Popup | `() => this` |
| setLngLat | 设置 Popup 锚点所在经纬度 | `(lngLat: { lng: number; lat: number } \| [number, number]) => this` |
| panToPopup | 将地图平移至当前 Popup 位置 | `() => this` |
## 事件

View File

@ -57,28 +57,39 @@ scene.on('loaded', () => {
## 配置
| 名称 | 说明 | 类型 | 默认值 |
| ------------------ | ------------------------------------------------------------------------ | -------------------------------------- |------------|
| lngLat | Popup 所在的经纬度 | `{ lng: number; lat: number }` | - |
| text | Popup 内容展示的文本内容 | `string` | - |
| html | Popup 内容展示的自定义 HTML可以传 HTML 字符串,也可以传 DOM 对象或数组 | `string | HTMLElement | HTMLElement[]` | - |
| title | Popup 标题展示的自定义 HTML可以传 HTML 字符串,也可以传 DOM 对象或数组 | `string | HTMLElement | HTMLElement[]` | - |
| closeOnClick | 点击地图区域时,是否关闭当前 Popup | `boolean` | `false` |
| closeOnEsc | 点击 Esc 键时,是否关闭当前 Popup | `boolean` | `false` |
| maxWidth | Popup 的最大宽度 | `string` | `240px` |
| anchor | Popup 箭头位置,可以控制 Popup 相对于经纬度点的展示位置 | [AnchorType](#anchortype) | `'bottom'` |
| offsets | Popup 相对于锚点的偏移 | `[number, number]` | `[0, 0]` |
| autoPan | 当 Popup 展示或者位置发生变化时,地图是否要自动平移至 Popup 所在位置 | `boolean` | `false` |
| autoClose | 当有其他 Popup 展示时,是否自动关闭当前气泡 | `boolean` | `true` |
| followCursor | Popup 是否跟随光标移动,若设为 `true`,则 `lngLat` 配置无效 | `boolean` | `false` |
| closeButton | 是否展示关闭 Popup 图标 | `boolean` | `true` |
| closeButtonOffsets | 关闭 Popup 图标的相对偏移 | `[number, number]` | - |
| stopPropagation | Popup 上的鼠标事件是否要阻止其冒泡 | `boolean` | `true` |
| 名称 | 说明 | 类型 | 默认值 |
| ------------------ | ------------------------------------------------------------------------ | ------------------------------ | ---------- |
| lngLat | Popup 所在的经纬度 | `{ lng: number; lat: number }` | - |
| text | Popup 内容展示的文本内容 | `string` | - |
| html | Popup 内容展示的自定义 HTML可以传 HTML 字符串,也可以传 DOM 对象或数组 | [ElementType](#elementtype) | - |
| title | Popup 标题展示的自定义 HTML可以传 HTML 字符串,也可以传 DOM 对象或数组 | [ElementType](#elementtype) | - |
| closeOnClick | 点击地图区域时,是否关闭当前 Popup | `boolean` | `false` |
| closeOnEsc | 点击 Esc 键时,是否关闭当前 Popup | `boolean` | `false` |
| maxWidth | Popup 的最大宽度 | `string` | `240px` |
| anchor | Popup 箭头位置,可以控制 Popup 相对于经纬度点的展示位置 | [AnchorType](#anchortype) | `'bottom'` |
| offsets | Popup 相对于锚点的偏移 | `[number, number]` | `[0, 0]` |
| autoPan | 当 Popup 展示或者位置发生变化时,地图是否要自动平移至 Popup 所在位置 | `boolean` | `false` |
| autoClose | 当有其他 Popup 展示时,是否自动关闭当前气泡 | `boolean` | `true` |
| followCursor | Popup 是否跟随光标移动,若设为 `true`,则 `lngLat` 配置无效 | `boolean` | `false` |
| closeButton | 是否展示关闭 Popup 图标 | `boolean` | `true` |
| closeButtonOffsets | 关闭 Popup 图标的相对偏移 | `[number, number]` | - |
| stopPropagation | Popup 上的鼠标事件是否要阻止其冒泡 | `boolean` | `true` |
### ElementType
```ts
type ElementType =
| HTMLElement
| HTMLElement[]
| DocumentFragment
| Text
| string;
```
### AnchorType
```ts
export type AnchorType =
type AnchorType =
| 'center'
| 'top'
| 'top-left'
@ -92,17 +103,18 @@ export type AnchorType =
## 方法
| 名称 | 说明 | 类型 |
| ---------- | --------------------------- | ------------------------------------------------------------------- |
| getOptions | 获取当前 Popup 配置 | `() => IPopupOption` |
| setOptions | 更新当前 Popup 配置 | `(newOption: Partial<IPopupOption>) => this` |
| show | 显示 Popup | `() => this` |
| hide | 隐藏 Popup | `() => this` |
| getIsShow | 判断当前气泡是否展示 | `() => boolean` |
| setHTML | 设置 Popup 内容展示的 HTML | `(html: string | HTMLElement | HTMLElement[]) => this` |
| setText | 设置 Popup 内容展示的文本 | `(text: string) => this` |
| setLngLat | 设置 Popup 锚点所在经纬度 | `(lngLat: { lng: number; lat: number } | [number, number]) => this` |
| panToPopup | 将地图平移至当前 Popup 位置 | `() => this` |
| 名称 | 说明 | 类型 |
| ---------- | --------------------------- | -------------------------------------------------------------------- |
| getOptions | 获取当前 Popup 配置 | `() => IPopupOption` |
| setOptions | 更新当前 Popup 配置 | `(newOption: Partial<IPopupOption>) => this` |
| show | 显示 Popup | `() => this` |
| hide | 隐藏 Popup | `() => this` |
| getIsShow | 判断当前气泡是否展示 | `() => boolean` |
| setTitle | 设置 Popup 标题展示的 HTML | `(title: ElementType) => this` |
| setHTML | 设置 Popup 内容展示的 HTML | `(html: ElementType) => this` |
| setText | 设置 Popup 内容展示的文本 | `(text: string) => this` |
| setLngLat | 设置 Popup 锚点所在经纬度 | `(lngLat: { lng: number; lat: number } \| [number, number]) => this` |
| panToPopup | 将地图平移至当前 Popup 位置 | `() => this` |
## 事件

View File

@ -1,6 +1,6 @@
| 名称 | 说明 | 类型 |
| -------- | ------------------------------------------------------- | --------------------------------- |
| btnIcon | 按钮图标 | `HTMLElement` &#124; `SVGElement` |
| btnIcon | 按钮图标 | `HTMLElement` \| `SVGElement` |
| btnText | 按钮内容文本 | `string` |
| title | 按钮的 `title` 属性 | `string` |
| vertical | 在 btnIcon 有值的情况下,按钮内的图标和文案是否纵向排列 | `boolean` |

View File

@ -1,7 +1,7 @@
| 名称 | 说明 | 类型 |
| --------------- | -------------------- | ----------------------------------- |
| popperPlacement | 气泡相对于按钮的位置 | [PopperPlacement](#PopperPlacement) |
| popperTrigger | 气泡弹出的触发方式 | `'click'` &#124; `'hover'` |
| popperTrigger | 气泡弹出的触发方式 | `'click'` \| `'hover'` |
| popperClassName | 气泡容器自定义样式名 | `string` |
### PopperPlacement

View File

@ -0,0 +1,132 @@
import * as G2 from '@antv/g2';
import { GaodeMap, LayerPopup, PointLayer, Scene } from '@antv/l7';
const data: any[] = [
{
lng: 120.132235,
lat: 30.250868,
value: 34.71314604052238,
name: '坐标点1',
},
{
lng: 120.156236,
lat: 30.260268,
value: 96.807880210153,
name: '坐标点2',
},
{
lng: 120.163014,
lat: 30.251297,
value: 29.615472482876815,
name: '坐标点3',
},
{
lng: 120.15394,
lat: 30.231489,
value: 49.90316258911784,
name: '坐标点4',
},
{
lng: 120.154596,
lat: 30.24065,
value: 45.788587061188466,
name: '坐标点5',
},
{
lng: 120.150223,
lat: 30.235078,
value: 29.741111717098544,
name: '坐标点6',
},
{
lng: 120.143992,
lat: 30.229411,
value: 40.241555782182935,
name: '坐标点7',
},
{
lng: 120.136995,
lat: 30.237439,
value: 86.5369792415296,
name: '坐标点8',
},
];
const scene = new Scene({
id: 'map',
map: new GaodeMap({
pitch: 0,
style: 'normal',
center: [120.154672, 30.241095],
zoom: 12,
}),
logoVisible: false,
});
scene.on('loaded', () => {
const pointLayer = new PointLayer({});
pointLayer
.source(data, {
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
})
.color('value', ['#FFCCC6', '#CF1421'])
.size(10)
.shape('circle');
scene.addLayer(pointLayer);
const div = document.createElement('div');
const chart = new G2.Chart({
container: div,
autoFit: true,
width: 200,
height: 200,
});
// 新建一个 view 用来单独渲染Annotation
chart.coordinate('theta', {
radius: 0.75,
innerRadius: 0.5,
});
chart.scale('percent', {
formatter: (val) => {
val = val * 100 + '%';
return val;
},
});
chart.tooltip(false);
// 声明需要进行自定义图例字段: 'item'
chart.legend(false);
chart
.interval()
.adjust('stack')
.position('percent')
.color('item', ['#5B8FF9', '#797979'])
.style({
fillOpacity: 1,
});
const layerPopup = new LayerPopup({
items: [
{
layer: pointLayer,
customContent: (e) => {
const otherValue = 100 - e.value;
chart.data([
{ item: '值', count: e.value, percent: e.value / 100 },
{ item: '', count: otherValue, percent: otherValue / 100 },
]);
chart.render();
return div;
},
},
],
});
scene.addPopup(layerPopup);
});

View File

@ -27,6 +27,14 @@
"en": "LayerPopup"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*HbyzRqv6JZcAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "customContent.ts",
"title": {
"zh": "图层气泡 自定义内容",
"en": "LayerPopup"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*orwHQKugIdIAAAAAAAAAAAAADmJ7AQ/original"
}
]
}

View File

@ -6,9 +6,9 @@ const scene = new Scene({
id: 'map',
map: new GaodeMap({
style: 'light',
center: [ 2.6125016864608597, 49.359131 ],
zoom: 4.19
})
center: [2.6125016864608597, 49.359131],
zoom: 4.19,
}),
});
scene.on('loaded', () => {
addChart();
@ -16,11 +16,11 @@ scene.on('loaded', () => {
});
function addChart() {
fetch(
'https://gw.alipayobjects.com/os/basement_prod/0b96cca4-7e83-449a-93d0-2a77053e74ab.json'
'https://gw.alipayobjects.com/os/basement_prod/0b96cca4-7e83-449a-93d0-2a77053e74ab.json',
)
.then(res => res.json())
.then(data => {
data.nodes.forEach(function(item) {
.then((res) => res.json())
.then((data) => {
data.nodes.forEach(function (item) {
const el = document.createElement('div');
const total =
item.gdp.Agriculture + item.gdp.Industry + item.gdp.Service;
@ -33,18 +33,18 @@ function addChart() {
{
item: 'Agriculture',
count: item.gdp.Agriculture,
percent: item.gdp.Agriculture / total
percent: item.gdp.Agriculture / total,
},
{
item: 'Industry',
count: item.gdp.Industry,
percent: item.gdp.Industry / total
percent: item.gdp.Industry / total,
},
{
item: 'Service',
count: item.gdp.Service,
percent: item.gdp.Service / total
}
percent: item.gdp.Service / total,
},
];
const chart = new G2.Chart({
@ -52,25 +52,24 @@ function addChart() {
width: size,
height: size,
render: 'svg',
padding: 0
padding: 0,
});
chart.legend(false);
chart.source(itemData);
chart.data(itemData);
chart.tooltip(false);
chart.axis('count', {
grid: false
});
chart.axis('count', false);
chart.axis('item', false);
chart
.interval()
.position('item*count')
.color('item', [ '#5CCEA1', '#5D7092', '#5B8FF9' ])
.opacity(1);
.color('item', ['#5CCEA1', '#5D7092', '#5B8FF9']);
chart.render();
const marker = new Marker({
element: el
element: el,
}).setLnglat({
lng: item.coordinates[0],
lat: item.coordinates[1]
lat: item.coordinates[1],
});
scene.addMarker(marker);
});

View File

@ -6,7 +6,7 @@ const scene = new Scene({
id: 'map',
map: new GaodeMap({
style: 'light',
center: [ 2.6125016864608597, 49.359131 ],
center: [2.6125016864608597, 49.359131],
zoom: 4.19
})
});
@ -20,7 +20,7 @@ function addChart() {
)
.then(res => res.json())
.then(data => {
data.nodes.forEach(function(item) {
data.nodes.forEach(function (item) {
const el = document.createElement('div');
const total =
item.gdp.Agriculture + item.gdp.Industry + item.gdp.Service;
@ -47,28 +47,6 @@ function addChart() {
}
];
const sliceNumber = 0.02;
// 自定义 other 的图形,增加两条线
G2.Shape.registerShape('interval', 'sliceShape', {
draw: function draw(cfg, container) {
const points = cfg.points;
let path = [];
path.push([ 'M', points[0].x, points[0].y ]);
path.push([ 'L', points[1].x, points[1].y - sliceNumber ]);
path.push([ 'L', points[2].x, points[2].y - sliceNumber ]);
path.push([ 'L', points[3].x, points[3].y ]);
path.push('Z');
path = this.parsePath(path);
return container.addShape('path', {
attrs: {
fill: cfg.color,
path
}
});
}
});
const chart = new G2.Chart({
container: el,
width: size,
@ -76,17 +54,18 @@ function addChart() {
render: 'svg',
padding: 0
});
console.log(chart)
chart.legend(false);
chart.source(itemData);
chart.data(itemData);
chart.coord('theta', {
innerRadius: 0.6
});
chart.tooltip(false);
chart
.intervalStack()
.position('percent')
.color('item', [ '#5CCEA1', '#5D7092', '#5B8FF9' ])
.shape('sliceShape');
.interval()
.adjust('stack')
.position('percent').color('item', ['#5CCEA1', '#5D7092', '#5B8FF9'])
.shape('sliceShape')
chart.render();
const marker = new Marker({
element: el

View File

@ -3,6 +3,13 @@ import { $window } from './mini-adapter';
export type ELType = HTMLElement | SVGElement;
export type ElementType =
| HTMLElement
| HTMLElement[]
| DocumentFragment
| Text
| string;
export function getContainer(domId: string | HTMLDivElement) {
let $dom = domId as HTMLDivElement;
if (typeof domId === 'string') {
@ -225,3 +232,20 @@ export function clearChildren(el: ELType) {
export function setUnDraggable(el: ELType) {
el.setAttribute('draggable', 'false');
}
export function appendElementType(
container: HTMLElement | DocumentFragment,
children: ElementType,
) {
if (typeof children === 'string') {
const div = document.createElement('div');
div.innerHTML = children;
while (div.firstChild) {
container.append(div.firstChild);
}
} else if (Array.isArray(children)) {
container.append(...children);
} else {
container.append(children);
}
}