feat(layer): add blend 效果配置支持 normal,additive

This commit is contained in:
thinkinggis 2019-12-18 21:13:56 +08:00
parent 84d78cdda7
commit 4bb8c65a46
25 changed files with 248 additions and 55 deletions

View File

@ -24,6 +24,10 @@ new Layer(option)
## 配置项
### name
设置图层名称,可根据 name 获取 layer;
### visable
图层是否可见   {bool } default true
@ -40,6 +44,15 @@ new Layer(option)
图层显示最大缩放等级 0-18   {number}  default 18
### blend
图层元素混合效果
- normal 正常效果 默认
- additive 叠加模式
- subtractive 相减模式
- max 最大值
# 方法
### source
@ -284,6 +297,13 @@ scene.render();
layer.setData(data);
```
### setBlend(type)
设置图层叠加方法
参数:
- type blend 类型
## 图层控制方法
### show
@ -422,3 +442,13 @@ layer.on('unmousedown', (ev) => {}); // 图层外单击按下时触发
layer.on('uncontextmenu', (ev) => {}); // 图层外点击右键
layer.on('unpick', (ev) => {}); // 图层外的操作的所有事件
```
## 图层事件
### inited
图层初始化完成后触发
### remove
图层移除时触发

View File

@ -50,6 +50,7 @@ const defaultLayerConfig: Partial<ILayerConfig> = {
visible: true,
autoFit: false,
zIndex: 0,
blend: 'normal',
pickedFeatureID: -1,
enableMultiPassRenderer: true,
enablePicking: true,

View File

@ -3,7 +3,11 @@ import { SyncBailHook, SyncHook, SyncWaterfallHook } from 'tapable';
import Clock from '../../utils/clock';
import { ISceneConfig } from '../config/IConfigService';
import { IMapService } from '../map/IMapService';
import { IModel, IModelInitializationOptions } from '../renderer/IModel';
import {
IBlendOptions,
IModel,
IModelInitializationOptions,
} from '../renderer/IModel';
import {
IMultiPassRenderer,
IPass,
@ -22,6 +26,16 @@ import {
StyleAttributeOption,
Triangulation,
} from './IStyleAttributeService';
export enum BlendType {
normal = 'normal',
additive = 'additive',
subtractive = 'subtractive',
min = 'min',
max = 'max',
}
export interface IBlendTypes {
[key: string]: Partial<IBlendOptions>;
}
export interface IDataState {
dataSourceNeedUpdate: boolean;
dataMappingNeedUpdate: boolean;
@ -38,6 +52,7 @@ export interface ILayerModelInitializationOptions {
export interface ILayerModel {
render(): void;
getUninforms(): IModelUniform;
getBlend(): IBlendOptions;
buildModels(): IModel[];
}
export interface IModelUniform {
@ -57,7 +72,8 @@ export interface IActiveOption {
export interface ILayer {
id: string; // 一个场景中同一类型 Layer 可能存在多个
name: string; // 代表 Layer 的类型
type: string; // 代表 Layer 的类型
name: string; //
inited: boolean; // 是否初始化完成
zIndex: number;
plugins: ILayerPlugin[];
@ -120,6 +136,7 @@ export interface ILayer {
isVisible(): boolean;
setMaxZoom(min: number): ILayer;
setMinZoom(max: number): ILayer;
setBlend(type: keyof typeof BlendType): void;
// animate(field: string, option: any): ILayer;
render(): ILayer;
destroy(): void;
@ -190,6 +207,8 @@ export interface ILayerConfig {
visible: boolean;
zIndex: number;
autoFit: boolean;
name: string; //
blend: keyof typeof BlendType;
pickedFeatureID: number;
enableMultiPassRenderer: boolean;
passes: Array<string | [string, { [key: string]: unknown }]>;

View File

@ -38,8 +38,8 @@ export default class LayerService implements ILayerService {
return this.layers;
}
public getLayer(id: string): ILayer | undefined {
return this.layers.find((layer) => layer.id === id);
public getLayer(name: string): ILayer | undefined {
return this.layers.find((layer) => layer.name === name);
}
public remove(layer: ILayer): void {
@ -47,6 +47,7 @@ export default class LayerService implements ILayerService {
if (layerIndex > -1) {
this.layers.splice(layerIndex, 1);
}
layer.emit('remove', null);
layer.destroy();
this.renderLayers();
}

View File

@ -4,7 +4,7 @@ import { ILogService } from './ILogService';
const Logger = new Log({ id: 'L7' }).enable(true);
// // 只输出 debug 级别以上的日志信息
Logger.priority = 4;
Logger.priority = 5;
@injectable()
export default class LogService implements ILogService {

View File

@ -3,6 +3,30 @@ import { IAttribute } from './IAttribute';
import { IElements } from './IElements';
import { IUniform } from './IUniform';
export interface IBlendOptions {
// gl.enable(gl.BLEND)
enable: boolean;
// gl.blendFunc
func: BlendingFunctionSeparate;
// gl.blendEquation
equation: {
// TODO: EXT_blend_minmax
rgb:
| gl.FUNC_ADD
| gl.FUNC_SUBTRACT
| gl.FUNC_REVERSE_SUBTRACT
| gl.MIN_EXT
| gl.MAX_EXT;
alpha?:
| gl.FUNC_ADD
| gl.FUNC_SUBTRACT
| gl.FUNC_REVERSE_SUBTRACT
| gl.MIN_EXT
| gl.MAX_EXT;
};
// gl.blendColor
color: [number, number, number, number];
}
type stencilOp =
| gl.ZERO
| gl.KEEP
@ -153,20 +177,7 @@ export interface IModelInitializationOptions {
/**
* blending
*/
blend?: Partial<{
// gl.enable(gl.BLEND)
enable: boolean;
// gl.blendFunc
func: BlendingFunctionSeparate;
// gl.blendEquation
equation: {
// TODO: EXT_blend_minmax
rgb: gl.FUNC_ADD | gl.FUNC_SUBTRACT | gl.FUNC_REVERSE_SUBTRACT;
alpha: gl.FUNC_ADD | gl.FUNC_SUBTRACT | gl.FUNC_REVERSE_SUBTRACT;
};
// gl.blendColor
color: [number, number, number, number];
}>;
blend?: Partial<IBlendOptions>;
/**
* stencil
@ -221,6 +232,8 @@ export interface IModelDrawOptions {
[key: string]: IAttribute;
};
elements?: IElements;
blend?: IBlendOptions;
}
/**

View File

@ -60,6 +60,10 @@ export enum gl {
FUNC_SUBTRACT = 0x800a,
FUNC_REVERSE_SUBTRACT = 0x800b,
/** Blend Min Max */
MAX_EXT = 0x8008,
MIN_EXT = 0x8007,
/* Separate Blend Functions */
BLEND_DST_RGB = 0x80c8,
BLEND_SRC_RGB = 0x80c9,

View File

@ -1,4 +1,5 @@
import {
BlendType,
gl,
IActiveOption,
IAnimateOption,
@ -34,10 +35,9 @@ import {
ScaleTypes,
StyleAttributeField,
StyleAttributeOption,
TYPES,
TYPES
} from '@antv/l7-core';
import Source from '@antv/l7-source';
import { bindAll } from '@antv/l7-utils';
import { EventEmitter } from 'eventemitter3';
import { Container } from 'inversify';
import { isFunction, isObject } from 'lodash';
@ -45,6 +45,7 @@ import { isFunction, isObject } from 'lodash';
import mergeJsonSchemas from 'merge-json-schemas';
import { SyncBailHook, SyncHook, SyncWaterfallHook } from 'tapable';
import { normalizePasses } from '../plugins/MultiPassRendererPlugin';
import { BlendTypes } from '../utils/blend';
import baseLayerSchema from './schema';
/**
* layer id
@ -55,6 +56,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
implements ILayer {
public id: string = `${layerIdCounter++}`;
public name: string;
public type: string;
public visible: boolean = true;
public zIndex: number = 0;
public minZoom: number;
@ -165,6 +167,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
constructor(config: Partial<ILayerConfig & ChildLayerStyleOptions> = {}) {
super();
this.name = config.name || this.id;
this.rawConfig = config;
}
@ -299,6 +302,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
this.buildModels();
// 触发初始化完成事件;
this.emit('inited');
this.emit('added');
return this;
}
@ -514,10 +518,18 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
this.interactionService.triggerSelect(id);
}
}
public setBlend(type: keyof typeof BlendType): void {
this.updateLayerConfig({
blend: type,
});
this.layerModelNeedUpdate = true;
this.render();
}
public show(): ILayer {
this.updateLayerConfig({
visible: true,
});
return this;
}
@ -685,15 +697,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
fs,
vs,
elements,
blend: {
enable: true,
func: {
srcRGB: gl.SRC_ALPHA,
srcAlpha: 1,
dstRGB: gl.ONE_MINUS_SRC_ALPHA,
dstAlpha: 1,
},
},
blend: BlendTypes[BlendType.normal],
...rest,
});
}

View File

@ -1,5 +1,7 @@
import {
BlendType,
IAttribute,
IBlendOptions,
ICameraService,
IElements,
IFontService,
@ -17,7 +19,7 @@ import {
Triangulation,
TYPES,
} from '@antv/l7-core';
import { BlendTypes } from '../utils/blend';
export default class BaseModel implements ILayerModel {
public triangulation: Triangulation;
@ -54,7 +56,10 @@ export default class BaseModel implements ILayerModel {
.get<ICameraService>(TYPES.ICameraService);
this.registerBuiltinAttributes();
}
public getBlend(): IBlendOptions {
const { blend = 'normal' } = this.layer.getLayerConfig();
return BlendTypes[BlendType[blend]] as IBlendOptions;
}
public getUninforms(): IModelUniform {
throw new Error('Method not implemented.');
}

View File

@ -5,7 +5,7 @@ interface IPointLayerStyleOptions {
opacity: number;
}
export default class HeatMapLayer extends BaseLayer<IPointLayerStyleOptions> {
public name: string = 'HeatMapLayer';
public type: string = 'HeatMapLayer';
protected getConfigSchema() {
return {
properties: {

View File

@ -10,7 +10,7 @@ interface IDashLineLayerStyleOptions {
export default class DashLineLayer extends BaseLayer<
IDashLineLayerStyleOptions
> {
public name: string = 'LineLayer';
public type: string = 'LineLayer';
protected getConfigSchema() {
return {

View File

@ -4,7 +4,7 @@ interface IPointLayerStyleOptions {
opacity: number;
}
export default class LineLayer extends BaseLayer<IPointLayerStyleOptions> {
public name: string = 'LineLayer';
public type: string = 'LineLayer';
private animateStartTime: number = 0;

View File

@ -7,7 +7,7 @@ interface IPointLayerStyleOptions {
strokeColor: string;
}
export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
public name: string = 'PointLayer';
public type: string = 'PointLayer';
protected getConfigSchema() {
return {
properties: {

View File

@ -1,5 +1,6 @@
import {
AttributeType,
BlendType,
gl,
IEncodeFeature,
IModel,
@ -8,9 +9,9 @@ import {
import { rgb2arr } from '@antv/l7-utils';
import BaseModel from '../../core/BaseModel';
import { BlendTypes } from '../../utils/blend';
import normalFrag from '../shaders/normal_frag.glsl';
import normalVert from '../shaders/normal_vert.glsl';
interface IPointLayerStyleOptions {
opacity: number;
strokeWidth: number;
@ -38,7 +39,6 @@ export default class NormalModel extends BaseModel {
u_stroke_color: rgb2arr(strokeColor),
};
}
public buildModels(): IModel[] {
return [
this.layer.buildLayerModel({
@ -48,15 +48,7 @@ export default class NormalModel extends BaseModel {
triangulation: PointTriangulation,
depth: { enable: false },
primitive: gl.POINTS,
blend: {
enable: true,
func: {
srcRGB: gl.ONE,
srcAlpha: 1,
dstRGB: gl.ONE,
dstAlpha: 1,
},
},
blend: this.getBlend(),
}),
];
}

View File

@ -26,7 +26,7 @@ export function PointTriangulation(feature: IEncodeFeature) {
};
}
export default class TextLayer extends BaseLayer<IPointTextLayerStyleOptions> {
public name: string = 'PointLayer';
public type: string = 'PointLayer';
protected getConfigSchema() {
return {

View File

@ -7,7 +7,7 @@ interface IPolygonLayerStyleOptions {
}
export default class PolygonLayer extends BaseLayer<IPolygonLayerStyleOptions> {
public name: string = 'PolygonLayer';
public type: string = 'PolygonLayer';
protected getConfigSchema() {
return {

View File

@ -14,7 +14,7 @@ interface IPointLayerStyleOptions {
}
export default class ImageLayer extends BaseLayer<IPointLayerStyleOptions> {
public name: string = 'ImageLayer';
public type: string = 'ImageLayer';
protected texture: ITexture2D;
protected getConfigSchema() {

View File

@ -26,7 +26,7 @@ interface IRasterLayerStyleOptions {
}
export default class RasterLayer extends BaseLayer<IRasterLayerStyleOptions> {
public name: string = 'e';
public type: string = 'RasterLayer';
protected texture: ITexture2D;
protected colorTexture: ITexture2D;

View File

@ -12,7 +12,7 @@ interface IRasterLayerStyleOptions {
}
export default class Raster2dLayer extends BaseLayer<IRasterLayerStyleOptions> {
public name: string = 'RasterLayer';
public type: string = 'RasterLayer';
protected texture: ITexture2D;
protected colorTexture: ITexture2D;

View File

@ -0,0 +1,54 @@
import { BlendType, gl, IBlendOptions, IBlendTypes } from '@antv/l7-core';
export const BlendTypes: IBlendTypes = {
[BlendType.additive]: {
enable: true,
func: {
srcRGB: gl.ONE,
dstRGB: gl.ONE,
srcAlpha: 1,
dstAlpha: 1,
},
},
[BlendType.normal]: {
enable: true,
func: {
srcRGB: gl.SRC_ALPHA,
dstRGB: gl.ONE_MINUS_SRC_ALPHA,
srcAlpha: 1,
dstAlpha: 1,
},
},
[BlendType.subtractive]: {
enable: true,
func: {
srcRGB: gl.ONE,
dstRGB: gl.ONE,
srcAlpha: gl.ZERO,
dstAlpha: gl.ONE_MINUS_SRC_COLOR,
},
equation: {
rgb: gl.FUNC_SUBTRACT,
alpha: gl.FUNC_SUBTRACT,
},
},
[BlendType.max]: {
enable: true,
func: {
srcRGB: gl.ONE,
dstRGB: gl.ONE,
},
equation: {
rgb: gl.MAX_EXT,
},
},
[BlendType.min]: {
enable: true,
func: {
srcRGB: gl.ONE,
dstRGB: gl.ONE,
},
equation: {
rgb: gl.MIN_EXT,
},
},
};

View File

@ -204,7 +204,7 @@ export default class MapboxService
*/
// 判断全局 mapboxgl 对象的加载
if (!('mapInstance' in this.config) && !mapboxgl) {
if (!mapInstance && !mapboxgl) {
// 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。
this.logger.error(this.configService.getSceneWarninfo('SDK'));
}
@ -213,13 +213,13 @@ export default class MapboxService
token === MAPBOX_API_KEY &&
style !== 'blank' &&
!mapboxgl.accessToken &&
!('mapInstance' in this.config) // 如果用户传递了 mapInstance应该不去干预实例的 accessToken。
!mapInstance // 如果用户传递了 mapInstance应该不去干预实例的 accessToken。
) {
this.logger.warn(this.configService.getSceneWarninfo('MapToken'));
}
// 判断是否设置了 accessToken
if ('mapInstance' in this.config && !mapboxgl.accessToken) {
if (!mapInstance && !mapboxgl.accessToken) {
// 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。
mapboxgl.accessToken = token;
}

View File

@ -141,6 +141,8 @@ export const blendEquationMap: {
[key: string]: regl.BlendingEquation;
} = {
[gl.FUNC_ADD]: 'add',
[gl.MIN_EXT]: 'min',
[gl.MAX_EXT]: 'max',
[gl.FUNC_SUBTRACT]: 'subtract',
[gl.FUNC_REVERSE_SUBTRACT]: 'reverse subtract',
};

View File

@ -53,6 +53,7 @@ export default class ReglRendererService implements IRendererService {
extensions: [
'OES_element_index_uint',
// 'EXT_shader_texture_lod', // IBL 兼容性问题
'EXT_blend_minmax',
'OES_standard_derivatives', // wireframe
// 'OES_texture_float', // shadow map 兼容性问题
'WEBGL_depth_texture',

View File

@ -5,6 +5,7 @@ import ArcLineDemo from './components/Arcline';
import Column from './components/column';
import DataUpdate from './components/data_update';
import HeatMapDemo from './components/HeatMap';
import LightDemo from './components/light';
import LineLayer from './components/Line';
import PointDemo from './components/Point';
import Point3D from './components/Point3D';
@ -17,6 +18,7 @@ import RasterLayerDemo from './components/RasterLayer';
storiesOf('图层', module)
.add('点图层', () => <PointDemo />)
.add('数据更新', () => <DataUpdate />)
.add('亮度图', () => <LightDemo />)
.add('3D点', () => <Point3D />)
.add('Column', () => <Column />)
.add('图片标注', () => <PointImage />)

View File

@ -0,0 +1,65 @@
import { PointLayer, Scene } from '@antv/l7';
import { GaodeMap, Mapbox } from '@antv/l7-maps';
import * as React from 'react';
// @ts-ignore
export default class Light extends React.Component {
// @ts-ignore
private scene: Scene;
public componentWillUnmount() {
this.scene.destroy();
}
public async componentDidMount() {
const response = await fetch(
'https://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/antvdemo/assets/city/bj.csv',
);
const pointsData = await response.text();
const scene = new Scene({
id: 'map',
map: new Mapbox({
pitch: 0,
style: 'dark',
center: [116.405289, 39.904987],
zoom: 6,
}),
});
this.scene = scene;
scene.on('loaded', async () => {
const pointLayer = new PointLayer({
blend: 'min',
})
.source(pointsData, {
parser: {
type: 'csv',
x: 'lng',
y: 'lat',
},
})
.size(2)
.color("#FFFECC")
.style({
opacity: 1,
});
scene.addLayer(pointLayer);
this.scene = scene;
});
}
public render() {
return (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}