mirror of https://gitee.com/antv-l7/antv-l7
feat: point text add overlap
This commit is contained in:
parent
d33d599f50
commit
a18561e548
|
@ -47,7 +47,7 @@ const defaultLayerConfig: Partial<ILayerConfig> = {
|
|||
],
|
||||
shape3d: ['cylinder', 'triangleColumn', 'hexagonColumn', 'squareColumn'],
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
maxZoom: 24,
|
||||
visible: true,
|
||||
autoFit: false,
|
||||
zIndex: 0,
|
||||
|
|
|
@ -55,6 +55,7 @@ export interface ILayerModel {
|
|||
getDefaultStyle(): unknown;
|
||||
getAnimateUniforms(): IModelUniform;
|
||||
buildModels(): IModel[];
|
||||
needUpdate(): boolean;
|
||||
}
|
||||
export interface IModelUniform {
|
||||
[key: string]: IUniform;
|
||||
|
|
|
@ -161,7 +161,9 @@ export default class Scene extends EventEmitter implements ISceneService {
|
|||
this.$container as HTMLDivElement,
|
||||
this.handleWindowResized,
|
||||
);
|
||||
// window.addEventListener('resize', this.handleWindowResized, false);
|
||||
window
|
||||
.matchMedia('screen and (-webkit-min-device-pixel-ratio: 1.5)')
|
||||
.addListener(this.handleWindowResized);
|
||||
} else {
|
||||
this.logger.error('容器 id 不存在');
|
||||
}
|
||||
|
@ -227,7 +229,9 @@ export default class Scene extends EventEmitter implements ISceneService {
|
|||
this.rendererService.destroy();
|
||||
this.map.destroy();
|
||||
unbind(this.$container as HTMLDivElement, this.handleWindowResized);
|
||||
// window.removeEventListener('resize', this.handleWindowResized, false);
|
||||
window
|
||||
.matchMedia('screen and (min-resolution: 2dppx)')
|
||||
.removeListener(this.handleWindowResized);
|
||||
}
|
||||
|
||||
private handleWindowResized = () => {
|
||||
|
|
|
@ -759,7 +759,16 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
}
|
||||
|
||||
protected renderModels() {
|
||||
throw new Error('Method not implemented.');
|
||||
if (this.layerModelNeedUpdate) {
|
||||
this.models = this.layerModel.buildModels();
|
||||
this.layerModelNeedUpdate = false;
|
||||
}
|
||||
this.models.forEach((model) => {
|
||||
model.draw({
|
||||
uniforms: this.layerModel.getUninforms(),
|
||||
});
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
protected getModelType(): unknown {
|
||||
|
|
|
@ -77,6 +77,9 @@ export default class BaseModel<ChildLayerStyleOptions = {}>
|
|||
return {};
|
||||
}
|
||||
|
||||
public needUpdate(): boolean {
|
||||
return false;
|
||||
}
|
||||
public buildModels(): IModel[] {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
|
|
@ -21,8 +21,12 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
|
|||
protected renderModels() {
|
||||
const shape = this.getModelType();
|
||||
if (shape === 'heatmap') {
|
||||
this.layerModel.render();
|
||||
return;
|
||||
this.layerModel.render(); // 独立的渲染流程
|
||||
return this;
|
||||
}
|
||||
if (this.layerModelNeedUpdate) {
|
||||
this.models = this.layerModel.buildModels();
|
||||
this.layerModelNeedUpdate = false;
|
||||
}
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
|
|
|
@ -20,8 +20,8 @@ import MultiPassRendererPlugin from './plugins/MultiPassRendererPlugin';
|
|||
import PixelPickingPlugin from './plugins/PixelPickingPlugin';
|
||||
import RegisterStyleAttributePlugin from './plugins/RegisterStyleAttributePlugin';
|
||||
import ShaderUniformPlugin from './plugins/ShaderUniformPlugin';
|
||||
import UpdateModelPlugin from './plugins/UpdateModelPlugin';
|
||||
import UpdateStyleAttributePlugin from './plugins/UpdateStyleAttributePlugin';
|
||||
|
||||
/**
|
||||
* 校验传入参数配置项的正确性
|
||||
* @see /dev-docs/ConfigSchemaValidation.md
|
||||
|
@ -74,6 +74,15 @@ container
|
|||
.bind<ILayerPlugin>(TYPES.ILayerPlugin)
|
||||
.to(UpdateStyleAttributePlugin)
|
||||
.inRequestScope();
|
||||
|
||||
/**
|
||||
* 负责Model更新
|
||||
*/
|
||||
container
|
||||
.bind<ILayerPlugin>(TYPES.ILayerPlugin)
|
||||
.to(UpdateModelPlugin)
|
||||
.inRequestScope();
|
||||
|
||||
/**
|
||||
* Multi Pass 自定义渲染管线
|
||||
*/
|
||||
|
|
|
@ -26,14 +26,6 @@ export default class LineLayer extends BaseLayer<ILineLayerStyleOptions> {
|
|||
};
|
||||
return defaultConfig[type];
|
||||
}
|
||||
protected renderModels() {
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: this.layerModel.getUninforms(),
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected buildModels() {
|
||||
const shape = this.getModelType();
|
||||
|
|
|
@ -2,6 +2,9 @@ import { ILayer, ILayerPlugin, IMapService, TYPES } from '@antv/l7-core';
|
|||
import Source from '@antv/l7-source';
|
||||
import { encodePickingColor, rgb2arr } from '@antv/l7-utils';
|
||||
import { injectable } from 'inversify';
|
||||
/**
|
||||
* 更新图层样式,初始图层相关配置
|
||||
*/
|
||||
@injectable()
|
||||
export default class LayerStylePlugin implements ILayerPlugin {
|
||||
public apply(layer: ILayer) {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { ILayer, ILayerPlugin, IMapService, TYPES } from '@antv/l7-core';
|
||||
import { injectable } from 'inversify';
|
||||
/**
|
||||
* Model 更新
|
||||
*/
|
||||
@injectable()
|
||||
export default class UpdateModelPlugin implements ILayerPlugin {
|
||||
public apply(layer: ILayer) {
|
||||
layer.hooks.beforeRender.tap('UpdateModelPlugin', () => {
|
||||
// 处理文本更新
|
||||
layer.layerModel.needUpdate();
|
||||
// if (layer.layerModel.needUpdate()) {
|
||||
// layer.layerModelNeedUpdate = true;
|
||||
// }
|
||||
});
|
||||
}
|
||||
}
|
|
@ -28,23 +28,12 @@ export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
|
|||
fill: {},
|
||||
extrude: {},
|
||||
image: {},
|
||||
text: {},
|
||||
text: {
|
||||
blend: 'normal',
|
||||
},
|
||||
};
|
||||
return defaultConfig[type];
|
||||
}
|
||||
protected renderModels() {
|
||||
if (this.layerModelNeedUpdate) {
|
||||
this.models = this.layerModel.buildModels();
|
||||
this.layerModelNeedUpdate = false;
|
||||
}
|
||||
this.models.forEach((model) => {
|
||||
model.draw({
|
||||
uniforms: this.layerModel.getUninforms(),
|
||||
});
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
protected buildModels() {
|
||||
const modelType = this.getModelType();
|
||||
this.layerModel = new PointModels[modelType](this);
|
||||
|
@ -79,9 +68,4 @@ export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
|
|||
return 'text';
|
||||
}
|
||||
}
|
||||
|
||||
private updateData() {
|
||||
// const bounds = this.mapService.getBounds();
|
||||
// console.log(bounds);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,15 +21,7 @@ export default class ExtrudeModel extends BaseModel {
|
|||
vertexShader: pointExtrudeVert,
|
||||
fragmentShader: pointExtrudeFrag,
|
||||
triangulation: PointExtrudeTriangulation,
|
||||
blend: {
|
||||
enable: true,
|
||||
func: {
|
||||
srcRGB: gl.SRC_ALPHA,
|
||||
srcAlpha: 1,
|
||||
dstRGB: gl.ONE_MINUS_SRC_ALPHA,
|
||||
dstAlpha: 1,
|
||||
},
|
||||
},
|
||||
blend: this.getBlend(),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -42,15 +42,7 @@ export default class ImageModel extends BaseModel {
|
|||
triangulation: PointImageTriangulation,
|
||||
primitive: gl.POINTS,
|
||||
depth: { enable: false },
|
||||
blend: {
|
||||
enable: true,
|
||||
func: {
|
||||
srcRGB: gl.SRC_ALPHA,
|
||||
srcAlpha: 1,
|
||||
dstRGB: gl.ONE_MINUS_SRC_ALPHA,
|
||||
dstAlpha: 1,
|
||||
},
|
||||
},
|
||||
blend: this.getBlend(),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
BlendType,
|
||||
gl,
|
||||
IEncodeFeature,
|
||||
ILayer,
|
||||
ILayerConfig,
|
||||
IModel,
|
||||
IModelUniform,
|
||||
|
@ -10,7 +11,7 @@ import {
|
|||
} from '@antv/l7-core';
|
||||
import { rgb2arr } from '@antv/l7-utils';
|
||||
import BaseModel from '../../core/BaseModel';
|
||||
import { PointFillTriangulation } from '../../core/triangulation';
|
||||
import CollisionIndex from '../../utils/collision-index';
|
||||
import {
|
||||
getGlyphQuads,
|
||||
IGlyphQuad,
|
||||
|
@ -81,14 +82,18 @@ export function TextTriangulation(feature: IEncodeFeature) {
|
|||
|
||||
export default class TextModel extends BaseModel {
|
||||
private texture: ITexture2D;
|
||||
private glyphInfo: IEncodeFeature[];
|
||||
private currentZoom: number = -1;
|
||||
private extent: [[number, number], [number, number]];
|
||||
|
||||
public getUninforms(): IModelUniform {
|
||||
const {
|
||||
fontWeight = 'normal',
|
||||
fontWeight = 800,
|
||||
fontFamily,
|
||||
stroke,
|
||||
strokeWidth,
|
||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||
const { canvas, fontAtlas, mapping } = this.fontService;
|
||||
const { canvas } = this.fontService;
|
||||
return {
|
||||
u_opacity: 1.0,
|
||||
u_sdf_map: this.texture,
|
||||
|
@ -100,10 +105,9 @@ export default class TextModel extends BaseModel {
|
|||
}
|
||||
|
||||
public buildModels(): IModel[] {
|
||||
this.initTextFont();
|
||||
this.generateGlyphLayout();
|
||||
this.registerBuiltinAttributes();
|
||||
this.initGlyph();
|
||||
this.updateTexture();
|
||||
this.filterGlyphs();
|
||||
return [
|
||||
this.layer.buildLayerModel({
|
||||
moduleName: 'pointText',
|
||||
|
@ -115,9 +119,36 @@ export default class TextModel extends BaseModel {
|
|||
}),
|
||||
];
|
||||
}
|
||||
public needUpdate() {
|
||||
const {
|
||||
textAllowOverlap = false,
|
||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||
const zoom = this.mapService.getZoom();
|
||||
const extent = this.mapService.getBounds();
|
||||
const flag =
|
||||
extent[0][0] < this.extent[0][0] ||
|
||||
extent[0][1] < this.extent[0][1] ||
|
||||
extent[1][0] > this.extent[1][0] ||
|
||||
extent[1][1] < this.extent[1][1];
|
||||
|
||||
if (!textAllowOverlap && (Math.abs(this.currentZoom - zoom) > 1 || flag)) {
|
||||
this.filterGlyphs();
|
||||
this.layer.models = [
|
||||
this.layer.buildLayerModel({
|
||||
moduleName: 'pointText',
|
||||
vertexShader: textVert,
|
||||
fragmentShader: textFrag,
|
||||
triangulation: TextTriangulation,
|
||||
depth: { enable: false },
|
||||
blend: this.getBlend(),
|
||||
}),
|
||||
];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected registerBuiltinAttributes() {
|
||||
const viewProjection = this.cameraService.getViewProjectionMatrix();
|
||||
this.styleAttributeService.registerStyleAttribute({
|
||||
name: 'textOffsets',
|
||||
type: AttributeType.Attribute,
|
||||
|
@ -190,6 +221,18 @@ export default class TextModel extends BaseModel {
|
|||
},
|
||||
});
|
||||
}
|
||||
private textExtent(): [[number, number], [number, number]] {
|
||||
const bounds = this.mapService.getBounds();
|
||||
const step =
|
||||
Math.min(bounds[1][0] - bounds[0][0], bounds[1][1] - bounds[1][0]) / 2;
|
||||
return [
|
||||
[bounds[0][0] - step, bounds[0][1] - step],
|
||||
[bounds[1][0] + step, bounds[1][1] + step],
|
||||
];
|
||||
}
|
||||
/**
|
||||
* 生成文字纹理
|
||||
*/
|
||||
private initTextFont() {
|
||||
const {
|
||||
fontWeight = 'normal',
|
||||
|
@ -213,20 +256,19 @@ export default class TextModel extends BaseModel {
|
|||
fontFamily,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 生成文字布局
|
||||
*/
|
||||
private generateGlyphLayout() {
|
||||
const { canvas, fontAtlas, mapping } = this.fontService;
|
||||
const { mapping } = this.fontService;
|
||||
const {
|
||||
spacing = 2,
|
||||
textAnchor = 'center',
|
||||
textOffset,
|
||||
padding = [4, 4],
|
||||
textAllowOverlap,
|
||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||
const data = this.layer.getEncodedData();
|
||||
data.forEach((feature: IEncodeFeature) => {
|
||||
const { coordinates, shape = '' } = feature;
|
||||
const size = feature.size as number;
|
||||
const fontScale = size / 24;
|
||||
this.glyphInfo = data.map((feature: IEncodeFeature) => {
|
||||
const { shape = '' } = feature;
|
||||
const shaping = shapeText(
|
||||
shape.toString(),
|
||||
mapping,
|
||||
|
@ -239,19 +281,60 @@ export default class TextModel extends BaseModel {
|
|||
const glyphQuads = getGlyphQuads(shaping, textOffset, false);
|
||||
feature.shaping = shaping;
|
||||
feature.glyphQuads = glyphQuads;
|
||||
return feature;
|
||||
});
|
||||
}
|
||||
|
||||
private drawGlyph() {
|
||||
/**
|
||||
* 文字避让
|
||||
*/
|
||||
private filterGlyphs() {
|
||||
const {
|
||||
spacing = 2,
|
||||
textAnchor = 'center',
|
||||
textOffset = [0, 0],
|
||||
padding = [4, 4],
|
||||
textAllowOverlap,
|
||||
textAllowOverlap = false,
|
||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||
const viewProjection = this.cameraService.getViewProjectionMatrix();
|
||||
if (textAllowOverlap) {
|
||||
return;
|
||||
}
|
||||
this.currentZoom = this.mapService.getZoom();
|
||||
this.extent = this.textExtent();
|
||||
const { width, height } = this.rendererService.getViewportSize();
|
||||
const collisionIndex = new CollisionIndex(width, height);
|
||||
const filterData = this.glyphInfo.filter((feature: IEncodeFeature) => {
|
||||
const { shaping, id = 0 } = feature;
|
||||
const coordinates = feature.coordinates as [number, number];
|
||||
const size = feature.size as number;
|
||||
const fontScale: number = size / 24;
|
||||
const pixels = this.mapService.lngLatToContainer(coordinates);
|
||||
const { box } = collisionIndex.placeCollisionBox({
|
||||
x1: shaping.left * fontScale - padding[0],
|
||||
x2: shaping.right * fontScale + padding[0],
|
||||
y1: shaping.top * fontScale - padding[1],
|
||||
y2: shaping.bottom * fontScale + padding[1],
|
||||
anchorPointX: pixels.x,
|
||||
anchorPointY: pixels.y,
|
||||
});
|
||||
if (box && box.length) {
|
||||
// TODO:featureIndex
|
||||
collisionIndex.insertCollisionBox(box, id);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
this.layer.setEncodedData(filterData);
|
||||
}
|
||||
/**
|
||||
* 初始化文字布局
|
||||
*/
|
||||
private initGlyph() {
|
||||
// 1.生成文字纹理
|
||||
this.initTextFont();
|
||||
// 2.生成文字布局
|
||||
this.generateGlyphLayout();
|
||||
}
|
||||
/**
|
||||
* 更新文字纹理
|
||||
*/
|
||||
private updateTexture() {
|
||||
const { createTexture2D } = this.rendererService;
|
||||
const { canvas } = this.fontService;
|
||||
|
|
|
@ -27,7 +27,7 @@ void main() {
|
|||
vec4 projected_position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
|
||||
|
||||
gl_Position = vec4(projected_position.xy / projected_position.w
|
||||
+ a_textOffsets * fontScale / u_ViewportSize * 2., 0.0, 1.0);
|
||||
+ a_textOffsets * fontScale / u_ViewportSize * 2. * u_DevicePixelRatio, 0.0, 1.0);
|
||||
v_gamma_scale = gl_Position.w;
|
||||
|
||||
|
||||
|
|
|
@ -20,16 +20,6 @@ export default class PolygonLayer extends BaseLayer<IPolygonLayerStyleOptions> {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected renderModels() {
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: this.layerModel.getUninforms(),
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected buildModels() {
|
||||
const shape = this.getModelType();
|
||||
this.layerModel = new PolygonModels[shape](this);
|
||||
|
|
|
@ -9,10 +9,6 @@ export interface ICollisionBox {
|
|||
// @mapbox/grid-index 并没有类似 hitTest 的单纯获取碰撞检测结果的方法,query 将导致计算大量多余的包围盒结果,因此使用改良版
|
||||
import { mat4, vec4 } from 'gl-matrix';
|
||||
import GridIndex from './grid-index';
|
||||
|
||||
// 为 viewport 加上 buffer,避免边缘处的文本无法显示
|
||||
const viewportPadding = 100;
|
||||
|
||||
/**
|
||||
* 基于网格实现文本避让,大幅提升包围盒碰撞检测效率
|
||||
* @see https://zhuanlan.zhihu.com/p/74373214
|
||||
|
@ -21,6 +17,7 @@ export default class CollisionIndex {
|
|||
private width: number;
|
||||
private height: number;
|
||||
private grid: GridIndex;
|
||||
private viewportPadding: number = 100;
|
||||
private screenRightBoundary: number;
|
||||
private screenBottomBoundary: number;
|
||||
private gridRightBoundary: number;
|
||||
|
@ -28,30 +25,35 @@ export default class CollisionIndex {
|
|||
constructor(width: number, height: number) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.viewportPadding = Math.max(width, height);
|
||||
// 创建网格索引
|
||||
this.grid = new GridIndex(
|
||||
width + 2 * viewportPadding,
|
||||
height + 2 * viewportPadding,
|
||||
width + this.viewportPadding,
|
||||
height + this.viewportPadding,
|
||||
25,
|
||||
);
|
||||
|
||||
this.screenRightBoundary = width + viewportPadding;
|
||||
this.screenBottomBoundary = height + viewportPadding;
|
||||
this.gridRightBoundary = width + 2 * viewportPadding;
|
||||
this.gridBottomBoundary = height + 2 * viewportPadding;
|
||||
this.screenRightBoundary = width + this.viewportPadding;
|
||||
this.screenBottomBoundary = height + this.viewportPadding;
|
||||
this.gridRightBoundary = width + 2 * this.viewportPadding;
|
||||
this.gridBottomBoundary = height + 2 * this.viewportPadding;
|
||||
}
|
||||
|
||||
public placeCollisionBox(collisionBox: ICollisionBox, mvpMatrix: mat4) {
|
||||
const projectedPoint = this.project(
|
||||
mvpMatrix,
|
||||
collisionBox.anchorPointX,
|
||||
collisionBox.anchorPointY,
|
||||
);
|
||||
public placeCollisionBox(collisionBox: ICollisionBox) {
|
||||
// const projectedPoint = this.project(
|
||||
// mvpMatrix,
|
||||
// collisionBox.anchorPointX,
|
||||
// collisionBox.anchorPointY,
|
||||
// );
|
||||
|
||||
const tlX = collisionBox.x1 + projectedPoint.x;
|
||||
const tlY = collisionBox.y1 + projectedPoint.y;
|
||||
const brX = collisionBox.x2 + projectedPoint.x;
|
||||
const brY = collisionBox.y2 + projectedPoint.y;
|
||||
const tlX =
|
||||
collisionBox.x1 + collisionBox.anchorPointX + this.viewportPadding;
|
||||
const tlY =
|
||||
collisionBox.y1 + collisionBox.anchorPointY + this.viewportPadding;
|
||||
const brX =
|
||||
collisionBox.x2 + collisionBox.anchorPointX + this.viewportPadding;
|
||||
const brY =
|
||||
collisionBox.y2 + collisionBox.anchorPointY + this.viewportPadding;
|
||||
|
||||
if (
|
||||
!this.isInsideGrid(tlX, tlY, brX, brY) ||
|
||||
|
@ -79,14 +81,16 @@ export default class CollisionIndex {
|
|||
* @param {number} y P20 平面坐标Y
|
||||
* @return {Point} projectedPoint
|
||||
*/
|
||||
public project(mvpMatrix: mat4, x: number, y: number) {
|
||||
public project(mvpMatrix: number[], x: number, y: number) {
|
||||
const point = vec4.fromValues(x, y, 0, 1);
|
||||
const out = vec4.create();
|
||||
vec4.transformMat4(out, point, mvpMatrix);
|
||||
// @ts-ignore
|
||||
const mat = mat4.fromValues(...mvpMatrix);
|
||||
vec4.transformMat4(out, point, mat);
|
||||
// GL 坐标系[-1, 1] -> viewport 坐标系[width, height]
|
||||
return {
|
||||
x: ((out[0] / out[3] + 1) / 2) * this.width + viewportPadding,
|
||||
y: ((-out[1] / out[3] + 1) / 2) * this.height + viewportPadding,
|
||||
x: ((out[0] / out[3] + 1) / 2) * this.width + this.viewportPadding,
|
||||
y: ((-out[1] / out[3] + 1) / 2) * this.height + this.viewportPadding,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ type CallBack = (...args: any[]) => any;
|
|||
* @see https://zhuanlan.zhihu.com/p/74373214
|
||||
*/
|
||||
class GridIndex {
|
||||
private boxCells: number[][];
|
||||
private boxCells: number[][] = [];
|
||||
private xCellCount: number;
|
||||
private yCellCount: number;
|
||||
private boxKeys: string[];
|
||||
|
|
|
@ -44,7 +44,7 @@ export default class TextLayerDemo extends React.Component {
|
|||
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
map: new Mapbox({
|
||||
center: [120.19382669582967, 30.258134],
|
||||
pitch: 0,
|
||||
style: 'dark',
|
||||
|
@ -61,16 +61,16 @@ export default class TextLayerDemo extends React.Component {
|
|||
},
|
||||
})
|
||||
.shape('m', 'text')
|
||||
.size(24)
|
||||
.size(12)
|
||||
.color('#fff')
|
||||
.style({
|
||||
fontWeight: 800,
|
||||
fontWeight: 200,
|
||||
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
|
||||
textOffset: [0, 0], // 文本相对锚点的偏移量 [水平, 垂直]
|
||||
spacing: 2, // 字符间距
|
||||
padding: [4, 4], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
|
||||
strokeColor: 'white', // 描边颜色
|
||||
strokeWidth: 4, // 描边宽度
|
||||
padding: [1, 1], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
|
||||
stroke: 'red', // 描边颜色
|
||||
strokeWidth: 2, // 描边宽度
|
||||
strokeOpacity: 1.0,
|
||||
});
|
||||
scene.addLayer(pointLayer);
|
||||
|
|
Loading…
Reference in New Issue