mirror of https://gitee.com/antv-l7/antv-l7
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:
parent
889abf7b65
commit
ee3cc51d3f
|
@ -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);
|
||||
|
|
|
@ -220,7 +220,7 @@ const Demo: FunctionComponent = () => {
|
|||
<button
|
||||
onClick={() => {
|
||||
popup?.setOptions({
|
||||
title: undefined,
|
||||
title: popup.getOptions().title ? undefined : 'Popup Title',
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,7 +224,21 @@ export default class LayerPopup extends Popup<ILayerPopupOption> {
|
|||
) {
|
||||
feature = feature.properties;
|
||||
}
|
||||
const { fields } = layerInfo;
|
||||
const { title, fields, customContent } = layerInfo;
|
||||
|
||||
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'
|
||||
|
@ -229,19 +248,30 @@ export default class LayerPopup extends Popup<ILayerPopupOption> {
|
|||
const row = DOM.create('div', 'l7-layer-popup__row');
|
||||
const value = getValue ? getValue(e.feature) : get(feature, field);
|
||||
|
||||
const fieldText =
|
||||
const fieldElement =
|
||||
(formatField instanceof Function
|
||||
? formatField(field)
|
||||
? formatField(field, feature)
|
||||
: formatField) ?? field;
|
||||
const valueText =
|
||||
|
||||
const valueElement =
|
||||
(formatValue instanceof Function
|
||||
? formatValue(value)
|
||||
? formatValue(value, feature)
|
||||
: formatValue) ?? value;
|
||||
row.innerHTML = `<span class="l7-layer-popup__key">${fieldText}</span>: <span class="l7-layer-popup__value">${valueText}</span>`;
|
||||
frag.appendChild(row);
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,20 +459,8 @@ export default class Popup<O extends IPopupOption = IPopupOption>
|
|||
if (this.content) {
|
||||
DOM.remove(this.content);
|
||||
}
|
||||
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;
|
||||
}
|
||||
this.content = DOM.create('div', 'l7-popup-content', this.container);
|
||||
|
||||
if (this.popupOption.closeButton) {
|
||||
const closeButton = createL7Icon('l7-icon-guanbi');
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -61,7 +61,7 @@ scene.on('loaded', () => {
|
|||
|
||||
| 名称 | 说明 | 类型 |
|
||||
| --------- | -------------------------------------------------- | -------------------------- |
|
||||
| imageType | 截图图片的格式 | `'png'` | `'jpeg'` |
|
||||
| imageType | 截图图片的格式 | `'png'` \| `'jpeg'` |
|
||||
| onExport | 截图成功后,用于接收图片 `Base64` 字符串的回调函数 | `(base64: string) => void` |
|
||||
|
||||
<embed src="@/docs/common/control/btn-api.md"></embed>
|
||||
|
|
|
@ -33,7 +33,7 @@ scene.on('loaded', () => {
|
|||
|
||||
| 名称 | 说明 | 类型 |
|
||||
| ----------- | --------------------------------- | --------------------------------- |
|
||||
| exitBtnIcon | 退出全屏按钮的图标 | `HTMLElement` | `SVGElement` |
|
||||
| exitBtnIcon | 退出全屏按钮的图标 | `HTMLElement` \| `SVGElement` |
|
||||
| exitBtnText | 退出全屏按钮的文本 | `string` |
|
||||
| exitTitle | 退出全屏按钮的文本的 `title` 属性 | `string` |
|
||||
|
||||
|
|
|
@ -38,8 +38,8 @@ 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>
|
||||
|
||||
|
|
|
@ -31,9 +31,9 @@ scene.on('loaded', () => {
|
|||
|
||||
| 名称 | 说明 | 类型 |
|
||||
| ------------ | ----------------------- | ------------------------- |
|
||||
| zoomInText | 放大按钮的展示内容 | `Element` | `string` |
|
||||
| zoomInText | 放大按钮的展示内容 | `Element` \| `string` |
|
||||
| zoomInTitle | 放大按钮的 `title` 属性 | `string` |
|
||||
| zoomOutText | 缩小按钮的展示内容 | `Element` | `string` |
|
||||
| zoomOutText | 缩小按钮的展示内容 | `Element` \| `string` |
|
||||
| zoomOutTitle | 缩小按钮的 `title` 属性 | `string` |
|
||||
|
||||
<embed src="@/docs/common/control/api.md"></embed>
|
||||
|
|
|
@ -16,6 +16,7 @@ LayerPopup 会自行对目标图层的鼠标事件进行监听,当用户点击
|
|||
## 使用
|
||||
|
||||
[示例](/examples/component/popup#layerpopup)
|
||||
[自定义内容示例](/zh/examples/component/popup/#customContent)
|
||||
|
||||
```ts
|
||||
import { Scene, LayerPopup, PointLayer } from '@antv/l7';
|
||||
|
@ -69,36 +70,46 @@ 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` |
|
||||
| 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` |
|
||||
| setLngLat | 设置 Popup 锚点所在经纬度 | `(lngLat: { lng: number; lat: number } \| [number, number]) => this` |
|
||||
| panToPopup | 将地图平移至当前 Popup 位置 | `() => this` |
|
||||
|
||||
## 事件
|
||||
|
|
|
@ -58,11 +58,11 @@ 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[]` | - |
|
||||
| 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` |
|
||||
|
@ -75,10 +75,21 @@ scene.on('loaded', () => {
|
|||
| 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'
|
||||
|
@ -93,15 +104,16 @@ 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` |
|
||||
| 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` |
|
||||
| setLngLat | 设置 Popup 锚点所在经纬度 | `(lngLat: { lng: number; lat: number } \| [number, number]) => this` |
|
||||
| panToPopup | 将地图平移至当前 Popup 位置 | `() => this` |
|
||||
|
||||
## 事件
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
| 名称 | 说明 | 类型 |
|
||||
| -------- | ------------------------------------------------------- | --------------------------------- |
|
||||
| btnIcon | 按钮图标 | `HTMLElement` | `SVGElement` |
|
||||
| btnIcon | 按钮图标 | `HTMLElement` \| `SVGElement` |
|
||||
| btnText | 按钮内容文本 | `string` |
|
||||
| title | 按钮的 `title` 属性 | `string` |
|
||||
| vertical | 在 btnIcon 有值的情况下,按钮内的图标和文案是否纵向排列 | `boolean` |
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
| 名称 | 说明 | 类型 |
|
||||
| --------------- | -------------------- | ----------------------------------- |
|
||||
| popperPlacement | 气泡相对于按钮的位置 | [PopperPlacement](#PopperPlacement) |
|
||||
| popperTrigger | 气泡弹出的触发方式 | `'click'` | `'hover'` |
|
||||
| popperTrigger | 气泡弹出的触发方式 | `'click'` \| `'hover'` |
|
||||
| popperClassName | 气泡容器自定义样式名 | `string` |
|
||||
|
||||
### PopperPlacement
|
||||
|
|
|
@ -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);
|
||||
});
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue