mirror of https://gitee.com/antv-l7/antv-l7
feat: 新增公告牌 geometry (#1091)
* chore: update version 2.8.30 -> 2.8.31 * feat: 新增公告牌图层 * style: lint style
This commit is contained in:
parent
bfe95589e2
commit
3e7efa04a8
|
@ -31,6 +31,7 @@ export default class GeometryLayer extends BaseLayer<
|
|||
const defaultConfig = {
|
||||
plane: {},
|
||||
sprite: {},
|
||||
billboard: {},
|
||||
};
|
||||
return defaultConfig[type];
|
||||
}
|
||||
|
@ -44,6 +45,8 @@ export default class GeometryLayer extends BaseLayer<
|
|||
return 'plane';
|
||||
} else if (shape === 'sprite') {
|
||||
return 'sprite';
|
||||
} else if (shape === 'billboard') {
|
||||
return 'billboard';
|
||||
} else {
|
||||
return 'plane';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
import {
|
||||
AttributeType,
|
||||
gl,
|
||||
IAttrubuteAndElements,
|
||||
IEncodeFeature,
|
||||
IModelUniform,
|
||||
ITexture2D,
|
||||
} from '@antv/l7-core';
|
||||
import { getMask, isMini } from '@antv/l7-utils';
|
||||
import BaseModel from '../../core/BaseModel';
|
||||
import { IGeometryLayerStyleOptions } from '../../core/interface';
|
||||
import planeFrag from '../shaders/billboard_frag.glsl';
|
||||
import planeVert from '../shaders/billboard_vert.glsl';
|
||||
|
||||
export default class BillBoardModel extends BaseModel {
|
||||
protected texture: ITexture2D;
|
||||
protected terrainImage: HTMLImageElement;
|
||||
protected terrainImageLoaded: boolean = false;
|
||||
protected mapTexture: string | undefined;
|
||||
private radian: number = 0; // 旋转的弧度
|
||||
|
||||
public planeGeometryTriangulation = () => {
|
||||
const {
|
||||
center = [120, 30],
|
||||
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
|
||||
return {
|
||||
size: 4,
|
||||
indices: [0, 1, 2, 2, 3, 0],
|
||||
vertices: [
|
||||
...center,
|
||||
...[1, 1],
|
||||
...center,
|
||||
...[0, 1],
|
||||
...center,
|
||||
...[0, 0],
|
||||
...center,
|
||||
...[1, 0],
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
public getUninforms(): IModelUniform {
|
||||
const {
|
||||
opacity,
|
||||
width = 1,
|
||||
height = 1,
|
||||
raisingHeight = 0,
|
||||
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
|
||||
|
||||
/**
|
||||
* rotateFlag
|
||||
* L7MAP 1
|
||||
* MAPBOX 1
|
||||
* GAODE2.x -1
|
||||
* GAODE1.x -1
|
||||
*/
|
||||
let rotateFlag = 1;
|
||||
if (
|
||||
this.mapService.version === 'GAODE2.x' ||
|
||||
this.mapService.version === 'GAODE1.x'
|
||||
) {
|
||||
rotateFlag = -1;
|
||||
}
|
||||
// 控制图标的旋转角度(绕 Z 轴旋转)
|
||||
this.radian =
|
||||
(rotateFlag * Math.PI * (this.mapService.getRotation() % 360)) / 180;
|
||||
|
||||
return {
|
||||
u_raisingHeight: Number(raisingHeight),
|
||||
u_RotateMatrix: new Float32Array([
|
||||
// z
|
||||
Math.cos(this.radian),
|
||||
Math.sin(this.radian),
|
||||
-Math.sin(this.radian),
|
||||
Math.cos(this.radian),
|
||||
]),
|
||||
u_opacity: opacity || 1,
|
||||
u_texture: this.texture,
|
||||
u_size: [width, height],
|
||||
};
|
||||
}
|
||||
|
||||
public clearModels(): void {
|
||||
// @ts-ignore
|
||||
this.terrainImage = null;
|
||||
this.texture?.destroy();
|
||||
}
|
||||
|
||||
public initModels() {
|
||||
const {
|
||||
mask = false,
|
||||
maskInside = true,
|
||||
mapTexture,
|
||||
drawCanvas,
|
||||
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
|
||||
this.mapTexture = mapTexture;
|
||||
|
||||
const { createTexture2D } = this.rendererService;
|
||||
this.texture = createTexture2D({
|
||||
height: 0,
|
||||
width: 0,
|
||||
});
|
||||
|
||||
if (drawCanvas) {
|
||||
this.updateTexture(drawCanvas);
|
||||
}
|
||||
|
||||
return [
|
||||
this.layer.buildLayerModel({
|
||||
moduleName: 'geometry_billboard',
|
||||
vertexShader: planeVert,
|
||||
fragmentShader: planeFrag,
|
||||
triangulation: this.planeGeometryTriangulation,
|
||||
primitive: gl.TRIANGLES,
|
||||
// primitive: gl.LINES,
|
||||
depth: { enable: true },
|
||||
blend: this.getBlend(),
|
||||
stencil: getMask(mask, maskInside),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
public buildModels() {
|
||||
return this.initModels();
|
||||
}
|
||||
|
||||
public updateTexture(drawCanvas: (canvas: HTMLCanvasElement) => void): void {
|
||||
const { createTexture2D } = this.rendererService;
|
||||
|
||||
const {
|
||||
canvasWidth = 1,
|
||||
canvasHeight = 1,
|
||||
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = canvasWidth;
|
||||
canvas.height = canvasHeight;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
drawCanvas(canvas);
|
||||
this.texture = createTexture2D({
|
||||
data: canvas,
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
wrapS: gl.CLAMP_TO_EDGE,
|
||||
wrapT: gl.CLAMP_TO_EDGE,
|
||||
});
|
||||
this.layerService.updateLayerRenderList();
|
||||
this.layerService.renderLayers();
|
||||
}
|
||||
}
|
||||
|
||||
protected getConfigSchema() {
|
||||
return {
|
||||
properties: {
|
||||
opacity: {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
maximum: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected registerBuiltinAttributes() {
|
||||
this.styleAttributeService.registerStyleAttribute({
|
||||
name: 'extrude',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Extrude',
|
||||
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 extrude = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0];
|
||||
const extrudeIndex = (attributeIdx % 4) * 3;
|
||||
return [
|
||||
extrude[extrudeIndex],
|
||||
extrude[extrudeIndex + 1],
|
||||
extrude[extrudeIndex + 2],
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
// point layer size;
|
||||
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,
|
||||
) => {
|
||||
return [vertex[2], vertex[3]];
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import BillBoardModel from './billboard';
|
||||
import PlaneModel from './plane';
|
||||
import SpriteModel from './sprite';
|
||||
export type GeometryModelType = 'plane' | 'sprite';
|
||||
export type GeometryModelType = 'plane' | 'sprite' | 'billboard';
|
||||
|
||||
const GeometryModels: { [key in GeometryModelType]: any } = {
|
||||
plane: PlaneModel,
|
||||
sprite: SpriteModel,
|
||||
billboard: BillBoardModel,
|
||||
};
|
||||
export default GeometryModels;
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_opacity;
|
||||
|
||||
varying vec3 v_Color;
|
||||
varying vec2 v_uv;
|
||||
|
||||
#pragma include "picking"
|
||||
void main() {
|
||||
gl_FragColor = texture2D(u_texture, vec2(v_uv.x, 1.0 - v_uv.y));
|
||||
gl_FragColor.a *= u_opacity;
|
||||
gl_FragColor = filterColor(gl_FragColor);
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
precision highp float;
|
||||
uniform mat4 u_ModelMatrix;
|
||||
uniform float u_raisingHeight: 0.0;
|
||||
uniform mat4 u_Mvp;
|
||||
uniform float u_opacity;
|
||||
uniform vec2 u_size: [1.0, 1.0];
|
||||
uniform mat2 u_RotateMatrix;
|
||||
|
||||
attribute vec3 a_Extrude;
|
||||
attribute vec3 a_Position;
|
||||
attribute vec2 a_Uv;
|
||||
attribute vec3 a_Color;
|
||||
|
||||
varying vec3 v_Color;
|
||||
varying vec2 v_uv;
|
||||
|
||||
#pragma include "projection"
|
||||
#pragma include "picking"
|
||||
void main() {
|
||||
vec3 extrude = a_Extrude;
|
||||
v_Color = a_Color;
|
||||
v_uv = a_Uv;
|
||||
|
||||
float raiseHeight = u_raisingHeight;
|
||||
if(u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT || u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET) {
|
||||
float mapboxZoomScale = 4.0/pow(2.0, 21.0 - u_Zoom);
|
||||
raiseHeight = u_raisingHeight * mapboxZoomScale;
|
||||
}
|
||||
|
||||
// 计算经纬度点位坐标
|
||||
vec4 project_pos = project_position(vec4(a_Position.xy, 0.0, 1.0));
|
||||
|
||||
// 计算绕 z 轴旋转后的偏移
|
||||
vec2 offsetXY = project_pixel(u_RotateMatrix * vec2(extrude.x * u_size.x, 0.0));
|
||||
// 绕 z 轴旋转
|
||||
float x = project_pos.x + offsetXY.x;
|
||||
float y = project_pos.y + offsetXY.y;
|
||||
// z 轴不参与旋转
|
||||
float z = project_pixel(extrude.y * u_size.y + raiseHeight);
|
||||
|
||||
|
||||
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
|
||||
// gl_Position = u_Mvp * (vec4(project_pos.xy, a_Position.z, 1.0));
|
||||
gl_Position = u_Mvp * (vec4(x, y, z, 1.0));
|
||||
} else {
|
||||
gl_Position = project_common_position_to_clipspace(vec4(x , y, z , 1.0));
|
||||
}
|
||||
|
||||
setPickingColor(a_PickingColor);
|
||||
}
|
|
@ -144,6 +144,12 @@ export interface IGeometryLayerStyleOptions {
|
|||
terrainClipHeight?: number;
|
||||
rgb2height?: (r: number, g: number, b: number) => number;
|
||||
|
||||
// billboard
|
||||
raisingHeight?: number; // 抬升高度
|
||||
canvasWidth?: number;
|
||||
canvasHeight?: number;
|
||||
drawCanvas?: (canvas: HTMLCanvasElement) => void;
|
||||
|
||||
// sprite
|
||||
spriteAnimate?: string;
|
||||
spriteRadius?: number;
|
||||
|
|
|
@ -2,7 +2,7 @@ import { container, ILayerPlugin, TYPES } from '@antv/l7-core';
|
|||
import CanvasLayer from './canvas';
|
||||
import CityBuildingLayer from './citybuliding/building';
|
||||
import BaseLayer from './core/BaseLayer';
|
||||
import GeometryLayer from './Geometry';
|
||||
import GeometryLayer from './Geometry'; // 逐步替换为 Geometry
|
||||
import './glsl.d';
|
||||
import HeatmapLayer from './heatmap';
|
||||
import ImageLayer from './image';
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
import { GeometryLayer, Scene, IMapService, PointLayer } from '@antv/l7';
|
||||
import { GaodeMap, GaodeMapV2, Mapbox } from '@antv/l7-maps';
|
||||
import * as React from 'react';
|
||||
|
||||
export default class Demo 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({
|
||||
// map: new GaodeMapV2({
|
||||
// map: new Mapbox({
|
||||
pitch: 80,
|
||||
style: 'dark',
|
||||
center: [120, 30],
|
||||
zoom: 5,
|
||||
}),
|
||||
});
|
||||
this.scene = scene;
|
||||
|
||||
let point = new PointLayer()
|
||||
.source(
|
||||
[
|
||||
{
|
||||
lng: 120,
|
||||
lat: 30,
|
||||
},
|
||||
],
|
||||
{
|
||||
parser: {
|
||||
type: 'json',
|
||||
x: 'lng',
|
||||
y: 'lat',
|
||||
},
|
||||
},
|
||||
)
|
||||
.shape('circle')
|
||||
.size(80)
|
||||
.animate(true)
|
||||
.color('#0ff');
|
||||
|
||||
let pointBar = new PointLayer({ depth: false })
|
||||
.source(
|
||||
[
|
||||
{
|
||||
lng: 120,
|
||||
lat: 30,
|
||||
},
|
||||
],
|
||||
{
|
||||
parser: {
|
||||
type: 'json',
|
||||
x: 'lng',
|
||||
y: 'lat',
|
||||
},
|
||||
},
|
||||
)
|
||||
.shape('cylinder')
|
||||
.size([5, 5, 60])
|
||||
.color('#0ff')
|
||||
.style({
|
||||
opacityLinear: {
|
||||
enable: true,
|
||||
dir: 'up',
|
||||
},
|
||||
opacity: 0.6,
|
||||
heightFixed: true,
|
||||
});
|
||||
|
||||
scene.on('loaded', () => {
|
||||
scene.addLayer(point);
|
||||
scene.addLayer(pointBar);
|
||||
|
||||
const img = new Image();
|
||||
img.crossOrigin = '';
|
||||
img.onload = () => {
|
||||
let billboard = new GeometryLayer().shape('billboard').style({
|
||||
width: 90,
|
||||
height: 30,
|
||||
canvasWidth: 360,
|
||||
canvasHeight: 120,
|
||||
center: [120, 30],
|
||||
drawCanvas: (canvas: HTMLCanvasElement) => {
|
||||
let { width, height } = canvas;
|
||||
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.drawImage(
|
||||
img,
|
||||
0,
|
||||
0,
|
||||
img.width,
|
||||
img.height,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
ctx.globalAlpha = 1;
|
||||
ctx.fillStyle = '#0ff';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.font = '36px Georgia';
|
||||
ctx.fillText('Hello World! 蚂蚁', width / 2, height / 2);
|
||||
},
|
||||
raisingHeight: 100,
|
||||
});
|
||||
billboard.active({
|
||||
color: '#0ff',
|
||||
mix: 0.5,
|
||||
});
|
||||
scene.addLayer(billboard);
|
||||
};
|
||||
img.src =
|
||||
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*zMw0T6gEIZYAAAAAAAAAAAAAARQnAQ';
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import Sprite from './components/sprite';
|
|||
import PlaneTerrain from './components/planeTerrain';
|
||||
import Cursor from './components/cursor';
|
||||
import Arrow from './components/arrow';
|
||||
import BillBoard from './components/billboard';
|
||||
|
||||
storiesOf('Object', module)
|
||||
.add('water', () => <Water />)
|
||||
|
@ -18,6 +19,7 @@ storiesOf('Object', module)
|
|||
.add('Radar', () => <Radar/>)
|
||||
.add('CanvasDemo', () => <CanvasDemo/>)
|
||||
.add('Plane', () => <Plane/>)
|
||||
.add('BillBoard', () => <BillBoard/>)
|
||||
.add('Sprite', () => <Sprite/>)
|
||||
.add('PlaneTerrain', () => <PlaneTerrain/>)
|
||||
.add('Cursor', () => <Cursor/>)
|
||||
|
|
Loading…
Reference in New Issue