From 56f6f8fe718796001cc6729cbfd636056c0ff10f Mon Sep 17 00:00:00 2001 From: 2912401452 <2912401452@qq.com> Date: Wed, 16 Jun 2021 21:41:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=88=86=E6=9E=90=EF=BC=88=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E8=B5=8B=E5=80=BC/=E7=BA=B9=E7=90=86=E4=BC=A0=E5=80=BC?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/src/services/layer/LayerService.ts | 1 + .../core/src/services/scene/SceneService.ts | 1 + packages/layers/src/core/BaseLayer.ts | 11 ++ .../layers/src/plugins/DataMappingPlugin.ts | 2 + .../plugins/RegisterStyleAttributePlugin.ts | 10 +- .../src/plugins/UpdateStyleAttributePlugin.ts | 1 + packages/layers/src/point/models/fill.ts | 93 +++++++++++-- .../layers/src/point/shaders/fill_frag.glsl | 25 +++- .../layers/src/point/shaders/fill_vert.glsl | 12 +- packages/layers/src/utils/dataMappingStyle.ts | 130 ++++++++++++++++-- stories/Map/components/amap2demo.tsx | 15 +- 11 files changed, 260 insertions(+), 41 deletions(-) diff --git a/packages/core/src/services/layer/LayerService.ts b/packages/core/src/services/layer/LayerService.ts index e5792471c6..3ae5d29d6a 100644 --- a/packages/core/src/services/layer/LayerService.ts +++ b/packages/core/src/services/layer/LayerService.ts @@ -79,6 +79,7 @@ export default class LayerService implements ILayerService { .filter((layer) => layer.inited) .filter((layer) => layer.isVisible()) .forEach((layer) => { + // trigger hooks layer.hooks.beforeRenderData.call(); layer.hooks.beforeRender.call(); diff --git a/packages/core/src/services/scene/SceneService.ts b/packages/core/src/services/scene/SceneService.ts index 8e87893db4..768f8b4f10 100644 --- a/packages/core/src/services/scene/SceneService.ts +++ b/packages/core/src/services/scene/SceneService.ts @@ -267,6 +267,7 @@ export default class Scene extends EventEmitter implements ISceneService { // 尝试初始化未初始化的图层 this.layerService.renderLayers(); + // 组件需要等待layer 初始化完成之后添加 this.logger.debug(`scene ${this.id} render`); this.rendering = false; diff --git a/packages/layers/src/core/BaseLayer.ts b/packages/layers/src/core/BaseLayer.ts index 0692b13f7c..087c567315 100644 --- a/packages/layers/src/core/BaseLayer.ts +++ b/packages/layers/src/core/BaseLayer.ts @@ -53,6 +53,7 @@ import mergeJsonSchemas from 'merge-json-schemas'; import { normalizePasses } from '../plugins/MultiPassRendererPlugin'; import { BlendTypes } from '../utils/blend'; import baseLayerSchema from './schema'; +import { handleStyleOpacity, handleStyleStrokeOpacity } from '../utils/dataMappingStyle' /** * 分配 layer id */ @@ -206,6 +207,16 @@ export default class BaseLayer extends EventEmitter }; } else { const sceneId = this.container.get(TYPES.SceneID); + // @ts-ignore + if(configToUpdate.opacity){ + // @ts-ignore + handleStyleOpacity('opacity', this, configToUpdate.opacity); + } + // @ts-ignore + if(configToUpdate.strokeOpacity){ + // @ts-ignore + handleStyleStrokeOpacity('strokeOpacity', this, configToUpdate.strokeOpacity); + } this.configService.setLayerConfig(sceneId, this.id, { ...this.configService.getLayerConfig(this.id), ...this.needUpdateConfig, diff --git a/packages/layers/src/plugins/DataMappingPlugin.ts b/packages/layers/src/plugins/DataMappingPlugin.ts index 7ee9a6c231..b15e4513b7 100644 --- a/packages/layers/src/plugins/DataMappingPlugin.ts +++ b/packages/layers/src/plugins/DataMappingPlugin.ts @@ -83,6 +83,7 @@ export default class DataMappingPlugin implements ILayerPlugin { // 处理文本更新 layer.emit('remapping', null); } + }); } private generateMaping( @@ -174,6 +175,7 @@ export default class DataMappingPlugin implements ILayerPlugin { }); } } + // console.log('mappedData', mappedData) return mappedData; } diff --git a/packages/layers/src/plugins/RegisterStyleAttributePlugin.ts b/packages/layers/src/plugins/RegisterStyleAttributePlugin.ts index 3200e80ab1..7eadb5e9c3 100644 --- a/packages/layers/src/plugins/RegisterStyleAttributePlugin.ts +++ b/packages/layers/src/plugins/RegisterStyleAttributePlugin.ts @@ -92,10 +92,10 @@ export default class RegisterStyleAttributePlugin implements ILayerPlugin { }, }); styleAttributeService.registerStyleAttribute({ - name: 'opacity', + name: 'strokeOpacity', type: AttributeType.Attribute, descriptor: { - name: 'a_Opacity', + name: 'a_stroke_opacity', buffer: { // give the WebGL driver a hint that this buffer may change usage: gl.DYNAMIC_DRAW, @@ -104,10 +104,10 @@ export default class RegisterStyleAttributePlugin implements ILayerPlugin { }, size: 1, update: (feature: IEncodeFeature, featureIdx: number) => { - const { opacity } = feature; + const { strokeOpacity } = feature; // console.log('feature', feature) - // console.log('opacity', opacity) - return !isNumber(opacity) ? [1.0] : [opacity]; + // console.log('strokeOpacity', strokeOpacity) + return !isNumber(strokeOpacity) ? [1.0] : [strokeOpacity]; }, }, }); diff --git a/packages/layers/src/plugins/UpdateStyleAttributePlugin.ts b/packages/layers/src/plugins/UpdateStyleAttributePlugin.ts index 9ec4f14e96..536e2ac3f4 100644 --- a/packages/layers/src/plugins/UpdateStyleAttributePlugin.ts +++ b/packages/layers/src/plugins/UpdateStyleAttributePlugin.ts @@ -31,6 +31,7 @@ export default class UpdateStyleAttributePlugin implements ILayerPlugin { // }); layer.hooks.beforeRender.tap('UpdateStyleAttributePlugin', () => { + if (layer.layerModelNeedUpdate) { return; } diff --git a/packages/layers/src/point/models/fill.ts b/packages/layers/src/point/models/fill.ts index 3e8fb23377..3cb36c2912 100644 --- a/packages/layers/src/point/models/fill.ts +++ b/packages/layers/src/point/models/fill.ts @@ -8,6 +8,7 @@ import { ILayerConfig, IModel, IModelUniform, + ITexture2D } from '@antv/l7-core'; import { rgb2arr } from '@antv/l7-utils'; import BaseModel from '../../core/BaseModel'; @@ -15,8 +16,8 @@ import { PointFillTriangulation } from '../../core/triangulation'; import pointFillFrag from '../shaders/fill_frag.glsl'; import pointFillVert from '../shaders/fill_vert.glsl'; +import { getSize, getUvPosition, initTextureData, initDefaultTextureData } from '../../utils/dataMappingStyle' import { isNumber } from 'lodash'; -import { handleStyleOpacity } from '../../utils/dataMappingStyle'; interface IPointLayerStyleOptions { opacity: any; strokeWidth: number; @@ -25,29 +26,61 @@ interface IPointLayerStyleOptions { offsets: [number, number]; } +interface IDataLayout { + widthCount: number; + heightCount: number; + widthStep: number; + widthStart: number; + heightStep: number; + heightStart: number; +} + // 用于判断 opacity 的值是否发生该改变 let curretnOpacity: any = ''; +let curretnStrokeOpacity: any = '' + export default class FillModel extends BaseModel { + + protected opacityTexture: ITexture2D; + + public dataLayout: IDataLayout = { // 默认值 + widthCount: 1024, + heightCount: 1, + widthStep: 1/1024, + widthStart: 1/2048, + heightStep: 1, + heightStart: 0.5 + }; + public getUninforms(): IModelUniform { const { - opacity = 1.0, + opacity, stroke = 'rgb(0,0,0,0)', strokeWidth = 1, strokeOpacity = 1, offsets = [0, 0], } = this.layer.getLayerConfig() as IPointLayerStyleOptions; - // style 中 opacity 属性的处理(数据映射) - if (curretnOpacity !== JSON.stringify(opacity)) { - handleStyleOpacity(this.layer, opacity); + if ( curretnOpacity !== JSON.stringify(opacity)) { + const { createTexture2D } = this.rendererService; + // 从 encodeData 数据的 opacity 字段上取值,并将值按照排布写入到纹理中 + this.opacityTexture = initTextureData(this.dataLayout.heightCount, createTexture2D, this.layer.getEncodedData(), 'opacity') + curretnOpacity = JSON.stringify(opacity); + } + + + if(curretnStrokeOpacity !== JSON.stringify(strokeOpacity)) { + + curretnStrokeOpacity = JSON.stringify(strokeOpacity) } return { - u_opacity: isNumber(opacity) ? opacity : 1.0, + u_opacity_texture: this.opacityTexture, + u_opacity: opacity ? -1.0 : 1.0, u_stroke_width: strokeWidth, u_stroke_color: rgb2arr(stroke), - u_stroke_opacity: strokeOpacity, + u_stroke_opacity: isNumber(strokeOpacity)?strokeOpacity: 1.0, u_offsets: [-offsets[0], offsets[1]], }; } @@ -70,8 +103,21 @@ export default class FillModel extends BaseModel { PointFillTriangulation, ); } + + public initEncodeDataLayout(dataLength: number) { + let { width: widthCount, height: heightCount } = getSize(dataLength) + this.dataLayout.widthCount = widthCount + this.dataLayout.heightCount = heightCount + + this.dataLayout.widthStep = 1/widthCount + this.dataLayout.widthStart = this.dataLayout.widthStep/2 + this.dataLayout.heightStep = 1/heightCount + this.dataLayout.heightStart = this.dataLayout.heightStep/2 + } + public initModels(): IModel[] { - this.getUninforms(); + + this.initEncodeDataLayout(this.layer.getEncodedData().length) return this.buildModels(); } @@ -91,7 +137,6 @@ export default class FillModel extends BaseModel { return [option.enable ? 0 : 1.0, option.speed || 1, option.rings || 3, 0]; } protected registerBuiltinAttributes() { - const { opacity } = this.layer.getLayerConfig() as IPointLayerStyleOptions; this.styleAttributeService.registerStyleAttribute({ name: 'extrude', type: AttributeType.Attribute, @@ -137,11 +182,41 @@ export default class FillModel extends BaseModel { attributeIdx: number, ) => { const { size = 5 } = feature; + // console.log('featureIdx', featureIdx, feature) return Array.isArray(size) ? [size[0]] : [size as number]; }, }, }); + // point feature id + this.styleAttributeService.registerStyleAttribute({ + name: 'featureId', + type: AttributeType.Attribute, + descriptor: { + name: 'a_featureId', + 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 getUvPosition( + this.dataLayout.widthStep, + this.dataLayout.widthStart, + this.dataLayout.heightStep, + this.dataLayout.heightStart, + featureIdx) + }, + }, + }); + // point layer size; this.styleAttributeService.registerStyleAttribute({ name: 'shape', diff --git a/packages/layers/src/point/shaders/fill_frag.glsl b/packages/layers/src/point/shaders/fill_frag.glsl index 5be16fbc3e..9c81d88913 100644 --- a/packages/layers/src/point/shaders/fill_frag.glsl +++ b/packages/layers/src/point/shaders/fill_frag.glsl @@ -5,10 +5,13 @@ uniform float u_opacity : 1; uniform float u_stroke_width : 1; uniform vec4 u_stroke_color : [0, 0, 0, 0]; uniform float u_stroke_opacity : 1; +varying float v_stroke_opacity; + +uniform sampler2D u_opacity_texture; +varying vec2 v_featureId; varying vec4 v_data; varying vec4 v_color; -varying float v_opacity; varying float v_radius; uniform float u_time; uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ]; @@ -18,6 +21,17 @@ uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ]; void main() { int shape = int(floor(v_data.w + 0.5)); + // 处理透明度 + // float opacity = texture2D(u_opacity_texture, v_featureId).a?texture2D(u_opacity_texture, v_featureId).a:1.0; + float opacity = u_opacity; + if(u_opacity < 0.0) { + opacity = texture2D(u_opacity_texture, v_featureId).a; + } + + float stroke_opacity = u_stroke_opacity; + if(v_stroke_opacity > 0.0) { + stroke_opacity = v_stroke_opacity; + } lowp float antialiasblur = v_data.z; float antialiased_blur = -max(u_blur, antialiasblur); @@ -68,13 +82,8 @@ void main() { // gl_FragColor = v_color * color_t; // gl_FragColor = mix(vec4(v_color.rgb, v_color.a * u_opacity), strokeColor * u_stroke_opacity, color_t); - // gl_FragColor = mix(vec4(v_color.rgb, v_color.a * v_opacity), strokeColor * u_stroke_opacity, color_t); + gl_FragColor = mix(vec4(v_color.rgb, v_color.a * opacity), strokeColor * stroke_opacity, color_t); - if(v_opacity < 0.0) { // style 中的 opacity 为 number - gl_FragColor = mix(vec4(v_color.rgb, v_color.a * u_opacity), strokeColor * u_stroke_opacity, color_t); - } else { // style 中的 opacity 为 string | function - gl_FragColor = mix(vec4(v_color.rgb, v_color.a * v_opacity), strokeColor * u_stroke_opacity, color_t); - } gl_FragColor.a = gl_FragColor.a * opacity_t; if(u_aimate.x == Animate) { @@ -84,4 +93,6 @@ void main() { } 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 da4ff8dd5a..147dd756c6 100644 --- a/packages/layers/src/point/shaders/fill_vert.glsl +++ b/packages/layers/src/point/shaders/fill_vert.glsl @@ -2,8 +2,12 @@ attribute vec4 a_Color; attribute vec3 a_Position; attribute vec2 a_Extrude; attribute float a_Size; -attribute float a_Opacity; +attribute float a_stroke_opacity; attribute float a_Shape; + +attribute vec2 a_featureId; +varying vec2 v_featureId; + uniform mat4 u_ModelMatrix; uniform mat4 u_Mvp; @@ -13,7 +17,7 @@ uniform vec2 u_offsets; varying vec4 v_data; varying vec4 v_color; varying float v_radius; -varying float v_opacity; +varying float v_stroke_opacity; #pragma include "projection" #pragma include "picking" @@ -25,7 +29,9 @@ void main() { // unpack color(vec2) v_color = a_Color; - v_opacity = a_Opacity; + v_stroke_opacity = a_stroke_opacity; + + v_featureId = a_featureId; // radius(16-bit) v_radius = newSize; diff --git a/packages/layers/src/utils/dataMappingStyle.ts b/packages/layers/src/utils/dataMappingStyle.ts index 38e1a8eec4..5787d2d6a4 100644 --- a/packages/layers/src/utils/dataMappingStyle.ts +++ b/packages/layers/src/utils/dataMappingStyle.ts @@ -1,5 +1,6 @@ import { ILayer, + ITexture2D, IStyleAttributeUpdateOptions, StyleAttributeField, StyleAttributeOption, @@ -9,6 +10,9 @@ import { isArray, isFunction, isNumber, isString } from 'lodash'; * 该文件中的工具方法主要用于对 style 中的属性进行 数据映射 */ +// 画布默认的宽度 +const WIDTH = 1024 + /** * 当 style 中使用的 opacity 不是常数的时候根据数据进行映射 * @param field @@ -16,30 +20,31 @@ import { isArray, isFunction, isNumber, isString } from 'lodash'; * @param updateOptions */ function registerOpacityAttribute( - layer: ILayer, - field: StyleAttributeField, - values?: StyleAttributeOption, - updateOptions?: Partial, + fieldName: string, + layer: ILayer, + field: StyleAttributeField, + values?: StyleAttributeOption, + updateOptions?: Partial, ) { - layer.updateStyleAttribute('opacity', field, values, updateOptions); + layer.updateStyleAttribute(fieldName, field, values, updateOptions); } /** * 根据传入参数 opacity 的类型和值做相应的操作 */ -function handleStyleOpacity(layer: ILayer, opacity: any) { +function handleStyleOpacity(fieldName: string, layer: ILayer, opacity: any) { if (isString(opacity)) { // opacity = 'string' - registerOpacityAttribute(layer, opacity, (value: any) => { + registerOpacityAttribute(fieldName, layer, opacity, (value: any) => { return value; }); } else if (isNumber(opacity)) { - // opacity = 0.4 -> opacity 传入数字、u_Opacity 生效、v_Opacity 不生效 - registerOpacityAttribute(layer, [-1], undefined); + // opacity = 0.4 -> opacity 传入数字 + registerOpacityAttribute(fieldName, layer, [opacity], undefined); } else if (isArray(opacity) && opacity.length === 2) { if (isString(opacity[0]) && isFunction(opacity[1])) { // opacity = ['string', callback] - registerOpacityAttribute(layer, opacity[0], opacity[1]); + registerOpacityAttribute(fieldName, layer, opacity[0], opacity[1]); } else if ( isString(opacity[0]) && isArray(opacity[1]) && @@ -47,13 +52,110 @@ function handleStyleOpacity(layer: ILayer, opacity: any) { isNumber(opacity[1][1]) ) { // opacity = ['string', [start: number, end: nuber]] - registerOpacityAttribute(layer, opacity[0], opacity[1]); + registerOpacityAttribute(fieldName, layer, opacity[0], opacity[1]); } else { - registerOpacityAttribute(layer, [1.0], undefined); + registerOpacityAttribute(fieldName, layer, [1.0], undefined); } } else { - registerOpacityAttribute(layer, [1.0], undefined); + registerOpacityAttribute(fieldName, layer, [1.0], undefined); } } -export { handleStyleOpacity }; +/** + * 根据传入参数 strokeOpacity 的类型和值做相应的操作 + */ +function handleStyleStrokeOpacity(fieldName: string, layer: ILayer, strokeOpacity: any) { + handleStyleOpacity(fieldName, layer, strokeOpacity) +} + +/** + * 根据输入的 feature 的长度以及默认的宽度计算画布的大小 + * @param encodeDatalength + * @returns + */ +function getSize(encodeDatalength: number) { + let width = WIDTH; + let height = Math.ceil(encodeDatalength / width); + return { width, height } +} + +/** + * 根据输入的宽高边距信息,为需要为 index 的 feature 计算在画布上对应的 uv 值 + * @param widthStep + * @param widthStart + * @param heightStep + * @param heightStart + * @param id + * @returns + */ +function getUvPosition(widthStep: number, widthStart: number, heightStep: number, heightStart: number, index: number) { + // index 从零开始 + let row = Math.ceil((index + 1)/WIDTH); // 当前 index 所在的行 + let column = (index + 1) % WIDTH + if(column === 0) { // 取余等于零 + column = WIDTH + } + let u = widthStart + (column - 1) * widthStep + let v = 1 - (heightStart + (row - 1) * heightStep) + return [u, v] +} + +/** + * 1、根据输入的 field 字段从 originData 中取值 (style 样式用于数据映射的值) + * 2、根据输入的 heightCount 以及默认的 WIDTH 为纹理对象提供数据 + * 3、根据输入的 createTexture2D 构建纹理对象 + * @param heightCount + * @param createTexture2D + * @param originData + * @param field + * @returns + */ +function initTextureData(heightCount: number, createTexture2D: any, originData: any, field: string): ITexture2D { + let d = [] + for(let i = 0; i < WIDTH * heightCount; i++) { + if(originData[i] && originData[i][field]) { + let v = originData[i][field] * 255 + d.push( v, v, v, v ) + }else { + d.push( 0, 0, 0, 0 ) + } + } + let arr = new Uint8ClampedArray(d) + let imageData = new ImageData(arr, WIDTH, heightCount) // (arr, width, height) + + let texture = createTexture2D({ + flipY: true, + data: new Uint8Array(imageData.data), + width: imageData.width, + height: imageData.height + }); + + return texture +} + +function initDefaultTextureData(heightCount: number, createTexture2D: any): ITexture2D { + let d = [] + for(let i = 0; i < WIDTH * heightCount; i++) { + d.push( 255, 255, 255, 255 ) + } + let arr = new Uint8ClampedArray(d) + let imageData = new ImageData(arr, WIDTH, heightCount) // (arr, width, height) + + let texture = createTexture2D({ + flipY: true, + data: new Uint8Array(imageData.data), + width: imageData.width, + height: imageData.height + }); + + return texture +} + +export { + handleStyleOpacity, + handleStyleStrokeOpacity, + getSize, + getUvPosition, + initTextureData, + initDefaultTextureData +}; diff --git a/stories/Map/components/amap2demo.tsx b/stories/Map/components/amap2demo.tsx index ccb75c0567..8070fa73ca 100644 --- a/stories/Map/components/amap2demo.tsx +++ b/stories/Map/components/amap2demo.tsx @@ -26,16 +26,19 @@ export default class Amap2demo extends React.Component { lng: 121.107846, lat: 30.267069, opacity2: 0.2, + strokeOpacity2: 0.2 }, { lng: 121.107, lat: 30.267069, opacity2: 0.4, + strokeOpacity2: 0.4 }, { lng: 121.107846, lat: 30.26718, opacity2: 0.6, + strokeOpacity2: 0.6 }, // { // lng: 38.54, @@ -60,15 +63,21 @@ export default class Amap2demo extends React.Component { .color('rgba(255, 0, 0, 0.9)') .size(10) .style({ - stroke: '#fff', + stroke: '#000', storkeWidth: 2, + // strokeOpacity: 0.2, + // strokeOpacity: 'strokeOpacity2', + strokeOpacity: ['strokeOpacity2', (d: any) => { + return d + }], + // strokeOpacity: ['opacity2', [0.2, 0.6]], // offsets: [100, 100], - // opacity: 'opacity2' + opacity: 'opacity2' // opacity: 0.2 // opacity: ['opacity2', (d: any) => { // return d // }] - opacity: ['opacity2', [0.2, 0.6]], + // opacity: ['opacity2', [0.2, 0.6]], }) .active(true); scene.addLayer(layer);