diff --git a/build/rollup.config.js b/build/rollup.config.js index 4f1db7e8c7..74a5fe46f3 100644 --- a/build/rollup.config.js +++ b/build/rollup.config.js @@ -73,7 +73,8 @@ module.exports = [ 'isFunction', 'cloneDeep', 'isString', - 'isNumber' + 'isNumber', + 'merge' ] } }), diff --git a/examples/gallery/basic/demo/column_dark.js b/examples/gallery/basic/demo/column_dark.js index b2c3ea49fe..d012a0f878 100644 --- a/examples/gallery/basic/demo/column_dark.js +++ b/examples/gallery/basic/demo/column_dark.js @@ -26,6 +26,7 @@ fetch('https://gw.alipayobjects.com/os/rmsportal/oVTMqfzuuRFKiDwhPSFL.json') .size('t', function(level) { return [ 1, 2, level * 2 + 20 ]; }) + .active(true) .color('t', [ '#094D4A', '#146968', diff --git a/packages/core/src/services/config/ConfigService.ts b/packages/core/src/services/config/ConfigService.ts index 55c3c43a62..b0660c99a9 100644 --- a/packages/core/src/services/config/ConfigService.ts +++ b/packages/core/src/services/config/ConfigService.ts @@ -1,5 +1,6 @@ import Ajv from 'ajv'; import { injectable, postConstruct } from 'inversify'; +import { merge } from 'lodash'; import { ILayerConfig } from '../layer/ILayerService'; import { IGlobalConfigService, ISceneConfig } from './IConfigService'; import mapConfigSchema from './mapConfigSchema'; @@ -63,6 +64,12 @@ const defaultLayerConfig: Partial = { enableTAA: false, jitterScale: 1, enableLighting: false, + animateOption: { + enable: false, + interval: 0.2, + duration: 4, + trailLength: 0.15, + }, }; // @see https://github.com/epoberezkin/ajv#options @@ -141,9 +148,7 @@ export default class GlobalConfigService implements IGlobalConfigService { ) { // @ts-ignore this.layerConfigCache[layerId] = { - ...this.sceneConfigCache[sceneId], - ...defaultLayerConfig, - ...config, + ...merge({}, this.sceneConfigCache[sceneId], defaultLayerConfig, config), }; } diff --git a/packages/core/src/services/coordinate/CoordinateSystemService.ts b/packages/core/src/services/coordinate/CoordinateSystemService.ts index 227aa8c7ad..8bf04809dc 100644 --- a/packages/core/src/services/coordinate/CoordinateSystemService.ts +++ b/packages/core/src/services/coordinate/CoordinateSystemService.ts @@ -13,6 +13,7 @@ const VECTOR_TO_POINT_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]; @injectable() export default class CoordinateSystemService implements ICoordinateSystemService { + public needRefresh: boolean = true; @inject(TYPES.ICameraService) private readonly cameraService: ICameraService; @@ -59,6 +60,9 @@ export default class CoordinateSystemService * TODO: 使用 memoize 缓存参数以及计算结果 */ public refresh(): void { + // if (!this.needRefresh) { + // return; + // } const zoom = this.cameraService.getZoom(); const zoomScale = this.cameraService.getZoomScale(); const center = this.cameraService.getCenter(); @@ -86,6 +90,7 @@ export default class CoordinateSystemService } else if (this.coordinateSystem === CoordinateSystem.P20_OFFSET) { this.calculateLnglatOffset(center, zoom, zoomScale, true); } + this.needRefresh = false; // TODO: 判断是否应用瓦片 & 常规坐标系 } diff --git a/packages/core/src/services/coordinate/ICoordinateSystemService.ts b/packages/core/src/services/coordinate/ICoordinateSystemService.ts index 96b763777b..8bbc211970 100644 --- a/packages/core/src/services/coordinate/ICoordinateSystemService.ts +++ b/packages/core/src/services/coordinate/ICoordinateSystemService.ts @@ -30,6 +30,7 @@ export const CoordinateUniform = { }; export interface ICoordinateSystemService { + needRefresh: boolean; refresh(): void; getCoordinateSystem(): CoordinateSystem; setCoordinateSystem(coordinateSystem: CoordinateSystem): void; diff --git a/packages/core/src/services/interaction/InteractionService.ts b/packages/core/src/services/interaction/InteractionService.ts index 82be0ee533..f96ac5a262 100644 --- a/packages/core/src/services/interaction/InteractionService.ts +++ b/packages/core/src/services/interaction/InteractionService.ts @@ -68,7 +68,6 @@ export default class InteractionService extends EventEmitter this.logger.debug('add event listeners on canvas'); } } - private removeEventListenerOnMap() { const $containter = this.mapService.getMapContainer(); if ($containter) { diff --git a/packages/core/src/services/layer/ILayerService.ts b/packages/core/src/services/layer/ILayerService.ts index e0e6eaf9f7..71fb3f9dc7 100644 --- a/packages/core/src/services/layer/ILayerService.ts +++ b/packages/core/src/services/layer/ILayerService.ts @@ -53,6 +53,7 @@ export interface ILayerModel { render(): void; getUninforms(): IModelUniform; getDefaultStyle(): unknown; + getAnimateUniforms(): IModelUniform; buildModels(): IModel[]; } export interface IModelUniform { @@ -78,6 +79,7 @@ export interface ILayer { zIndex: number; plugins: ILayerPlugin[]; layerModelNeedUpdate: boolean; + layerModel: ILayerModel; dataState: IDataState; // 数据流状态 pickedFeatureID: number; hooks: { @@ -116,7 +118,7 @@ export interface ILayer { color(field: StyleAttrField, value?: StyleAttributeOption): ILayer; shape(field: StyleAttrField, value?: StyleAttributeOption): ILayer; label(field: StyleAttrField, value?: StyleAttributeOption): ILayer; - animate(option: IAnimateOption): ILayer; + animate(option: Partial | boolean): ILayer; // pattern(field: string, value: StyleAttributeOption): ILayer; filter(field: string, value: StyleAttributeOption): ILayer; active(option: IActiveOption | boolean): ILayer; @@ -172,6 +174,8 @@ export interface ILayer { pick(query: { x: number; y: number }): void; updateLayerConfig(configToUpdate: Partial): void; + setAnimateStartTime(): void; + getLayerAnimateTime(): number; } /** @@ -242,6 +246,7 @@ export interface ILayerConfig { * 开启光照 */ enableLighting: boolean; + animateOption: Partial; onHover(pickedFeature: IPickedFeature): void; onClick(pickedFeature: IPickedFeature): void; } diff --git a/packages/core/src/services/layer/IStyleAttributeService.ts b/packages/core/src/services/layer/IStyleAttributeService.ts index 0ca277cd61..2f00b415dc 100644 --- a/packages/core/src/services/layer/IStyleAttributeService.ts +++ b/packages/core/src/services/layer/IStyleAttributeService.ts @@ -75,6 +75,7 @@ export interface IAnimateOption { interval?: number; duration?: number; trailLength?: number; + repeat?: number; } export interface IEncodeFeature { @@ -118,7 +119,7 @@ export interface IStyleAttributeInitializationOptions { type: AttributeType; scale?: { field: StyleAttributeField; - values: unknown[]; + values: unknown[] | string; names: string[]; type: StyleScaleType; callback?: (...args: any[]) => []; diff --git a/packages/core/src/services/layer/LayerService.ts b/packages/core/src/services/layer/LayerService.ts index fa9125e758..02d15bb6eb 100644 --- a/packages/core/src/services/layer/LayerService.ts +++ b/packages/core/src/services/layer/LayerService.ts @@ -81,6 +81,7 @@ export default class LayerService implements ILayerService { public startAnimate() { if (this.animateInstanceCount++ === 0) { + this.clock.start(); this.runRender(); } } @@ -88,6 +89,7 @@ export default class LayerService implements ILayerService { public stopAnimate() { if (--this.animateInstanceCount === 0) { this.stopRender(); + this.clock.stop(); } } @@ -102,7 +104,7 @@ export default class LayerService implements ILayerService { private runRender() { this.renderLayers(); - this.layerRenderID = requestAnimationFrame(this.renderLayers.bind(this)); + this.layerRenderID = requestAnimationFrame(this.runRender.bind(this)); } private stopRender() { diff --git a/packages/core/src/services/scene/SceneService.ts b/packages/core/src/services/scene/SceneService.ts index acdbb76b78..1803cf8e4a 100644 --- a/packages/core/src/services/scene/SceneService.ts +++ b/packages/core/src/services/scene/SceneService.ts @@ -11,6 +11,7 @@ import { ICameraService, IViewport } from '../camera/ICameraService'; import { IControlService } from '../component/IControlService'; import { IMarkerService } from '../component/IMarkerService'; import { IGlobalConfigService, ISceneConfig } from '../config/IConfigService'; +import { ICoordinateSystemService } from '../coordinate/ICoordinateSystemService'; import { IInteractionService } from '../interaction/IInteractionService'; import { ILayer, ILayerService } from '../layer/ILayerService'; import { ILogService } from '../log/ILogService'; @@ -47,6 +48,9 @@ export default class Scene extends EventEmitter implements ISceneService { @inject(TYPES.IMapService) private readonly map: IMapService; + @inject(TYPES.ICoordinateSystemService) + private readonly coordinateSystemService: ICoordinateSystemService; + @inject(TYPES.IRendererService) private readonly rendererService: IRendererService; @@ -137,6 +141,8 @@ export default class Scene extends EventEmitter implements ISceneService { this.map.addMarkerContainer(); // 初始化未加载的marker; this.markerService.addMarkers(); + // 地图初始化之后 才能初始化 container 上的交互 + this.interactionService.init(); this.logger.debug('map loaded'); }); @@ -160,8 +166,6 @@ export default class Scene extends EventEmitter implements ISceneService { this.logger.error('容器 id 不存在'); } - // 初始化 container 上的交互 - this.interactionService.init(); this.logger.debug(`scene ${this.id} renderer loaded`); }); // TODO:init worker, fontAtlas... @@ -249,6 +253,7 @@ export default class Scene extends EventEmitter implements ISceneService { }); // 触发 Map, canvas DOM.triggerResize(); + this.coordinateSystemService.needRefresh = true; // repaint layers this.render(); } diff --git a/packages/core/src/shaders/project.glsl b/packages/core/src/shaders/project.glsl index df6ee0bc43..4da968f5f8 100644 --- a/packages/core/src/shaders/project.glsl +++ b/packages/core/src/shaders/project.glsl @@ -8,7 +8,7 @@ vec2 ProjectFlat(vec2 lnglat){ float x=lnglat.x*d; float y=lat*d; y=log(tan((PI/4.)+(y/2.))); - + float a=.5/PI, b=.5, c=-.5/PI; @@ -32,3 +32,9 @@ vec2 unProjectFlat(vec2 px){ float lng=x/d; return vec2(lng,lat); } + +float pixelDistance(vec2 from, vec2 to) { + vec2 a1 = ProjectFlat(from); + vec2 b1 = ProjectFlat(to); + return distance(a1, b1); +} diff --git a/packages/core/src/utils/vertex-compression.ts b/packages/core/src/utils/vertex-compression.ts index 1930d4531b..6f319b619b 100644 --- a/packages/core/src/utils/vertex-compression.ts +++ b/packages/core/src/utils/vertex-compression.ts @@ -14,7 +14,7 @@ export interface ICircleVertex { color: number[]; radius: number; opacity: number; - strokeColor: number[]; + stroke: number[]; strokeWidth: number; strokeOpacity: number; } @@ -68,7 +68,7 @@ export function packCircleVertex( tileY, shape, opacity, // packed buffer1 - strokeColor, + stroke, strokeWidth, strokeOpacity, // packed buffer2 } = props; @@ -81,8 +81,8 @@ export function packCircleVertex( packUint8ToFloat(color[2], color[3]), ]; const packedStrokeColor: [number, number] = [ - packUint8ToFloat(strokeColor[0], strokeColor[1]), - packUint8ToFloat(strokeColor[2], strokeColor[3]), + packUint8ToFloat(stroke[0], stroke[1]), + packUint8ToFloat(stroke[2], stroke[3]), ]; [ diff --git a/packages/layers/src/citybuliding/building.ts b/packages/layers/src/citybuliding/building.ts new file mode 100644 index 0000000000..ce415f1f96 --- /dev/null +++ b/packages/layers/src/citybuliding/building.ts @@ -0,0 +1,37 @@ +import { IEncodeFeature } from '@antv/l7-core'; +import BaseLayer from '../core/BaseLayer'; +import CityBuildModel from './models/build'; + +export default class CityBuildingLayer extends BaseLayer { + public type: string = 'PolygonLayer'; + + protected getConfigSchema() { + return { + properties: { + opacity: { + type: 'number', + minimum: 0, + maximum: 1, + }, + }, + }; + } + + protected renderModels() { + this.models.forEach((model) => + model.draw({ + uniforms: this.layerModel.getUninforms(), + }), + ); + return this; + } + + protected buildModels() { + this.layerModel = new CityBuildModel(this); + this.models = this.layerModel.buildModels(); + } + + protected getModelType(): string { + return 'citybuilding'; + } +} diff --git a/packages/layers/src/citybuliding/models/build.ts b/packages/layers/src/citybuliding/models/build.ts new file mode 100644 index 0000000000..6be8e04152 --- /dev/null +++ b/packages/layers/src/citybuliding/models/build.ts @@ -0,0 +1,115 @@ +import { AttributeType, gl, IEncodeFeature, IModel } from '@antv/l7-core'; +import { rgb2arr } from '@antv/l7-utils'; +import BaseModel from '../../core/BaseModel'; +import { PolygonExtrudeTriangulation } from '../../core/triangulation'; +import buildFrag from '../shaders/build_frag.glsl'; +import buildVert from '../shaders/build_vert.glsl'; +interface ICityBuildLayerStyleOptions { + opacity: number; + baseColor: string; + brightColor: string; + windowColor: string; +} +export default class CityBuildModel extends BaseModel { + public getUninforms() { + const { + opacity = 1, + baseColor = 'rgb(16,16,16)', + brightColor = 'rgb(255,176,38)', + windowColor = 'rgb(30,60,89)', + } = this.layer.getLayerConfig() as ICityBuildLayerStyleOptions; + return { + u_opacity: opacity, + u_baseColor: rgb2arr(baseColor), + u_brightColor: rgb2arr(brightColor), + u_windowColor: rgb2arr(windowColor), + u_time: this.layer.getLayerAnimateTime(), + }; + } + + public buildModels(): IModel[] { + this.startModelAnimate(); + return [ + this.layer.buildLayerModel({ + moduleName: 'cityBuilding', + vertexShader: buildVert, + fragmentShader: buildFrag, + triangulation: PolygonExtrudeTriangulation, + }), + ]; + } + + protected registerBuiltinAttributes() { + // point layer size; + this.styleAttributeService.registerStyleAttribute({ + name: 'normal', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Normal', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.STATIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 3, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + normal: number[], + ) => { + return normal; + }, + }, + }); + + this.styleAttributeService.registerStyleAttribute({ + name: 'size', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Size', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 1, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + const { size } = feature; + return Array.isArray(size) ? [size[0]] : [size as number]; + }, + }, + }); + this.styleAttributeService.registerStyleAttribute({ + name: 'uv', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Uv', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 2, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + const { size } = feature; + return [vertex[3], vertex[4]]; + }, + }, + }); + } +} diff --git a/packages/layers/src/citybuliding/shaders/build_frag.glsl b/packages/layers/src/citybuliding/shaders/build_frag.glsl new file mode 100644 index 0000000000..0bfd15324d --- /dev/null +++ b/packages/layers/src/citybuliding/shaders/build_frag.glsl @@ -0,0 +1,104 @@ +uniform float u_opacity: 1.0; +uniform vec4 u_baseColor : [ 1.0, 0, 0, 1.0 ]; +uniform vec4 u_brightColor : [ 1.0, 0, 0, 1.0 ]; +uniform vec4 u_windowColor : [ 1.0, 0, 0, 1.0 ]; +uniform float u_near : 0; +uniform float u_far : 1; +varying vec4 v_Color; +varying vec2 v_texCoord; +uniform float u_Zoom : 1; +uniform float u_time; + +#pragma include "picking" + +vec3 getWindowColor(float n, float hot, vec3 brightColor, vec3 darkColor) { + float s = step(hot, n); + vec3 color = mix(brightColor,vec3(0.9,0.9,1.0),n); + + return mix(darkColor, color, s); +} +float random (vec2 st) { + return fract(sin(dot(st.xy, vec2(12.9898,78.233)))* 43758.5453123); +} + +float LinearizeDepth() +{ + float z = gl_FragCoord.z * 2.0 - 1.0; + return (2.0 * u_near * u_far) / (u_far + u_near - z * (u_far - u_near)); +} + +vec3 fog(vec3 color, vec3 fogColor, float depth){ + float fogFactor=clamp(depth,0.0,1.0); + vec3 output_color=mix(fogColor,color,fogFactor); + return output_color; +} + +float sdRect(vec2 p, vec2 sz) { + vec2 d = abs(p) - sz; + float outside = length(max(d, 0.)); + float inside = min(max(d.x, d.y), 0.); + return outside + inside; +} + +void main() { + gl_FragColor = v_Color; + gl_FragColor.a *= u_opacity; + + vec3 baseColor = u_baseColor.xyz; + vec3 brightColor = u_brightColor.xyz; + vec3 windowColor = u_windowColor.xyz; + float targetColId = 5.; + float depth = 1.0 - LinearizeDepth() / u_far * u_Zoom; + vec3 fogColor = vec3(23.0/255.0,31.0/255.0,51.0/255.0); + if(v_texCoord.x < 0.) { //顶部颜色 + vec3 foggedColor = fog(baseColor.xyz + vec3(0.12*0.9,0.2*0.9,0.3*0.9),fogColor,depth); + gl_FragColor = vec4( foggedColor, v_Color.w); + }else { // 侧面颜色 + vec2 st = v_texCoord; + vec2 UvScale = v_texCoord; + float tStep = min(0.08,max(0.05* (18.0-u_Zoom),0.02)); + float tStart = 0.25 * tStep; + float tEnd = 0.75 * tStep; + float u = mod(UvScale.x, tStep); + float v = mod(UvScale.y, tStep); + float ux = floor(UvScale.x/tStep); + float uy = floor(UvScale.y/tStep); + float n = random(vec2(ux,uy)); + float lightP = u_time; + float head = 1.0- step(0.005,st.y); + /*step3*/ + // 将窗户颜色和墙面颜色区别开来 + float sU = step(tStart, u) - step(tEnd, u); + float sV = step(tStart, v) - step(tEnd, v); + vec2 windowSize = vec2(abs(tEnd-tStart),abs(tEnd-tStart)); + float dist = sdRect(vec2(u,v), windowSize); + float s = sU * sV; + + float curColId = floor(UvScale.x / tStep); + float sCol = step(targetColId - 0.2, curColId) - step(targetColId + 0.2, curColId); + + float mLightP = mod(lightP, 2.); + float sRow = step(mLightP - 0.2, st.y) - step(mLightP, st.y); + if(ux == targetColId){ + n =0.; + } + float timeP = min(0.75, abs ( sin(u_time/6.0) ) ); + float hot = smoothstep(1.0,0.0,timeP); + vec3 color = mix(baseColor, getWindowColor(n,hot,brightColor,windowColor), s); + //vec3 color = mix(baseColor, getWindowColor(n,hot,brightColor,windowColor), 1.0); + float sFinal = s * sCol * sRow; + color += mix(baseColor, brightColor, sFinal*n); + if (st.y<0.01){ + color = baseColor; + } + if(head ==1.0) { // 顶部亮线 + color = brightColor; + } + color = color * v_Color.rgb; + + vec3 foggedColor = fog(color,fogColor,depth); + + gl_FragColor = vec4(foggedColor,1.0); + } + gl_FragColor = filterColor(gl_FragColor); +} diff --git a/packages/layers/src/citybuliding/shaders/build_vert.glsl b/packages/layers/src/citybuliding/shaders/build_vert.glsl new file mode 100644 index 0000000000..70f30e045d --- /dev/null +++ b/packages/layers/src/citybuliding/shaders/build_vert.glsl @@ -0,0 +1,33 @@ +precision highp float; + +#define ambientRatio 0.5 +#define diffuseRatio 0.3 +#define specularRatio 0.2 + +attribute vec4 a_Color; +attribute vec3 a_Position; +attribute vec3 a_Normal; +attribute float a_Size; +uniform mat4 u_ModelMatrix; + +attribute vec2 a_Uv; +varying vec2 v_texCoord; + +varying vec4 v_Color; + +#pragma include "projection" +#pragma include "light" +#pragma include "picking" + +void main() { + vec4 pos = vec4(a_Position.xy, a_Position.z * a_Size, 1.0); + vec4 project_pos = project_position(pos); + v_texCoord = a_Uv; + gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0)); + + float lightWeight = calc_lighting(pos); + // v_Color = a_Color; + v_Color = vec4(a_Color.rgb * lightWeight, a_Color.w); + + setPickingColor(a_PickingColor); +} diff --git a/packages/layers/src/core/BaseLayer.ts b/packages/layers/src/core/BaseLayer.ts index 4a8f213200..cec96c3d14 100644 --- a/packages/layers/src/core/BaseLayer.ts +++ b/packages/layers/src/core/BaseLayer.ts @@ -55,7 +55,7 @@ let layerIdCounter = 0; export default class BaseLayer extends EventEmitter implements ILayer { public id: string = `${layerIdCounter++}`; - public name: string; + public name: string = `${layerIdCounter++}`; public type: string; public visible: boolean = true; public zIndex: number = 0; @@ -103,6 +103,8 @@ export default class BaseLayer extends EventEmitter options?: ISourceCFG; }; + public layerModel: ILayerModel; + @lazyInject(TYPES.ILogService) protected readonly logger: ILogService; @@ -133,8 +135,6 @@ export default class BaseLayer extends EventEmitter ) => IPostProcessingPass; protected normalPassFactory: (name: string) => IPass; - protected layerModel: ILayerModel; - protected animateOptions: IAnimateOption = { enable: false }; /** @@ -165,6 +165,8 @@ export default class BaseLayer extends EventEmitter private scaleOptions: IScaleOptions = {}; + private AnimateStartTime: number; + constructor(config: Partial = {}) { super(); this.name = config.name || this.id; @@ -298,11 +300,16 @@ export default class BaseLayer extends EventEmitter this.inited = true; this.hooks.afterInit.call(); - // 更新 module 样式 + // 更新 model 样式 this.updateLayerConfig({ - ...this.rawConfig, ...(this.getDefaultConfig() as object), + ...this.rawConfig, }); + // 启动动画 + const { animateOption } = this.getLayerConfig(); + if (animateOption?.enable) { + this.layerService.startAnimate(); + } this.buildModels(); // 触发初始化完成事件; this.emit('inited'); @@ -381,8 +388,21 @@ export default class BaseLayer extends EventEmitter }); return this; } - public animate(options: IAnimateOption) { - this.animateOptions = options; + public animate(options: IAnimateOption | boolean) { + let rawAnimate: Partial = {}; + if (isObject(options)) { + rawAnimate.enable = true; + rawAnimate = { + ...rawAnimate, + ...options, + }; + } else { + rawAnimate.enable = options; + } + this.updateLayerConfig({ + animateOption: rawAnimate, + }); + // this.animateOptions = options; return this; } @@ -706,6 +726,16 @@ export default class BaseLayer extends EventEmitter }); } + public getTime() { + return this.layerService.clock.getDelta(); + } + public setAnimateStartTime() { + this.AnimateStartTime = this.layerService.clock.getElapsedTime(); + } + public getLayerAnimateTime(): number { + return this.layerService.clock.getElapsedTime() - this.AnimateStartTime; + } + protected getConfigSchema() { throw new Error('Method not implemented.'); } @@ -735,8 +765,4 @@ export default class BaseLayer extends EventEmitter callback: isFunction(valuesOrCallback) ? valuesOrCallback : undefined, }; } - - private layerMapHander(type: string, data: any) { - this.emit(type, data); - } } diff --git a/packages/layers/src/core/BaseModel.ts b/packages/layers/src/core/BaseModel.ts index e90c8f0d7e..732354af42 100644 --- a/packages/layers/src/core/BaseModel.ts +++ b/packages/layers/src/core/BaseModel.ts @@ -1,5 +1,6 @@ import { BlendType, + IAnimateOption, IAttribute, IBlendOptions, ICameraService, @@ -56,7 +57,10 @@ export default class BaseModel this.cameraService = layer .getContainer() .get(TYPES.ICameraService); + // 注册 Attribute this.registerBuiltinAttributes(); + // 开启动画 + this.startModelAnimate(); } public getBlend(): IBlendOptions { const { blend = 'normal' } = this.layer.getLayerConfig(); @@ -69,6 +73,10 @@ export default class BaseModel throw new Error('Method not implemented.'); } + public getAnimateUniforms(): IModelUniform { + return {}; + } + public buildModels(): IModel[] { throw new Error('Method not implemented.'); } @@ -86,4 +94,18 @@ export default class BaseModel protected registerBuiltinAttributes() { throw new Error('Method not implemented.'); } + protected animateOption2Array(option: IAnimateOption): number[] { + return [ + option.enable ? 0 : 1.0, + option.duration || 4.0, + option.interval || 0.2, + option.trailLength || 0.1, + ]; + } + protected startModelAnimate() { + const { animateOption } = this.layer.getLayerConfig() as ILayerConfig; + if (animateOption.enable) { + this.layer.setAnimateStartTime(); + } + } } diff --git a/packages/layers/src/core/interface.ts b/packages/layers/src/core/interface.ts new file mode 100644 index 0000000000..ef6d574a62 --- /dev/null +++ b/packages/layers/src/core/interface.ts @@ -0,0 +1,11 @@ +export enum lineStyleType { + 'solid' = 0.0, + 'dash' = 1.0, +} + +export interface ILineLayerStyleOptions { + opacity: number; + lineType?: keyof typeof lineStyleType; + dashArray?: [number, number]; + segmentNumber: number; +} diff --git a/packages/layers/src/core/shape/extrude.ts b/packages/layers/src/core/shape/extrude.ts index 6de03661a5..841bf408b2 100644 --- a/packages/layers/src/core/shape/extrude.ts +++ b/packages/layers/src/core/shape/extrude.ts @@ -87,7 +87,7 @@ export function fillPolygon(points: IPath[]) { export function extrude_PolygonNormal( path: IPath[], - needFlat = false, + needFlat = false, // 是否需要转成平面坐标 ): IExtrudeGeomety { const p1 = path[0][0]; const p2 = path[0][path[0].length - 1]; @@ -100,12 +100,12 @@ export function extrude_PolygonNormal( const positions = []; const indexArray = []; const normals = []; - // 设置顶部z值 + // 设置顶部z值 position uv for (let j = 0; j < vertices.length / dimensions; j++) { if (dimensions === 2) { - positions.push(vertices[j * 2], vertices[j * 2 + 1], 1); + positions.push(vertices[j * 2], vertices[j * 2 + 1], 1, -1, -1); } else { - positions.push(vertices[j * 3], vertices[j * 3 + 1], 1); + positions.push(vertices[j * 3], vertices[j * 3 + 1], 1, -1, -1); } normals.push(0, 0, 1); } @@ -127,20 +127,28 @@ export function extrude_PolygonNormal( if (nextPoint.length === 0) { nextPoint = flattengeo.vertices.slice(0, dimensions); } - const indexOffset = positions.length / 3; + const indexOffset = positions.length / 5; positions.push( prePoint[0], prePoint[1], 1, + 0, + 0, nextPoint[0], nextPoint[1], 1, + 0.1, + 0, prePoint[0], prePoint[1], 0, + 0, + 0.8, nextPoint[0], nextPoint[1], 0, + 0.1, + 0.8, ); const normal = computeVertexNormals( [nextPoint[0], nextPoint[1], 1], diff --git a/packages/layers/src/core/triangulation.ts b/packages/layers/src/core/triangulation.ts index c554840310..a059a2c75d 100644 --- a/packages/layers/src/core/triangulation.ts +++ b/packages/layers/src/core/triangulation.ts @@ -47,7 +47,7 @@ export function PointExtrudeTriangulation(feature: IEncodeFeature) { indices: index, normals, // normals: Array.from(computeVertexNormals(positions, index, 3, false)), - size: 3, + size: 5, }; } @@ -103,10 +103,10 @@ export function PolygonExtrudeTriangulation(feature: IEncodeFeature) { ); return { - vertices: positions, // [ x, y, z ] + vertices: positions, // [ x, y, z, uv.x,uv.y ] indices: index, normals, - size: 3, + size: 5, }; } diff --git a/packages/layers/src/index.ts b/packages/layers/src/index.ts index 408d77b348..3f95eaa2e8 100644 --- a/packages/layers/src/index.ts +++ b/packages/layers/src/index.ts @@ -1,8 +1,8 @@ import { container, ILayerPlugin, TYPES } from '@antv/l7-core'; +import CityBuildingLayer from './citybuliding/building'; import BaseLayer from './core/BaseLayer'; import './glsl.d'; import HeatmapLayer from './heatmap'; -import DashLineLayer from './line/dash'; import LineLayer from './line/index'; import PointLayer from './point'; import PolygonLayer from './polygon'; @@ -13,6 +13,7 @@ import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin import DataMappingPlugin from './plugins/DataMappingPlugin'; import DataSourcePlugin from './plugins/DataSourcePlugin'; import FeatureScalePlugin from './plugins/FeatureScalePlugin'; +import LayerAnimateStylePlugin from './plugins/LayerAnimateStylePlugin'; import LayerStylePlugin from './plugins/LayerStylePlugin'; import LightingPlugin from './plugins/LightingPlugin'; import MultiPassRendererPlugin from './plugins/MultiPassRendererPlugin'; @@ -87,6 +88,14 @@ container .bind(TYPES.ILayerPlugin) .to(ShaderUniformPlugin) .inRequestScope(); + +/** + * 传入动画参数 + */ +container + .bind(TYPES.ILayerPlugin) + .to(LayerAnimateStylePlugin) + .inRequestScope(); /** * 传入光照相关参数 */ @@ -107,7 +116,7 @@ export { PointLayer, PolygonLayer, LineLayer, - DashLineLayer, + CityBuildingLayer, ImageLayer, RasterLayer, HeatmapLayer, diff --git a/packages/layers/src/line/dash.ts b/packages/layers/src/line/dash.ts deleted file mode 100644 index 7eca2e28f0..0000000000 --- a/packages/layers/src/line/dash.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { AttributeType, gl, IEncodeFeature } from '@antv/l7-core'; -import BaseLayer from '../core/BaseLayer'; -import { LineTriangulation } from '../core/triangulation'; -import line_dash_frag from './shaders/line_dash_frag.glsl'; -import line_dash_vert from './shaders/line_dash_vert.glsl'; -interface IDashLineLayerStyleOptions { - opacity: number; - dashArray: [number, number]; - lineType: string; -} -export default class DashLineLayer extends BaseLayer< - IDashLineLayerStyleOptions -> { - public type: string = 'LineLayer'; - - protected getConfigSchema() { - return { - properties: { - opacity: { - type: 'number', - minimum: 0, - maximum: 1, - }, - }, - }; - } - - protected renderModels() { - const { - opacity, - dashArray = [10, 5], - lineType = 'dash', - } = this.getLayerConfig(); - this.models.forEach((model) => - model.draw({ - uniforms: { - u_opacity: opacity || 1.0, - u_dash_array: dashArray, - }, - }), - ); - return this; - } - - protected buildModels() { - this.registerBuiltinAttributes(); - this.models = [ - this.buildLayerModel({ - moduleName: 'line_dash', - vertexShader: line_dash_vert, - fragmentShader: line_dash_frag, - triangulation: LineTriangulation, - blend: { - enable: true, - func: { - srcRGB: gl.SRC_ALPHA, - srcAlpha: 1, - dstRGB: gl.ONE_MINUS_SRC_ALPHA, - dstAlpha: 1, - }, - }, - }), - ]; - } - - private registerBuiltinAttributes() { - // point layer size; - this.styleAttributeService.registerStyleAttribute({ - name: 'size', - type: AttributeType.Attribute, - descriptor: { - name: 'a_Size', - buffer: { - // give the WebGL driver a hint that this buffer may change - usage: gl.DYNAMIC_DRAW, - data: [], - type: gl.FLOAT, - }, - size: 1, - update: ( - feature: IEncodeFeature, - featureIdx: number, - vertex: number[], - attributeIdx: number, - ) => { - const { size } = feature; - return Array.isArray(size) ? [size[0]] : [size as number]; - }, - }, - }); - - // point layer size; - this.styleAttributeService.registerStyleAttribute({ - name: 'normal', - type: AttributeType.Attribute, - descriptor: { - name: 'a_Normal', - buffer: { - // give the WebGL driver a hint that this buffer may change - usage: gl.STATIC_DRAW, - data: [], - type: gl.FLOAT, - }, - size: 3, - update: ( - feature: IEncodeFeature, - featureIdx: number, - vertex: number[], - attributeIdx: number, - normal: number[], - ) => { - return normal; - }, - }, - }); - - this.styleAttributeService.registerStyleAttribute({ - name: 'miter', - type: AttributeType.Attribute, - descriptor: { - name: 'a_Miter', - buffer: { - // give the WebGL driver a hint that this buffer may change - usage: gl.DYNAMIC_DRAW, - data: [], - type: gl.FLOAT, - }, - size: 1, - update: ( - feature: IEncodeFeature, - featureIdx: number, - vertex: number[], - attributeIdx: number, - ) => { - return [vertex[4]]; - }, - }, - }); - - // this.styleAttributeService.registerStyleAttribute({ - // name: 'startPos', - // type: AttributeType.Attribute, - // descriptor: { - // name: 'a_StartPos', - // buffer: { - // // give the WebGL driver a hint that this buffer may change - // usage: gl.DYNAMIC_DRAW, - // data: [], - // type: gl.FLOAT, - // }, - // size: 3, - // update: ( - // feature: IEncodeFeature, - // featureIdx: number, - // vertex: number[], - // attributeIdx: number, - // ) => { - // const coordinates = feature.coordinates as number[][]; - // const coord = coordinates[0]; - // return coord.length === 3 ? coord : [...coord, 0.0]; - // }, - // }, - // }); - - this.styleAttributeService.registerStyleAttribute({ - name: 'distance', - type: AttributeType.Attribute, - descriptor: { - name: 'a_Distance', - buffer: { - // give the WebGL driver a hint that this buffer may change - usage: gl.DYNAMIC_DRAW, - data: [], - type: gl.FLOAT, - }, - size: 1, - update: ( - feature: IEncodeFeature, - featureIdx: number, - vertex: number[], - attributeIdx: number, - ) => { - return [vertex[3]]; - }, - }, - }); - - this.styleAttributeService.registerStyleAttribute({ - name: 'total_distance', - type: AttributeType.Attribute, - descriptor: { - name: 'a_Total_Distance', - buffer: { - // give the WebGL driver a hint that this buffer may change - usage: gl.DYNAMIC_DRAW, - data: [], - type: gl.FLOAT, - }, - size: 1, - update: ( - feature: IEncodeFeature, - featureIdx: number, - vertex: number[], - attributeIdx: number, - ) => { - return [vertex[5]]; - }, - }, - }); - } -} diff --git a/packages/layers/src/line/index.ts b/packages/layers/src/line/index.ts index 2daa0fbce6..dbad4d7ac7 100644 --- a/packages/layers/src/line/index.ts +++ b/packages/layers/src/line/index.ts @@ -1,14 +1,7 @@ import BaseLayer from '../core/BaseLayer'; +import { ILineLayerStyleOptions } from '../core/interface'; import LineModels, { LineModelType } from './models'; -export enum LineType { - 'solid' = 'solid', - 'dash' = 'dash', -} -interface ILineLayerStyleOptions { - opacity: number; - lineType?: keyof typeof LineType; - dashArray?: [number, number]; -} + export default class LineLayer extends BaseLayer { public type: string = 'LineLayer'; @@ -25,7 +18,16 @@ export default class LineLayer extends BaseLayer { }, }; } - + protected getDefaultConfig() { + const type = this.getModelType(); + const defaultConfig = { + line: {}, + arc3d: { blend: 'additive' }, + arc: { blend: 'additive' }, + greatcircle: { blend: 'additive' }, + }; + return defaultConfig[type]; + } protected renderModels() { this.models.forEach((model) => model.draw({ diff --git a/packages/layers/src/line/models/arc.ts b/packages/layers/src/line/models/arc.ts index 3283ddb680..9963a24a36 100644 --- a/packages/layers/src/line/models/arc.ts +++ b/packages/layers/src/line/models/arc.ts @@ -1,31 +1,42 @@ import { AttributeType, gl, + IAnimateOption, IEncodeFeature, + ILayerConfig, IModel, IModelUniform, } from '@antv/l7-core'; import BaseModel from '../../core/BaseModel'; +import { ILineLayerStyleOptions, lineStyleType } from '../../core/interface'; import { LineArcTriangulation } from '../../core/triangulation'; import line_arc_frag from '../shaders/line_arc_frag.glsl'; import line_arc2d_vert from '../shaders/line_bezier_vert.glsl'; - -interface IArcLayerStyleOptions { - opacity: number; - segmentNumber: number; - blur: number; -} +const lineStyleObj: { [key: string]: number } = { + solid: 0.0, + dash: 1.0, +}; export default class ArcModel extends BaseModel { public getUninforms(): IModelUniform { const { opacity, - blur = 0.99, - } = this.layer.getLayerConfig() as IArcLayerStyleOptions; + lineType = 'solid', + dashArray = [10, 5], + } = this.layer.getLayerConfig() as ILineLayerStyleOptions; return { u_opacity: opacity || 1, segmentNumber: 30, - u_blur: blur, + u_line_type: lineStyleObj[lineType || 'solid'], + u_dash_array: dashArray, + }; + } + + public getAnimateUniforms(): IModelUniform { + const { animateOption } = this.layer.getLayerConfig() as ILayerConfig; + return { + u_aimate: this.animateOption2Array(animateOption as IAnimateOption), + u_time: this.layer.getLayerAnimateTime(), }; } @@ -37,15 +48,7 @@ export default class ArcModel extends BaseModel { fragmentShader: line_arc_frag, triangulation: LineArcTriangulation, depth: { enable: false }, - blend: { - enable: true, - func: { - srcRGB: gl.ONE, - srcAlpha: 1, - dstRGB: gl.ONE, - dstAlpha: 1, - }, - }, + blend: this.getBlend(), }), ]; } diff --git a/packages/layers/src/line/models/arc_3d.ts b/packages/layers/src/line/models/arc_3d.ts index 70f14b8b97..4110ae6c7b 100644 --- a/packages/layers/src/line/models/arc_3d.ts +++ b/packages/layers/src/line/models/arc_3d.ts @@ -1,44 +1,51 @@ import { AttributeType, gl, + IAnimateOption, IEncodeFeature, + ILayerConfig, IModel, IModelUniform, } from '@antv/l7-core'; import BaseModel from '../../core/BaseModel'; +import { ILineLayerStyleOptions, lineStyleType } from '../../core/interface'; import { LineArcTriangulation } from '../../core/triangulation'; import line_arc_frag from '../shaders/line_arc_frag.glsl'; import line_arc_vert from '../shaders/line_arc_vert.glsl'; - -interface IArcLayerStyleOptions { - opacity: number; - segmentNumber: number; -} +const lineStyleObj: { [key: string]: number } = { + solid: 0.0, + dash: 1.0, +}; export default class Arc3DModel extends BaseModel { public getUninforms(): IModelUniform { - const { opacity } = this.layer.getLayerConfig() as IArcLayerStyleOptions; + const { + opacity, + lineType = 'solid', + dashArray = [10, 5], + } = this.layer.getLayerConfig() as ILineLayerStyleOptions; return { u_opacity: opacity || 1, segmentNumber: 30, + u_line_type: lineStyleObj[lineType as string] || 0.0, + u_dash_array: dashArray, + }; + } + public getAnimateUniforms(): IModelUniform { + const { animateOption } = this.layer.getLayerConfig() as ILayerConfig; + return { + u_aimate: this.animateOption2Array(animateOption as IAnimateOption), + u_time: this.layer.getLayerAnimateTime(), }; } public buildModels(): IModel[] { return [ this.layer.buildLayerModel({ - moduleName: 'arcline', + moduleName: 'arc3Dline', vertexShader: line_arc_vert, fragmentShader: line_arc_frag, triangulation: LineArcTriangulation, - blend: { - enable: true, - func: { - srcRGB: gl.ONE, - srcAlpha: 1, - dstRGB: gl.ONE, - dstAlpha: 1, - }, - }, + blend: this.getBlend(), }), ]; } diff --git a/packages/layers/src/line/models/dash.ts b/packages/layers/src/line/models/dash.ts deleted file mode 100644 index ee37772a4f..0000000000 --- a/packages/layers/src/line/models/dash.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - AttributeType, - gl, - IEncodeFeature, - ILayer, - ILayerModel, - IModel, -} from '@antv/l7-core'; - -import BaseModel from '../../core/BaseModel'; -export default class ArcModel extends BaseModel { - public getUninforms() { - return {}; - } - - public buildModels(): IModel[] { - throw new Error('Method not implemented.'); - } - - protected registerBuiltinAttributes() { - // - } -} diff --git a/packages/layers/src/line/models/great_circle.ts b/packages/layers/src/line/models/great_circle.ts index d43326842c..16ce7d75af 100644 --- a/packages/layers/src/line/models/great_circle.ts +++ b/packages/layers/src/line/models/great_circle.ts @@ -1,31 +1,42 @@ import { AttributeType, gl, + IAnimateOption, IEncodeFeature, + ILayerConfig, IModel, IModelUniform, } from '@antv/l7-core'; import BaseModel from '../../core/BaseModel'; +import { ILineLayerStyleOptions, lineStyleType } from '../../core/interface'; import { LineArcTriangulation } from '../../core/triangulation'; import line_arc2d_vert from '../shaders/line_arc2d_vert.glsl'; import line_arc_frag from '../shaders/line_arc_frag.glsl'; +const lineStyleObj: { [key: string]: number } = { + solid: 0.0, + dash: 1.0, +}; -interface IArcLayerStyleOptions { - opacity: number; - segmentNumber: number; - blur: number; -} export default class GreatCircleModel extends BaseModel { public getUninforms(): IModelUniform { const { opacity, - blur = 0.99, - } = this.layer.getLayerConfig() as IArcLayerStyleOptions; + lineType = 'solid', + dashArray = [10, 5], + } = this.layer.getLayerConfig() as Partial; return { u_opacity: opacity || 1, segmentNumber: 30, - u_blur: blur, + u_line_type: lineStyleObj[lineType as string] || 0.0, + u_dash_array: dashArray, + }; + } + public getAnimateUniforms(): IModelUniform { + const { animateOption } = this.layer.getLayerConfig() as ILayerConfig; + return { + u_aimate: this.animateOption2Array(animateOption as IAnimateOption), + u_time: this.layer.getLayerAnimateTime(), }; } @@ -37,20 +48,11 @@ export default class GreatCircleModel extends BaseModel { fragmentShader: line_arc_frag, triangulation: LineArcTriangulation, depth: { enable: false }, - blend: { - enable: true, - func: { - srcRGB: gl.ONE, - srcAlpha: 1, - dstRGB: gl.ONE, - dstAlpha: 1, - }, - }, + blend: this.getBlend(), }), ]; } protected registerBuiltinAttributes() { - // point layer size; this.styleAttributeService.registerStyleAttribute({ name: 'size', type: AttributeType.Attribute, diff --git a/packages/layers/src/line/models/line.ts b/packages/layers/src/line/models/line.ts index 9cc28bfc00..b34654eb0e 100644 --- a/packages/layers/src/line/models/line.ts +++ b/packages/layers/src/line/models/line.ts @@ -1,24 +1,40 @@ import { AttributeType, gl, + IAnimateOption, IEncodeFeature, + ILayerConfig, IModel, IModelUniform, } from '@antv/l7-core'; import BaseModel from '../../core/BaseModel'; +import { ILineLayerStyleOptions, lineStyleType } from '../../core/interface'; import { LineTriangulation } from '../../core/triangulation'; import line_frag from '../shaders/line_frag.glsl'; import line_vert from '../shaders/line_vert.glsl'; - -interface ILineLayerStyleOptions { - opacity: number; -} +const lineStyleObj: { [key: string]: number } = { + solid: 0.0, + dash: 1.0, +}; export default class LineModel extends BaseModel { public getUninforms(): IModelUniform { - const { opacity } = this.layer.getLayerConfig() as ILineLayerStyleOptions; + const { + opacity, + lineType = 'solid', + dashArray = [10, 5], + } = this.layer.getLayerConfig() as ILineLayerStyleOptions; return { u_opacity: opacity || 1.0, + u_line_type: lineStyleObj[lineType], + u_dash_array: dashArray, + }; + } + public getAnimateUniforms(): IModelUniform { + const { animateOption } = this.layer.getLayerConfig() as ILayerConfig; + return { + u_aimate: this.animateOption2Array(animateOption as IAnimateOption), + u_time: this.layer.getLayerAnimateTime(), }; } @@ -29,21 +45,62 @@ export default class LineModel extends BaseModel { vertexShader: line_vert, fragmentShader: line_frag, triangulation: LineTriangulation, - blend: { - enable: true, - func: { - srcRGB: gl.SRC_ALPHA, - srcAlpha: 1, - dstRGB: gl.ONE_MINUS_SRC_ALPHA, - dstAlpha: 1, - }, - }, + blend: this.getBlend(), }), ]; } protected registerBuiltinAttributes() { // const lineType = this // point layer size; + const { + lineType = 'solid', + } = this.layer.getLayerConfig() as ILineLayerStyleOptions; + // if (lineType === 'dash') { + this.styleAttributeService.registerStyleAttribute({ + name: 'distance', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Distance', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 1, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + return [vertex[3]]; + }, + }, + }); + this.styleAttributeService.registerStyleAttribute({ + name: 'total_distance', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Total_Distance', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 1, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + return [vertex[5]]; + }, + }, + }); + // } this.styleAttributeService.registerStyleAttribute({ name: 'size', type: AttributeType.Attribute, diff --git a/packages/layers/src/line/shaders/line_arc2d_vert.glsl b/packages/layers/src/line/shaders/line_arc2d_vert.glsl index 93b7bc9534..e9cfafb0d0 100644 --- a/packages/layers/src/line/shaders/line_arc2d_vert.glsl +++ b/packages/layers/src/line/shaders/line_arc2d_vert.glsl @@ -1,13 +1,23 @@ +#define LineTypeSolid 0.0 +#define LineTypeDash 1.0 +#define Animate 0.0 attribute vec4 a_Color; attribute vec3 a_Position; attribute vec4 a_Instance; attribute float a_Size; uniform mat4 u_ModelMatrix; uniform float segmentNumber; +uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ]; varying vec4 v_color; varying vec2 v_normal; +varying float v_distance_ratio; +uniform float u_line_type: 0.0; +uniform vec2 u_dash_array: [10.0, 5.]; +varying vec2 v_dash_array; + #pragma include "projection" +#pragma include "project" #pragma include "picking" float maps (float value, float start1, float stop1, float start2, float stop2) { @@ -83,13 +93,22 @@ void main() { float segmentIndex = a_Position.x; float segmentRatio = getSegmentRatio(segmentIndex); float indexDir = mix(-1.0, 1.0, step(segmentIndex, 0.0)); + if(u_line_type == LineTypeDash) { + v_distance_ratio = segmentIndex / segmentNumber; + float total_Distance = pixelDistance(a_Instance.rg, a_Instance.ba); + v_dash_array = pow(2.0, 20.0 - u_Zoom) * u_dash_array / (total_Distance / segmentNumber * segmentIndex); + } + if(u_aimate.x == Animate) { + v_distance_ratio = segmentIndex / segmentNumber; + } float nextSegmentRatio = getSegmentRatio(segmentIndex + indexDir); + v_distance_ratio = segmentIndex / segmentNumber; vec4 curr = project_position(vec4(degrees(interpolate(source, target, angularDist, segmentRatio)), 0.0, 1.0)); vec4 next = project_position(vec4(degrees(interpolate(source, target, angularDist, nextSegmentRatio)), 0.0, 1.0)); v_normal = getNormal((next.xy - curr.xy) * indexDir, a_Position.y); vec2 offset = project_pixel(getExtrusionOffset((next.xy - curr.xy) * indexDir, a_Position.y)); // vec4 project_pos = project_position(vec4(curr.xy, 0, 1.0)); - gl_Position = project_common_position_to_clipspace(vec4(curr.xy + offset, 0, 1.0)); + gl_Position = project_common_position_to_clipspace(vec4(curr.xy + offset, curr.z, 1.0)); setPickingColor(a_PickingColor); } diff --git a/packages/layers/src/line/shaders/line_arc_frag.glsl b/packages/layers/src/line/shaders/line_arc_frag.glsl index a371c17cb7..4c1757aaf6 100644 --- a/packages/layers/src/line/shaders/line_arc_frag.glsl +++ b/packages/layers/src/line/shaders/line_arc_frag.glsl @@ -1,7 +1,17 @@ +#define LineTypeSolid 0.0 +#define LineTypeDash 1.0 +#define Animate 0.0 + uniform float u_opacity; -varying vec4 v_color; -uniform float u_blur : 0.90; +uniform float u_blur : 0.9; +uniform float u_line_type: 0.0; varying vec2 v_normal; +varying vec2 v_dash_array; +varying float v_distance_ratio; +varying vec4 v_color; + +uniform float u_time; +uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ]; #pragma include "picking" @@ -9,5 +19,17 @@ void main() { gl_FragColor = v_color; float blur = 1.- smoothstep(u_blur, 1., length(v_normal.xy)); gl_FragColor.a *= (blur * u_opacity); + if(u_line_type == LineTypeDash) { + gl_FragColor.a *= blur * (1.0- step(v_dash_array.x, mod(v_distance_ratio, v_dash_array.x +v_dash_array.y))); + // gl_FragColor.a = + } + + if(u_aimate.x == Animate) { + float alpha =1.0 - fract( mod(1.0- v_distance_ratio, u_aimate.z)* (1.0/ u_aimate.z) + u_time / u_aimate.y); + alpha = (alpha + u_aimate.w -1.0) / u_aimate.w; + alpha = smoothstep(0., 1., alpha); + gl_FragColor.a *= alpha; + // gl_FragColor.a = fract(u_time); + } gl_FragColor = filterColor(gl_FragColor); } diff --git a/packages/layers/src/line/shaders/line_arc_vert.glsl b/packages/layers/src/line/shaders/line_arc_vert.glsl index 7ac5b71df4..d0ce46b315 100644 --- a/packages/layers/src/line/shaders/line_arc_vert.glsl +++ b/packages/layers/src/line/shaders/line_arc_vert.glsl @@ -1,3 +1,6 @@ +#define LineTypeSolid 0.0 +#define LineTypeDash 1.0 +#define Animate 0.0 attribute vec3 a_Position; attribute vec4 a_Instance; attribute vec4 a_Color; @@ -5,10 +8,17 @@ attribute float a_Size; uniform mat4 u_ModelMatrix; uniform float segmentNumber; +uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ]; varying vec4 v_color; varying vec2 v_normal; +varying float v_distance_ratio; +uniform float u_line_type: 0.0; +uniform vec2 u_dash_array: [10.0, 5.]; + +varying vec2 v_dash_array; #pragma include "projection" +#pragma include "project" #pragma include "picking" float maps (float value, float start1, float stop1, float start2, float stop2) { @@ -61,6 +71,15 @@ void main() { float segmentRatio = getSegmentRatio(segmentIndex); float indexDir = mix(-1.0, 1.0, step(segmentIndex, 0.0)); + if(u_line_type == LineTypeDash) { + v_distance_ratio = segmentIndex / segmentNumber; + float total_Distance = pixelDistance(a_Instance.rg, a_Instance.ba) / 2.0 * PI; + v_dash_array = pow(2.0, 20.0 - u_Zoom) * u_dash_array / (total_Distance / segmentNumber * segmentIndex); + } + if(u_aimate.x == Animate) { + v_distance_ratio = segmentIndex / segmentNumber; + } + float nextSegmentRatio = getSegmentRatio(segmentIndex + indexDir); vec3 curr = getPos(source, target, segmentRatio); vec3 next = getPos(source, target, nextSegmentRatio); diff --git a/packages/layers/src/line/shaders/line_bezier_vert.glsl b/packages/layers/src/line/shaders/line_bezier_vert.glsl index 4856661047..50a8b340ae 100644 --- a/packages/layers/src/line/shaders/line_bezier_vert.glsl +++ b/packages/layers/src/line/shaders/line_bezier_vert.glsl @@ -1,13 +1,22 @@ +#define LineTypeSolid 0.0 +#define LineTypeDash 1.0 +#define Animate 0.0 attribute vec4 a_Color; attribute vec3 a_Position; attribute vec4 a_Instance; attribute float a_Size; uniform mat4 u_ModelMatrix; uniform float segmentNumber; +uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ]; varying vec4 v_color; varying vec2 v_normal; +varying float v_distance_ratio; +uniform float u_line_type: 0.0; +uniform vec2 u_dash_array: [10.0, 5.]; +varying vec2 v_dash_array; #pragma include "projection" +#pragma include "project" #pragma include "picking" float bezier3(vec3 arr, float t) { @@ -58,7 +67,14 @@ void main() { float segmentRatio = getSegmentRatio(segmentIndex); float indexDir = mix(-1.0, 1.0, step(segmentIndex, 0.0)); float nextSegmentRatio = getSegmentRatio(segmentIndex + indexDir); - + if(u_line_type == LineTypeDash) { + v_distance_ratio = segmentIndex / segmentNumber; + float total_Distance = pixelDistance(a_Instance.rg, a_Instance.ba) / 2.0 * PI; + v_dash_array = pow(2.0, 20.0 - u_Zoom) * u_dash_array / (total_Distance / segmentNumber * segmentIndex); + } + if(u_aimate.x == Animate) { + v_distance_ratio = segmentIndex / segmentNumber; + } vec4 curr = project_position(vec4(interpolate(source, target, segmentRatio), 0.0, 1.0)); vec4 next = project_position(vec4(interpolate(source, target, nextSegmentRatio), 0.0, 1.0)); v_normal = getNormal((next.xy - curr.xy) * indexDir, a_Position.y); diff --git a/packages/layers/src/line/shaders/line_dash_vert.glsl b/packages/layers/src/line/shaders/line_dash_vert.glsl index ec7c3590d6..e918c3b10e 100644 --- a/packages/layers/src/line/shaders/line_dash_vert.glsl +++ b/packages/layers/src/line/shaders/line_dash_vert.glsl @@ -22,7 +22,7 @@ varying float v_distance_ratio; #pragma include "projection" void main() { - + v_distance_ratio = a_Distance / a_Total_Distance; v_dash_array = pow(2.0, 20.0 - u_Zoom) * u_dash_array / a_Total_Distance; diff --git a/packages/layers/src/line/shaders/line_frag.glsl b/packages/layers/src/line/shaders/line_frag.glsl index 041b2fba47..0303e815b2 100644 --- a/packages/layers/src/line/shaders/line_frag.glsl +++ b/packages/layers/src/line/shaders/line_frag.glsl @@ -1,29 +1,44 @@ +#define LineTypeSolid 0.0 +#define LineTypeDash 1.0 +#define Animate 0.0 uniform float u_blur : 0.9; +uniform float u_line_type: 0.0; uniform float u_opacity : 1.0; -uniform float u_dash_offset : 0.0; -uniform float u_dash_ratio : 0.0; varying vec4 v_color; varying vec2 v_normal; + +// dash +uniform float u_dash_offset : 0.0; +uniform float u_dash_ratio : 0.1; +varying float v_distance_ratio; +varying vec2 v_dash_array; +varying float v_side; + + #pragma include "picking" uniform float u_time; uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ]; // [animate, duration, interval, trailLength], -varying float v_distance_ratio; -varying float v_dash_array; void main() { gl_FragColor = v_color; // anti-alias float blur = 1.- smoothstep(u_blur, 1., length(v_normal.xy)) * u_opacity; // gl_FragColor.a *= blur; - #ifdef ANIMATE - float alpha =1.0 - fract( mod(1.0- v_distance_ratio,u_aimate.z)* (1.0/ u_aimate.z) + u_time / u_aimate.y); - alpha = (alpha + u_aimate.w -1.0) / u_aimate.w; - alpha = smoothstep(0., 1., alpha); - gl_FragColor.a *= alpha * blur; - #endif + if(u_aimate.x == Animate) { + float alpha =1.0 - fract( mod(1.0- v_distance_ratio, u_aimate.z)* (1.0/ u_aimate.z) + u_time / u_aimate.y); + alpha = (alpha + u_aimate.w -1.0) / u_aimate.w; + alpha = smoothstep(0., 1., alpha); + float alpha2 = exp(-abs(v_side)); + gl_FragColor.a *= alpha * blur * alpha2; + // gl_FragColor.a = fract(u_time); + } + // dash line + if(u_line_type == LineTypeDash) { + gl_FragColor.a *= blur * (1.0- step(v_dash_array.x, mod(v_distance_ratio, v_dash_array.x +v_dash_array.y))); + } gl_FragColor = filterColor(gl_FragColor); } diff --git a/packages/layers/src/line/shaders/line_vert.glsl b/packages/layers/src/line/shaders/line_vert.glsl index d29d006822..c3036680b9 100644 --- a/packages/layers/src/line/shaders/line_vert.glsl +++ b/packages/layers/src/line/shaders/line_vert.glsl @@ -1,23 +1,46 @@ +#define LineTypeSolid 0.0 +#define LineTypeDash 1.0 +#define Animate 0.0 attribute float a_Miter; attribute vec4 a_Color; attribute vec2 a_Size; attribute vec3 a_Normal; attribute vec3 a_Position; + +// dash line +attribute float a_Total_Distance; +attribute float a_Distance; + uniform mat4 u_ModelMatrix; +uniform float u_line_type: 0.0; +uniform vec2 u_dash_array: [10.0, 5.]; +uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ]; #pragma include "projection" #pragma include "picking" varying vec4 v_color; -varying float v_dash_array; +varying vec2 v_dash_array; varying vec2 v_normal; +varying float v_distance_ratio; +varying float v_side; + void main() { + + if(u_line_type == LineTypeDash) { + v_distance_ratio = a_Distance / a_Total_Distance; + v_dash_array = pow(2.0, 20.0 - u_Zoom) * u_dash_array / a_Total_Distance; + } + if(u_aimate.x == Animate) { + v_distance_ratio = a_Distance / a_Total_Distance; + } v_normal = vec2(reverse_offset_normal(a_Normal) * sign(a_Miter)); v_color = a_Color; vec3 size = a_Miter * a_Size.x * reverse_offset_normal(a_Normal); //v_normal * vec3(1., -1., 1.0); vec2 offset = project_pixel(size.xy); + v_side = a_Miter * a_Size.x; vec4 project_pos = project_position(vec4(a_Position.xy, 0, 1.0)); gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, a_Size.y, 1.0)); diff --git a/packages/layers/src/plugins/ConfigSchemaValidationPlugin.ts b/packages/layers/src/plugins/ConfigSchemaValidationPlugin.ts index da74dd38f3..0943149f53 100644 --- a/packages/layers/src/plugins/ConfigSchemaValidationPlugin.ts +++ b/packages/layers/src/plugins/ConfigSchemaValidationPlugin.ts @@ -21,12 +21,12 @@ export default class ConfigSchemaValidationPlugin implements ILayerPlugin { public apply(layer: ILayer) { layer.hooks.init.tap('ConfigSchemaValidationPlugin', () => { this.configService.registerLayerConfigSchemaValidator( - layer.name, + layer.name as string, layer.getConfigSchemaForValidation(), ); const { valid, errorText } = this.configService.validateLayerConfig( - layer.name, + layer.name as string, layer.getLayerConfig(), ); diff --git a/packages/layers/src/plugins/FeatureScalePlugin.ts b/packages/layers/src/plugins/FeatureScalePlugin.ts index 2bcfdb0901..955f904afd 100644 --- a/packages/layers/src/plugins/FeatureScalePlugin.ts +++ b/packages/layers/src/plugins/FeatureScalePlugin.ts @@ -118,7 +118,7 @@ export default class FeatureScalePlugin implements ILayerPlugin { scales.forEach((scale) => { // 如果设置了回调, 这不需要设置让range if (!attributeScale.callback) { - if (attributeScale.values) { + if (attributeScale.values && attributeScale.values !== 'text') { if ( scale.option?.type === 'linear' && attributeScale.values.length > 2 @@ -131,6 +131,7 @@ export default class FeatureScalePlugin implements ILayerPlugin { scale.scale.range(attributeScale.values); } else if (scale.option?.type === 'cat') { // 如果没有设置初值且 类型为cat,range ==domain; + scale.scale.range(scale.option.domain); } } @@ -159,20 +160,13 @@ export default class FeatureScalePlugin implements ILayerPlugin { dataArray: IParseDataItem[], ) { const scalekey = [field, attribute.name].join('_'); + const values = attribute.scale?.values; if (this.scaleCache[scalekey]) { return this.scaleCache[scalekey]; } - const styleScale = this.createScale(field, dataArray); + const styleScale = this.createScale(field, values, dataArray); this.scaleCache[scalekey] = styleScale; - // if ( - // styleScale.type === StyleScaleType.VARIABLE && - // attribute.scale?.values && - // attribute.scale?.values.length > 0 - // ) { // 只有变量初始化range - // styleScale.scale.range(attribute.scale?.values); - // } - return this.scaleCache[scalekey]; } @@ -191,7 +185,11 @@ export default class FeatureScalePlugin implements ILayerPlugin { return [field]; } - private createScale(field: string, data?: IParseDataItem[]): IStyleScale { + private createScale( + field: string, + values: unknown[] | string | undefined, + data?: IParseDataItem[], + ): IStyleScale { // 首先查找全局默认配置例如 color const scaleOption: IScale | undefined = this.scaleOptions[field]; const styleScale: IStyleScale = { @@ -200,6 +198,7 @@ export default class FeatureScalePlugin implements ILayerPlugin { type: StyleScaleType.VARIABLE, option: scaleOption, }; + if (!data || !data.length) { if (scaleOption && scaleOption.type) { styleScale.scale = this.createDefaultScale(scaleOption); @@ -216,9 +215,12 @@ export default class FeatureScalePlugin implements ILayerPlugin { styleScale.type = StyleScaleType.CONSTANT; } else { // 根据数据类型判断 默认等分位,时间,和枚举类型 - const type = + let type = (scaleOption && scaleOption.type) || this.getDefaultType(firstValue); - + if (values === 'text') { + // text 为内置变 如果是文本则为cat + type = ScaleTypes.CAT; + } const cfg = this.createDefaultScaleConfig(type, field, data); Object.assign(cfg, scaleOption); styleScale.scale = this.createDefaultScale(cfg); diff --git a/packages/layers/src/plugins/LayerAnimateStylePlugin.ts b/packages/layers/src/plugins/LayerAnimateStylePlugin.ts new file mode 100644 index 0000000000..db766a6e35 --- /dev/null +++ b/packages/layers/src/plugins/LayerAnimateStylePlugin.ts @@ -0,0 +1,32 @@ +import { + CameraUniform, + CoordinateUniform, + ICameraService, + ICoordinateSystemService, + ILayer, + ILayerPlugin, + IModel, + IRendererService, + TYPES, +} from '@antv/l7-core'; +import { inject, injectable } from 'inversify'; + +@injectable() +export default class LayerAnimateStylePlugin implements ILayerPlugin { + @inject(TYPES.ICameraService) + private readonly cameraService: ICameraService; + + @inject(TYPES.IRendererService) + private readonly rendererService: IRendererService; + + public apply(layer: ILayer) { + layer.hooks.beforeRender.tap('LayerAnimateStylePlugin', () => { + // 重新计算坐标系参数 + layer.models.forEach((model: IModel) => { + model.addUniforms({ + ...layer.layerModel.getAnimateUniforms(), + }); + }); + }); + } +} diff --git a/packages/layers/src/plugins/ShaderUniformPlugin.ts b/packages/layers/src/plugins/ShaderUniformPlugin.ts index a075e0db0e..4e488b7754 100644 --- a/packages/layers/src/plugins/ShaderUniformPlugin.ts +++ b/packages/layers/src/plugins/ShaderUniformPlugin.ts @@ -31,7 +31,6 @@ export default class ShaderUniformPlugin implements ILayerPlugin { public apply(layer: ILayer) { layer.hooks.beforeRender.tap('ShaderUniformPlugin', () => { // 重新计算坐标系参数 - this.coordinateSystemService.refresh(); const { width, height } = this.rendererService.getViewportSize(); diff --git a/packages/layers/src/point/index.ts b/packages/layers/src/point/index.ts index 232d05edde..9ea48d5fe7 100644 --- a/packages/layers/src/point/index.ts +++ b/packages/layers/src/point/index.ts @@ -4,7 +4,7 @@ import PointModels, { PointType } from './models/index'; interface IPointLayerStyleOptions { opacity: number; strokeWidth: number; - strokeColor: string; + stroke: string; } export default class PointLayer extends BaseLayer { public type: string = 'PointLayer'; diff --git a/packages/layers/src/point/models/fill.ts b/packages/layers/src/point/models/fill.ts index 0b068eb6ca..61a01b11fa 100644 --- a/packages/layers/src/point/models/fill.ts +++ b/packages/layers/src/point/models/fill.ts @@ -1,9 +1,11 @@ import { AttributeType, gl, + IAnimateOption, IAttribute, IElements, IEncodeFeature, + ILayerConfig, IModel, IModelUniform, } from '@antv/l7-core'; @@ -15,19 +17,26 @@ import pointFillVert from '../shaders/fill_vert.glsl'; interface IPointLayerStyleOptions { opacity: number; strokeWidth: number; - strokeColor: string; + stroke: string; } export default class FillModel extends BaseModel { public getUninforms(): IModelUniform { const { opacity = 1, - strokeColor = 'rgb(0,0,0,0)', + stroke = 'rgb(0,0,0,0)', strokeWidth = 1, } = this.layer.getLayerConfig() as IPointLayerStyleOptions; return { u_opacity: opacity, u_stroke_width: strokeWidth, - u_stroke_color: rgb2arr(strokeColor), + u_stroke_color: rgb2arr(stroke), + }; + } + public getAnimateUniforms(): IModelUniform { + const { animateOption } = this.layer.getLayerConfig() as ILayerConfig; + return { + u_aimate: this.animateOption2Array(animateOption as IAnimateOption), + u_time: this.layer.getLayerAnimateTime(), }; } diff --git a/packages/layers/src/point/models/index.ts b/packages/layers/src/point/models/index.ts index e0b6ef8c87..ea4c8b9647 100644 --- a/packages/layers/src/point/models/index.ts +++ b/packages/layers/src/point/models/index.ts @@ -3,6 +3,7 @@ import ExtrudeModel from './extrude'; import FillModel from './fill'; import IMageModel from './image'; import NormalModel from './normal'; +import TextModel from './text'; export type PointType = 'fill' | 'image' | 'normal' | 'extrude' | 'text'; @@ -11,7 +12,7 @@ const PointModels: { [key in PointType]: any } = { image: IMageModel, normal: NormalModel, extrude: ExtrudeModel, - text: null, + text: TextModel, }; export default PointModels; diff --git a/packages/layers/src/point/models/normal.ts b/packages/layers/src/point/models/normal.ts index 4dd82398a9..c0ef21654e 100644 --- a/packages/layers/src/point/models/normal.ts +++ b/packages/layers/src/point/models/normal.ts @@ -16,7 +16,7 @@ import normalVert from '../shaders/normal_vert.glsl'; interface IPointLayerStyleOptions { opacity: number; strokeWidth: number; - strokeColor: string; + stroke: string; } export function PointTriangulation(feature: IEncodeFeature) { const coordinates = feature.coordinates as number[]; @@ -36,13 +36,13 @@ export default class NormalModel extends BaseModel { public getUninforms(): IModelUniform { const { opacity = 1, - strokeColor = 'rgb(0,0,0,0)', + stroke = 'rgb(0,0,0,0)', strokeWidth = 1, } = this.layer.getLayerConfig() as IPointLayerStyleOptions; return { u_opacity: opacity, u_stroke_width: strokeWidth, - u_stroke_color: rgb2arr(strokeColor), + u_stroke_color: rgb2arr(stroke), }; } public buildModels(): IModel[] { diff --git a/packages/layers/src/point/models/text.ts b/packages/layers/src/point/models/text.ts index 4b643b340c..085c1edec3 100644 --- a/packages/layers/src/point/models/text.ts +++ b/packages/layers/src/point/models/text.ts @@ -1,15 +1,264 @@ -import { IModel, IModelUniform } from '@antv/l7-core'; +import { + AttributeType, + BlendType, + gl, + IEncodeFeature, + ILayerConfig, + IModel, + IModelUniform, + ITexture2D, +} from '@antv/l7-core'; +import { rgb2arr } from '@antv/l7-utils'; import BaseModel from '../../core/BaseModel'; +import { PointFillTriangulation } from '../../core/triangulation'; +import { + getGlyphQuads, + IGlyphQuad, + shapeText, +} from '../../utils/symbol-layout'; +import textFrag from '../shaders/text_frag.glsl'; +import textVert from '../shaders/text_vert.glsl'; +interface IPointTextLayerStyleOptions { + opacity: number; + textAnchor: string; + spacing: number; + padding: [number, number]; + stroke: string; + strokeWidth: number; + strokeOpacity: number; + fontWeight: string; + fontFamily: string; + textOffset: [number, number]; + textAllowOverlap: boolean; +} +export function TextTriangulation(feature: IEncodeFeature) { + const coordinates = feature.coordinates as number[]; + const { glyphQuads } = feature; + const vertices: number[] = []; + const indices: number[] = []; + const coord = + coordinates.length === 2 + ? [coordinates[0], coordinates[1], 0] + : coordinates; + glyphQuads.forEach((quad: IGlyphQuad, index: number) => { + vertices.push( + ...coord, + quad.tex.x, + quad.tex.y + quad.tex.height, + quad.tl.x, + quad.tl.y, + ...coord, + quad.tex.x + quad.tex.width, + quad.tex.y + quad.tex.height, + quad.tr.x, + quad.tr.y, + ...coord, + quad.tex.x + quad.tex.width, + quad.tex.y, + quad.br.x, + quad.br.y, + ...coord, + quad.tex.x, + quad.tex.y, + quad.bl.x, + quad.bl.y, + ); + indices.push( + 0 + index * 4, + 1 + index * 4, + 2 + index * 4, + 2 + index * 4, + 3 + index * 4, + 0 + index * 4, + ); + }); + return { + vertices, // [ x, y, z, tex.x,tex.y, offset.x. offset.y] + indices, + size: 7, + }; +} -export default class ExtrudeModel extends BaseModel { +export default class TextModel extends BaseModel { + private texture: ITexture2D; public getUninforms(): IModelUniform { - throw new Error('Method not implemented.'); + const { + fontWeight = 'normal', + fontFamily, + stroke, + strokeWidth, + } = this.layer.getLayerConfig() as IPointTextLayerStyleOptions; + const { canvas, fontAtlas, mapping } = this.fontService; + return { + u_opacity: 1.0, + u_sdf_map: this.texture, + u_stroke: rgb2arr(stroke), + u_halo_blur: 0.5, + u_sdf_map_size: [canvas.width, canvas.height], + u_strokeWidth: strokeWidth, + }; } public buildModels(): IModel[] { - throw new Error('Method not implemented.'); + this.initTextFont(); + this.generateGlyphLayout(); + this.registerBuiltinAttributes(); + this.updateTexture(); + return [ + this.layer.buildLayerModel({ + moduleName: 'pointText', + vertexShader: textVert, + fragmentShader: textFrag, + triangulation: TextTriangulation, + depth: { enable: false }, + blend: this.getBlend(), + }), + ]; } + protected registerBuiltinAttributes() { - throw new Error('Method not implemented.'); + const viewProjection = this.cameraService.getViewProjectionMatrix(); + this.styleAttributeService.registerStyleAttribute({ + name: 'textOffsets', + type: AttributeType.Attribute, + descriptor: { + name: 'a_textOffsets', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.STATIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 2, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + return [vertex[5], vertex[6]]; + }, + }, + }); + + // point layer size; + this.styleAttributeService.registerStyleAttribute({ + name: 'size', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Size', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 1, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + const { size } = feature; + return Array.isArray(size) ? [size[0]] : [size as number]; + }, + }, + }); + + // point layer size; + this.styleAttributeService.registerStyleAttribute({ + name: 'textUv', + type: AttributeType.Attribute, + descriptor: { + name: 'a_tex', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 2, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + return [vertex[3], vertex[4]]; + }, + }, + }); + } + private initTextFont() { + const { + fontWeight = 'normal', + fontFamily, + } = this.layer.getLayerConfig() as IPointTextLayerStyleOptions; + const data = this.layer.getEncodedData(); + const characterSet: string[] = []; + data.forEach((item: IEncodeFeature) => { + let { shape = '' } = item; + shape = shape.toString(); + for (const char of shape) { + // 去重 + if (characterSet.indexOf(char) === -1) { + characterSet.push(char); + } + } + }); + this.fontService.setFontOptions({ + characterSet, + fontWeight, + fontFamily, + }); + } + private generateGlyphLayout() { + const { canvas, fontAtlas, 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; + const shaping = shapeText( + shape.toString(), + mapping, + 24, + textAnchor, + 'center', + spacing, + textOffset, + ); + const glyphQuads = getGlyphQuads(shaping, textOffset, false); + feature.shaping = shaping; + feature.glyphQuads = glyphQuads; + }); + } + + private drawGlyph() { + const { + spacing = 2, + textAnchor = 'center', + textOffset = [0, 0], + padding = [4, 4], + textAllowOverlap, + } = this.layer.getLayerConfig() as IPointTextLayerStyleOptions; + const viewProjection = this.cameraService.getViewProjectionMatrix(); + } + private updateTexture() { + const { createTexture2D } = this.rendererService; + const { canvas } = this.fontService; + this.texture = createTexture2D({ + data: canvas, + width: canvas.width, + height: canvas.height, + }); } } diff --git a/packages/layers/src/point/shaders/fill_frag.glsl b/packages/layers/src/point/shaders/fill_frag.glsl index b4c4b86e10..2e42a84e58 100644 --- a/packages/layers/src/point/shaders/fill_frag.glsl +++ b/packages/layers/src/point/shaders/fill_frag.glsl @@ -1,3 +1,5 @@ +#define Animate 0.0 + uniform float u_blur : 0; uniform float u_opacity : 1; uniform float u_stroke_width : 1; @@ -7,6 +9,8 @@ uniform float u_stroke_opacity : 1; varying vec4 v_data; varying vec4 v_color; varying float v_radius; +uniform float u_time; +uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ]; #pragma include "sdf_2d" #pragma include "picking" @@ -61,8 +65,19 @@ void main() { inner_df ); vec4 strokeColor = u_stroke_color == vec4(0) ? v_color : u_stroke_color; + float PI = 3.14159; + float N_RINGS = 3.0; + float FREQ = 1.0; + + gl_FragColor = opacity_t * mix(vec4(v_color.rgb, v_color.a * u_opacity), strokeColor * u_stroke_opacity, color_t); + if(u_aimate.x == Animate) { + float d = length(v_data.xy); + float intensity = clamp(cos(d * PI), 0.0, 1.0) * clamp(cos(2.0 * PI * (d * 2.0 * N_RINGS - FREQ * u_time)), 0.0, 1.0); + gl_FragColor = vec4(gl_FragColor.xyz * intensity, intensity); + } + gl_FragColor = filterColor(gl_FragColor); } diff --git a/packages/layers/src/point/shaders/fill_vert.glsl b/packages/layers/src/point/shaders/fill_vert.glsl index af5cb17eb2..80cfc15b18 100644 --- a/packages/layers/src/point/shaders/fill_vert.glsl +++ b/packages/layers/src/point/shaders/fill_vert.glsl @@ -34,7 +34,7 @@ void main() { float antialiasblur = 1.0 / (a_Size + u_stroke_width); // construct point coords - v_data = vec4(extrude, antialiasblur, shape_type); + v_data = vec4(extrude, antialiasblur,shape_type); setPickingColor(a_PickingColor); } diff --git a/packages/layers/src/point/shaders/text_frag.glsl b/packages/layers/src/point/shaders/text_frag.glsl index d3dfa93034..11b8520478 100644 --- a/packages/layers/src/point/shaders/text_frag.glsl +++ b/packages/layers/src/point/shaders/text_frag.glsl @@ -1,3 +1,5 @@ +#define SDF_PX 8.0 +#define EDGE_GAMMA 0.105 uniform sampler2D u_sdf_map; uniform float u_gamma_scale : 0.5; uniform float u_font_size : 24; @@ -5,6 +7,7 @@ uniform float u_opacity : 1.0; uniform vec4 u_stroke : [0, 0, 0, 1]; uniform float u_strokeWidth : 2.0; uniform float u_halo_blur : 0.5; +uniform float u_DevicePixelRatio; varying vec4 v_color; varying vec2 v_uv; @@ -17,7 +20,7 @@ void main() { float fontScale = u_font_size / 24.0; lowp float buff = (6.0 - u_strokeWidth / fontScale) / SDF_PX; - highp float gamma = (u_halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * u_gamma_scale); + highp float gamma = (u_halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * u_gamma_scale) / 1.0; highp float gamma_scaled = gamma * v_gamma_scale; diff --git a/packages/layers/src/point/shaders/text_vert.glsl b/packages/layers/src/point/shaders/text_vert.glsl index 496a5180c2..729b446105 100644 --- a/packages/layers/src/point/shaders/text_vert.glsl +++ b/packages/layers/src/point/shaders/text_vert.glsl @@ -1,14 +1,13 @@ +#define SDF_PX 8.0 +#define EDGE_GAMMA 0.105 attribute vec3 a_Position; attribute vec2 a_tex; -attribute vec2 a_offset; -attribute vec4 a_color; -attribute float a_size; +attribute vec2 a_textOffsets; +attribute vec4 a_Color; +attribute float a_Size; uniform vec2 u_sdf_map_size; -uniform vec2 u_viewport_size; - -uniform float u_activeId : 0; -uniform vec4 u_activeColor : [1.0, 0.0, 0.0, 1.0]; +uniform mat4 u_ModelMatrix; varying vec2 v_uv; varying float v_gamma_scale; @@ -17,18 +16,18 @@ varying vec4 v_color; #pragma include "projection" void main() { - v_color = a_color; + v_color = a_Color; v_uv = a_tex / u_sdf_map_size; // 文本缩放比例 - float fontScale = a_size / 24.; + float fontScale = a_Size / 24.; vec4 project_pos = project_position(vec4(a_Position, 1.0)); vec4 projected_position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0)); gl_Position = vec4(projected_position.xy / projected_position.w - + a_offset * fontScale / u_viewport_size * 2., 0.0, 1.0); + + a_textOffsets * fontScale / u_ViewportSize * 2., 0.0, 1.0); v_gamma_scale = gl_Position.w; diff --git a/packages/layers/src/point/text.ts b/packages/layers/src/point/text.ts deleted file mode 100644 index becc151b72..0000000000 --- a/packages/layers/src/point/text.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { AttributeType, gl, IEncodeFeature } from '@antv/l7-core'; -import BaseLayer from '../core/BaseLayer'; -import { getGlyphQuads, shapeText } from '../utils/symbol-layout'; -import textFrag from './shaders/text_frag.glsl'; -import textVert from './shaders/text_vert.glsl'; -interface IPointTextLayerStyleOptions { - opacity: number; - textAnchor: string; - textOffset: [number, number]; - spacing: number; - padding: [number, number]; - stroke: string; - strokeWidth: number; - strokeOpacity: number; - fontWeight: string; - fontFamily: string; - - textAllowOverlap: boolean; -} -export function PointTriangulation(feature: IEncodeFeature) { - const coordinates = feature.coordinates as number[]; - return { - vertices: [...coordinates, ...coordinates, ...coordinates, ...coordinates], - indices: [0, 1, 2, 2, 3, 0], - size: coordinates.length, - }; -} -export default class TextLayer extends BaseLayer { - public type: string = 'PointLayer'; - - protected getConfigSchema() { - return { - properties: { - opacity: { - type: 'number', - minimum: 0, - maximum: 1, - }, - }, - }; - } - - protected renderModels() { - const { opacity } = this.getLayerConfig(); - this.models.forEach((model) => - model.draw({ - uniforms: { - u_opacity: opacity || 1.0, - }, - }), - ); - return this; - } - - protected buildModels() { - this.registerBuiltinAttributes(); - this.models = [ - this.buildLayerModel({ - moduleName: 'pointText', - vertexShader: textVert, - fragmentShader: textFrag, - triangulation: PointTriangulation, - depth: { enable: false }, - blend: { - enable: true, - func: { - srcRGB: gl.SRC_ALPHA, - srcAlpha: 1, - dstRGB: gl.ONE_MINUS_SRC_ALPHA, - dstAlpha: 1, - }, - }, - }), - ]; - } - - private registerBuiltinAttributes() { - this.styleAttributeService.registerStyleAttribute({ - name: 'textOffsets', - type: AttributeType.Attribute, - descriptor: { - name: 'a_textOffsets', - buffer: { - // give the WebGL driver a hint that this buffer may change - usage: gl.STATIC_DRAW, - data: [], - type: gl.FLOAT, - }, - size: 2, - update: ( - feature: IEncodeFeature, - featureIdx: number, - vertex: number[], - attributeIdx: number, - ) => { - const extrude = [-1, -1, 1, -1, 1, 1, -1, 1]; - const extrudeIndex = (attributeIdx % 4) * 2; - return [extrude[extrudeIndex], extrude[extrudeIndex + 1]]; - }, - }, - }); - - // point layer size; - this.styleAttributeService.registerStyleAttribute({ - name: 'size', - type: AttributeType.Attribute, - descriptor: { - name: 'a_Size', - buffer: { - // give the WebGL driver a hint that this buffer may change - usage: gl.DYNAMIC_DRAW, - data: [], - type: gl.FLOAT, - }, - size: 1, - update: ( - feature: IEncodeFeature, - featureIdx: number, - vertex: number[], - attributeIdx: number, - ) => { - const { size } = feature; - return Array.isArray(size) ? [size[0]] : [size as number]; - }, - }, - }); - - // point layer size; - this.styleAttributeService.registerStyleAttribute({ - name: 'shape', - type: AttributeType.Attribute, - descriptor: { - name: 'a_Shape', - buffer: { - // give the WebGL driver a hint that this buffer may change - usage: gl.DYNAMIC_DRAW, - data: [], - type: gl.FLOAT, - }, - size: 1, - update: ( - feature: IEncodeFeature, - featureIdx: number, - vertex: number[], - attributeIdx: number, - ) => { - const { shape = 2 } = feature; - const shape2d = this.getLayerConfig().shape2d as string[]; - const shapeIndex = shape2d.indexOf(shape as string); - return [shapeIndex]; - }, - }, - }); - } - - private initTextFont() { - const { fontWeight = 'normal', fontFamily } = this.getLayerConfig(); - const data = this.getEncodedData(); - const characterSet: string[] = []; - data.forEach((item: IEncodeFeature) => { - let { shape = '' } = item; - shape = shape.toString(); - for (const char of shape) { - // 去重 - if (characterSet.indexOf(char) === -1) { - characterSet.push(char); - } - } - }); - - this.fontService.setFontOptions({ - characterSet, - fontWeight, - fontFamily, - }); - } -} diff --git a/packages/layers/src/utils/collision-index.ts b/packages/layers/src/utils/collision-index.ts new file mode 100644 index 0000000000..21e8873938 --- /dev/null +++ b/packages/layers/src/utils/collision-index.ts @@ -0,0 +1,109 @@ +export interface ICollisionBox { + x1: number; + y1: number; + x2: number; + y2: number; + anchorPointX: number; + anchorPointY: number; +} +// @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 + */ +export default class CollisionIndex { + private width: number; + private height: number; + private grid: GridIndex; + private screenRightBoundary: number; + private screenBottomBoundary: number; + private gridRightBoundary: number; + private gridBottomBoundary: number; + constructor(width: number, height: number) { + this.width = width; + this.height = height; + // 创建网格索引 + this.grid = new GridIndex( + width + 2 * viewportPadding, + height + 2 * viewportPadding, + 25, + ); + + this.screenRightBoundary = width + viewportPadding; + this.screenBottomBoundary = height + viewportPadding; + this.gridRightBoundary = width + 2 * viewportPadding; + this.gridBottomBoundary = height + 2 * viewportPadding; + } + + public placeCollisionBox(collisionBox: ICollisionBox, mvpMatrix: mat4) { + 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; + + if ( + !this.isInsideGrid(tlX, tlY, brX, brY) || + this.grid.hitTest(tlX, tlY, brX, brY) + ) { + return { + box: [], + }; + } + + return { + box: [tlX, tlY, brX, brY], + }; + } + + public insertCollisionBox(box: number[], featureIndex: number) { + const key = { featureIndex }; + this.grid.insert(key, box[0], box[1], box[2], box[3]); + } + + /** + * 后续碰撞检测都需要投影到 viewport 坐标系 + * @param {THREE.Matrix4} mvpMatrix mvp矩阵 + * @param {number} x P20 平面坐标X + * @param {number} y P20 平面坐标Y + * @return {Point} projectedPoint + */ + public project(mvpMatrix: mat4, x: number, y: number) { + const point = vec4.fromValues(x, y, 0, 1); + const out = vec4.create(); + vec4.transformMat4(out, point, mvpMatrix); + // 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, + }; + } + + /** + * 判断包围盒是否在整个网格内,需要加上 buffer + * @param {number} x1 x1 + * @param {number} y1 y1 + * @param {number} x2 x2 + * @param {number} y2 y2 + * @return {Point} isInside + */ + public isInsideGrid(x1: number, y1: number, x2: number, y2: number) { + return ( + x2 >= 0 && + x1 < this.gridRightBoundary && + y2 >= 0 && + y1 < this.gridBottomBoundary + ); + } +} diff --git a/packages/layers/src/utils/grid-index.ts b/packages/layers/src/utils/grid-index.ts new file mode 100644 index 0000000000..4f56a11b58 --- /dev/null +++ b/packages/layers/src/utils/grid-index.ts @@ -0,0 +1,210 @@ +interface IQueryArgs { + hitTest: boolean; + seenUids: { box: any; circle: any }; +} +type CallBack = (...args: any[]) => any; +/** + * 网格索引,相比 @mapbox/grid-index,在简单计算碰撞检测结果时效率更高 + * @see https://zhuanlan.zhihu.com/p/74373214 + */ +class GridIndex { + private boxCells: number[][]; + private xCellCount: number; + private yCellCount: number; + private boxKeys: string[]; + private bboxes: number[]; + private width: number; + private height: number; + private xScale: number; + private yScale: number; + private boxUid: number; + + constructor(width: number, height: number, cellSize: number) { + const boxCells = this.boxCells; + + this.xCellCount = Math.ceil(width / cellSize); + this.yCellCount = Math.ceil(height / cellSize); + + for (let i = 0; i < this.xCellCount * this.yCellCount; i++) { + boxCells.push([]); + } + this.boxKeys = []; + this.bboxes = []; + + this.width = width; + this.height = height; + this.xScale = this.xCellCount / width; + this.yScale = this.yCellCount / height; + this.boxUid = 0; + } + + public insert(key: any, x1: number, y1: number, x2: number, y2: number) { + this.forEachCell(x1, y1, x2, y2, this.insertBoxCell, this.boxUid++); + this.boxKeys.push(key); + this.bboxes.push(x1); + this.bboxes.push(y1); + this.bboxes.push(x2); + this.bboxes.push(y2); + } + + public query( + x1: number, + y1: number, + x2: number, + y2: number, + predicate?: CallBack, + ) { + return this.queryHitTest(x1, y1, x2, y2, false, predicate); + } + + public hitTest( + x1: number, + y1: number, + x2: number, + y2: number, + predicate?: CallBack, + ) { + return this.queryHitTest(x1, y1, x2, y2, true, predicate); + } + + private insertBoxCell( + x1: number, + y1: number, + x2: number, + y2: number, + cellIndex: number, + uid: number, + ) { + this.boxCells[cellIndex].push(uid); + } + + private queryHitTest( + x1: number, + y1: number, + x2: number, + y2: number, + hitTest: boolean, + predicate?: CallBack, + ) { + if (x2 < 0 || x1 > this.width || y2 < 0 || y1 > this.height) { + return hitTest ? false : []; + } + const result: any[] = []; + if (x1 <= 0 && y1 <= 0 && this.width <= x2 && this.height <= y2) { + // 这一步是高效的关键,后续精确碰撞检测结果在计算文本可见性时并不需要 + if (hitTest) { + return true; + } + for (let boxUid = 0; boxUid < this.boxKeys.length; boxUid++) { + result.push({ + key: this.boxKeys[boxUid], + x1: this.bboxes[boxUid * 4], + y1: this.bboxes[boxUid * 4 + 1], + x2: this.bboxes[boxUid * 4 + 2], + y2: this.bboxes[boxUid * 4 + 3], + }); + } + return predicate ? result.filter(predicate) : result; + } + + const queryArgs = { + hitTest, + seenUids: { box: {}, circle: {} }, + }; + this.forEachCell( + x1, + y1, + x2, + y2, + this.queryCell, + result, + queryArgs, + predicate, + ); + return hitTest ? result.length > 0 : result; + } + + private queryCell( + x1: number, + y1: number, + x2: number, + y2: number, + cellIndex: number, + result: any[], + queryArgs?: any, + predicate?: CallBack, + ) { + const seenUids = queryArgs.seenUids; + const boxCell = this.boxCells[cellIndex]; + if (boxCell !== null) { + const bboxes = this.bboxes; + for (const boxUid of boxCell) { + if (!seenUids.box[boxUid]) { + seenUids.box[boxUid] = true; + const offset = boxUid * 4; + if ( + x1 <= bboxes[offset + 2] && + y1 <= bboxes[offset + 3] && + x2 >= bboxes[offset + 0] && + y2 >= bboxes[offset + 1] && + (!predicate || predicate(this.boxKeys[boxUid])) + ) { + if (queryArgs.hitTest) { + result.push(true); + return true; + } + result.push({ + key: this.boxKeys[boxUid], + x1: bboxes[offset], + y1: bboxes[offset + 1], + x2: bboxes[offset + 2], + y2: bboxes[offset + 3], + }); + } + } + } + } + return false; + } + + private forEachCell( + x1: number, + y1: number, + x2: number, + y2: number, + fn: CallBack, + arg1: any[] | number, + arg2?: IQueryArgs, + predicate?: CallBack, + ) { + const cx1 = this.convertToXCellCoord(x1); + const cy1 = this.convertToYCellCoord(y1); + const cx2 = this.convertToXCellCoord(x2); + const cy2 = this.convertToYCellCoord(y2); + + for (let x = cx1; x <= cx2; x++) { + for (let y = cy1; y <= cy2; y++) { + const cellIndex = this.xCellCount * y + x; + if (fn.call(this, x1, y1, x2, y2, cellIndex, arg1, arg2, predicate)) { + return; + } + } + } + } + + private convertToXCellCoord(x: number) { + return Math.max( + 0, + Math.min(this.xCellCount - 1, Math.floor(x * this.xScale)), + ); + } + + private convertToYCellCoord(y: number) { + return Math.max( + 0, + Math.min(this.yCellCount - 1, Math.floor(y * this.yScale)), + ); + } +} + +export default GridIndex; diff --git a/packages/layers/src/utils/symbol-layout.ts b/packages/layers/src/utils/symbol-layout.ts index 204e192395..4ccf46d2d9 100644 --- a/packages/layers/src/utils/symbol-layout.ts +++ b/packages/layers/src/utils/symbol-layout.ts @@ -1,3 +1,22 @@ +interface IPoint { + x: number; + y: number; +} +export interface IGlyphQuad { + tr: IPoint; + tl: IPoint; + bl: IPoint; + br: IPoint; + tex: { + x: number; + y: number; + height: number; + width: number; + advance: number; + }; + glyphOffset: [number, number]; +} + /** * 返回文本相对锚点位置 * @param {string} anchor 锚点位置 @@ -181,7 +200,7 @@ export function shapeText( textAnchor: string, textJustify: string, spacing: number, - translate: [number, number], + translate: [number, number] = [0, 0], ) { // TODO:处理换行 const lines = text.split('\n'); @@ -215,11 +234,11 @@ export function shapeText( export function getGlyphQuads( shaping: any, - textOffset: [number, number], + textOffset: [number, number] = [0, 0], alongLine: boolean, -) { +): IGlyphQuad[] { const { positionedGlyphs } = shaping; - const quads = []; + const quads: IGlyphQuad[] = []; for (const positionedGlyph of positionedGlyphs) { const rect = positionedGlyph.metrics; @@ -229,7 +248,7 @@ export function getGlyphQuads( const halfAdvance = (rect.advance * positionedGlyph.scale) / 2; - const glyphOffset = alongLine + const glyphOffset: [number, number] = alongLine ? [positionedGlyph.x + halfAdvance, positionedGlyph.y] : [0, 0]; diff --git a/packages/maps/src/amap/index.ts b/packages/maps/src/amap/index.ts index fd26e3731e..60f55ed26e 100644 --- a/packages/maps/src/amap/index.ts +++ b/packages/maps/src/amap/index.ts @@ -114,7 +114,7 @@ export default class AMapService } public getZoom(): number { // 统一返回 Mapbox 缩放等级 - return this.map.getZoom(); + return this.map.getZoom() - 1; } public setZoom(zoom: number): void { diff --git a/stories/Layers/Layers.stories.tsx b/stories/Layers/Layers.stories.tsx index 4fd56d0c2f..15e517af9d 100644 --- a/stories/Layers/Layers.stories.tsx +++ b/stories/Layers/Layers.stories.tsx @@ -1,7 +1,9 @@ import { storiesOf } from '@storybook/react'; import * as React from 'react'; +import AnimatePoint from './components/AnimatePoint'; import Arc2DLineDemo from './components/Arc2DLine'; import ArcLineDemo from './components/Arcline'; +import CityBuildingLayerDemo from './components/citybuilding'; import Column from './components/column'; import DashLineDemo from './components/dash'; import DataUpdate from './components/data_update'; @@ -14,16 +16,20 @@ import PointImage from './components/PointImage'; import Polygon3D from './components/Polygon3D'; import ImageLayerDemo from './components/RasterImage'; import RasterLayerDemo from './components/RasterLayer'; +import TextLayerDemo from './components/Text'; // @ts-ignore storiesOf('图层', module) .add('点图层', () => ) .add('数据更新', () => ) .add('亮度图', () => ) + .add('点动画', () => ) .add('3D点', () => ) + .add('文字', () => ) .add('Column', () => ) .add('图片标注', () => ) .add('面3d图层', () => ) + .add('点亮城市', () => ) .add('线图层', () => ) .add('虚线', () => ) .add('3D弧线', () => ) diff --git a/stories/Layers/components/AnimatePoint.tsx b/stories/Layers/components/AnimatePoint.tsx new file mode 100644 index 0000000000..663a7788cf --- /dev/null +++ b/stories/Layers/components/AnimatePoint.tsx @@ -0,0 +1,67 @@ +import { PointLayer, Scene } from '@antv/l7'; +import { GaodeMap, Mapbox } from '@antv/l7-maps'; +import * as React from 'react'; +// @ts-ignore +import data from '../data/data.json'; +export default class AnimatePoint extends React.Component { + // @ts-ignore + private scene: Scene; + + public componentWillUnmount() { + this.scene.destroy(); + } + + public async componentDidMount() { + const scene = new Scene({ + id: 'map', + map: new GaodeMap({ + pitch: 0, + style: 'dark', + center: [112, 23.69], + zoom: 2.5, + }), + }); + + fetch( + 'https://gw.alipayobjects.com/os/basement_prod/9078fd36-ce8d-4ee2-91bc-605db8315fdf.csv', + ) + .then((res) => res.text()) + .then((data) => { + const pointLayer = new PointLayer({}) + .source(data, { + parser: { + type: 'csv', + x: 'Longitude', + y: 'Latitude', + }, + }) + .shape('circle') + .active(true) + .animate(true) + .size(40) + .color('#ffa842') + .style({ + opacity: 1, + }); + + scene.addLayer(pointLayer); + }); + + this.scene = scene; + } + + public render() { + return ( +
+ ); + } +} diff --git a/stories/Layers/components/Arc2DLine.tsx b/stories/Layers/components/Arc2DLine.tsx index ee39fda8ce..8253bf9228 100644 --- a/stories/Layers/components/Arc2DLine.tsx +++ b/stories/Layers/components/Arc2DLine.tsx @@ -1,5 +1,5 @@ import { LineLayer, Scene } from '@antv/l7'; -import { Mapbox } from '@antv/l7-maps'; +import { Mapbox, GaodeMap } from '@antv/l7-maps'; import * as React from 'react'; export default class Arc2DLineDemo extends React.Component { @@ -16,17 +16,14 @@ export default class Arc2DLineDemo extends React.Component { ); const scene = new Scene({ id: 'map', - map: new Mapbox({ + map: new GaodeMap({ center: [116.2825, 39.9], pitch: 0, - style: 'mapbox://styles/mapbox/dark-v9', + style: 'dark', zoom: 2, }), }); - const lineLayer = new LineLayer({ - enablePicking: true, - enableHighlight: true, - }) + const lineLayer = new LineLayer() .source(await response.text(), { parser: { type: 'csv', @@ -36,9 +33,17 @@ export default class Arc2DLineDemo extends React.Component { y1: 'lat2', }, }) - .size(0.5) - .shape('arc') - .color('rgb(13,64,140)'); + .size(1.2) + .shape('greatcircle') + .color('rgb(13,64,140)') + .animate({ + interval: 0.5, + duration: 2, + trailLength: 0.4, + }) + .style({ + opacity: 0.6, + }); scene.addLayer(lineLayer); scene.render(); this.scene = scene; diff --git a/stories/Layers/components/Arcline.tsx b/stories/Layers/components/Arcline.tsx index a746fbcc8c..9994416150 100644 --- a/stories/Layers/components/Arcline.tsx +++ b/stories/Layers/components/Arcline.tsx @@ -19,14 +19,13 @@ export default class ArcLineDemo extends React.Component { map: new Mapbox({ center: [116.2825, 39.9], pitch: 0, - style: 'mapbox://styles/mapbox/dark-v9', + style: 'dark', zoom: 2, }), }); this.scene = scene; const lineLayer = new LineLayer({ - enablePicking: true, - enableHighlight: true, + blend: 'normal', }) .source(await response.text(), { parser: { @@ -37,9 +36,18 @@ export default class ArcLineDemo extends React.Component { y1: 'lat2', }, }) - .size(0.5) - .shape('arc') - .color('rgb(13,64,140)'); + .size(1) + .shape('arc3d') + .active(true) + .color('rgb(13,64,140)') + .animate({ + interval: 0.1, + duration: 2, + trailLength: 1.0, + }) + .style({ + lineType: 'dash', + }); scene.addLayer(lineLayer); scene.render(); this.scene = scene; diff --git a/stories/Layers/components/Line.tsx b/stories/Layers/components/Line.tsx index ec09d0b702..40b48f4618 100644 --- a/stories/Layers/components/Line.tsx +++ b/stories/Layers/components/Line.tsx @@ -12,44 +12,34 @@ export default class LineDemo extends React.Component { public async componentDidMount() { const response = await fetch( - 'https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json', + 'https://arcgis.github.io/arcgis-samples-javascript/sample-data/custom-gl-animated-lines/lines.json', ); const scene = new Scene({ id: 'map', map: new Mapbox({ - center: [102.602992, 23.107329], - pitch: 0, - style: 'mapbox://styles/mapbox/dark-v9', - zoom: 13, + center: [-74.006, 40.7128], + zoom: 15, + style: 'dark', }), }); - const lineLayer = new LineLayer({ - enableMultiPassRenderer: true, - enablePicking: true, - enableHighlight: true, - // onHover: (pickedFeature: any) => { - // // tslint:disable-next-line:no-console - // console.log('Scene4', pickedFeature); - // }, - }) - .source(await response.json()) - .size(1) + const lineLayer = new LineLayer() + .source(await response.json(), { + parser: { + type: 'json', + coordinates: 'path', + }, + }) + .size(3) .shape('line') - .color( - 'ELEV', - [ - '#E8FCFF', - '#CFF6FF', - '#A1E9ff', - '#65CEF7', - '#3CB1F0', - '#2894E0', - '#1772c2', - '#105CB3', - '#0D408C', - '#002466', - ].reverse(), - ); + .color('color', (v) => { + return `rgb(${v[0]})`; + }); + // .animate({ + // enable: false, + // interval: 0.5, + // trailLength: 0.4, + // duration: 4, + // }) scene.addLayer(lineLayer); scene.render(); diff --git a/stories/Layers/components/Point.tsx b/stories/Layers/components/Point.tsx index 23642bcb1b..d0f41040a8 100644 --- a/stories/Layers/components/Point.tsx +++ b/stories/Layers/components/Point.tsx @@ -24,59 +24,59 @@ export default class Point3D extends React.Component { pitch: 0, style: 'dark', zoom: 3, - token: 'test', }), }); - scene.on('loaded', () => { - const pointLayer = new PointLayer({}) - .source(pointsData, { - cluster: true, - }) - .shape('circle') - .scale('point_count', { - type: 'quantile', - }) - .size('point_count', [5, 10, 15, 20, 25]) - .color('yellow') - .style({ - opacity: 0.5, - strokeWidth: 1, - }); - scene.addLayer(pointLayer); - pointLayer.on('mousemove', (e) => { - const id = e.featureId; - console.log(e.type); - pointLayer.setActive(id); + // scene.on('loaded', () => { + const pointLayer = new PointLayer({}) + .source(pointsData, { + cluster: true, + }) + .shape('circle') + .scale('point_count', { + type: 'quantile', + }) + .size('point_count', [5, 10, 15, 20, 25]) + .animate(false) + .color('yellow') + .style({ + opacity: 0.5, + strokeWidth: 1, }); - pointLayer.on('mousedown', (e) => { - const id = e.featureId; - console.log(e.type); - pointLayer.setActive(id); - }); - pointLayer.on('mouseup', (e) => { - const id = e.featureId; - console.log(e.type); - pointLayer.setActive(id); - }); - pointLayer.on('click', (e) => { - const id = e.featureId; - console.log(e.type); - pointLayer.setActive(id); - }); - - pointLayer.on('contextmenu', (e) => { - const id = e.featureId; - console.log(e.type); - pointLayer.setActive(id); - }); - pointLayer.on('unpick', (e) => { - const id = e.featureId; - console.log(e.type); - pointLayer.setActive(id); - }); - - this.scene = scene; + scene.addLayer(pointLayer); + pointLayer.on('mousemove', (e) => { + const id = e.featureId; + console.log(e.type); + pointLayer.setActive(id); }); + pointLayer.on('mousedown', (e) => { + const id = e.featureId; + console.log(e.type); + pointLayer.setActive(id); + }); + pointLayer.on('mouseup', (e) => { + const id = e.featureId; + console.log(e.type); + pointLayer.setActive(id); + }); + pointLayer.on('click', (e) => { + const id = e.featureId; + console.log(e.type); + pointLayer.setActive(id); + }); + + pointLayer.on('contextmenu', (e) => { + const id = e.featureId; + console.log(e.type); + pointLayer.setActive(id); + }); + pointLayer.on('unpick', (e) => { + const id = e.featureId; + console.log(e.type); + pointLayer.setActive(id); + }); + + this.scene = scene; + // }); } public render() { diff --git a/stories/Layers/components/Text.tsx b/stories/Layers/components/Text.tsx index fca96db54f..12bc4d9c21 100644 --- a/stories/Layers/components/Text.tsx +++ b/stories/Layers/components/Text.tsx @@ -1,61 +1,82 @@ import { PointLayer, Scene } from '@antv/l7'; +import { GaodeMap, Mapbox } from '@antv/l7-maps'; import * as React from 'react'; +// @ts-ignore import data from '../data/data.json'; -export default class Point3D extends React.Component { +export default class TextLayerDemo extends React.Component { + // @ts-ignore private scene: Scene; public componentWillUnmount() { this.scene.destroy(); } - public componentDidMount() { - const scene = new Scene({ - center: [120.19382669582967, 30.258134], - id: 'map', - pitch: 0, - type: 'mapbox', - style: 'mapbox://styles/mapbox/streets-v9', - zoom: 1, - }); - const pointLayer = new PointLayer({}); - const p1 = { + public async componentDidMount() { + const data = { type: 'FeatureCollection', features: [ { type: 'Feature', - properties: {}, + properties: { + name: '中华人民共和国', + }, geometry: { type: 'Point', - coordinates: [83.671875, 44.84029065139799], + coordinates: [103.0078125, 36.03133177633187], + }, + }, + { + type: 'Feature', + properties: { + name: '中华人民共和国', + }, + geometry: { + type: 'Point', + coordinates: [122.6953125, 10.833305983642491], }, }, ], }; - pointLayer - .source(data) - .color('name', [ - '#FFF5B8', - '#FFDC7D', - '#FFAB5C', - '#F27049', - '#D42F31', - '#730D1C', - ]) - .shape('subregion', [ - 'circle', - 'triangle', - 'square', - 'pentagon', - 'hexagon', - 'octogon', - 'hexagram', - 'rhombus', - 'vesica', - ]) - .size('scalerank', [2, 4, 6, 8, 10]); + const response = await fetch( + 'https://gw.alipayobjects.com/os/rmsportal/oVTMqfzuuRFKiDwhPSFL.json', + ); + const pointsData = await response.json(); + + const scene = new Scene({ + id: 'map', + map: new GaodeMap({ + center: [120.19382669582967, 30.258134], + pitch: 0, + style: 'dark', + zoom: 3, + }), + }); + // scene.on('loaded', () => { + const pointLayer = new PointLayer({}) + .source(pointsData.list, { + parser: { + type: 'json', + x: 'j', + y: 'w', + }, + }) + .shape('m', 'text') + .size(24) + .color('#fff') + .style({ + fontWeight: 800, + textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left + textOffset: [0, 0], // 文本相对锚点的偏移量 [水平, 垂直] + spacing: 2, // 字符间距 + padding: [4, 4], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近 + strokeColor: 'white', // 描边颜色 + strokeWidth: 4, // 描边宽度 + strokeOpacity: 1.0, + }); scene.addLayer(pointLayer); - scene.render(); + this.scene = scene; + // }); } public render() { diff --git a/stories/Layers/components/citybuilding.tsx b/stories/Layers/components/citybuilding.tsx new file mode 100644 index 0000000000..1b70dd6fc6 --- /dev/null +++ b/stories/Layers/components/citybuilding.tsx @@ -0,0 +1,60 @@ +import { CityBuildingLayer, Scene } from '@antv/l7'; +import { Mapbox } from '@antv/l7-maps'; +import * as React from 'react'; + +export default class CityBuildingLayerDemo extends React.Component { + // @ts-ignore + private scene: Scene; + + public componentWillUnmount() { + this.scene.destroy(); + } + + public async componentDidMount() { + const response = await fetch( + 'https://gw.alipayobjects.com/os/rmsportal/vmvAxgsEwbpoSWbSYvix.json', + ); + const scene = new Scene({ + id: 'map', + map: new Mapbox({ + style: 'dark', + center: [121.507674, 31.223043], + pitch: 65.59312320916906, + zoom: 15.4, + minZoom: 15, + maxZoom: 18, + }), + }); + const pointLayer = new CityBuildingLayer(); + pointLayer + .source(await response.json()) + .size('floor', [0, 500]) + .color('rgba(242,246,250,1.0)') + .animate({ + enable: true, + }) + .style({ + opacity: 1.0, + baseColor: 'rgb(16,16,16)', + windowColor: 'rgb(30,60,89)', + brightColor: 'rgb(255,176,38)', + }); + scene.addLayer(pointLayer); + scene.render(); + this.scene = scene; + } + public render() { + return ( +
+ ); + } +} diff --git a/stories/Layers/components/dash.tsx b/stories/Layers/components/dash.tsx index f06b9d94de..efa7055b94 100644 --- a/stories/Layers/components/dash.tsx +++ b/stories/Layers/components/dash.tsx @@ -1,4 +1,4 @@ -import { DashLineLayer, Scene } from '@antv/l7'; +import { LineLayer, Scene } from '@antv/l7'; import { Mapbox } from '@antv/l7-maps'; import * as React from 'react'; @@ -23,7 +23,7 @@ export default class DashLineDemo extends React.Component { zoom: 14, }), }); - const lineLayer = new DashLineLayer() + const lineLayer = new LineLayer() .source(await response.json()) .size(1) .shape('line') @@ -41,7 +41,10 @@ export default class DashLineDemo extends React.Component { '#0D408C', '#002466', ].reverse(), - ); + ) + .style({ + lineType: 'dash', + }); scene.addLayer(lineLayer); this.scene = scene; diff --git a/stories/Layers/components/data_update.tsx b/stories/Layers/components/data_update.tsx index 26e22e90db..9146b89291 100644 --- a/stories/Layers/components/data_update.tsx +++ b/stories/Layers/components/data_update.tsx @@ -48,7 +48,7 @@ export default class DataUpdate extends React.Component { .active(false) .color('#2F54EB') .style({ - strokeColor: '#fff', + stroke: '#fff', strokeWidth: 2, opacity: 1, }); diff --git a/stories/Layers/components/light.tsx b/stories/Layers/components/light.tsx index 045eb87dc9..0a987f72d8 100644 --- a/stories/Layers/components/light.tsx +++ b/stories/Layers/components/light.tsx @@ -27,7 +27,9 @@ export default class Light extends React.Component { }); this.scene = scene; scene.on('loaded', async () => { - const pointLayer = new PointLayer() + const pointLayer = new PointLayer({ + blend: 'normal', + }) .source(pointsData, { parser: { type: 'csv',