* feat: 新增 canvasLayer、修复 mapbox 地图下 mapchange 事件失效

* style: lint style

* feat: 优化 canvasLayer show dragend 模式的事件

* style: lint style
This commit is contained in:
YiQianYao 2022-03-17 10:17:18 +08:00 committed by GitHub
parent c95839c137
commit 781b2586e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 306 additions and 2 deletions

View File

@ -63,6 +63,7 @@ export interface ILayerModelInitializationOptions {
export interface ILayerModel {
render(): void;
renderUpdate?(): void;
getUninforms(): IModelUniform;
getDefaultStyle(): unknown;
getAnimateUniforms(): IModelUniform;

View File

@ -0,0 +1,36 @@
import BaseLayer from '../core/BaseLayer';
import { ICanvasLayerStyleOptions } from '../core/interface';
import CanvasModels, { CanvasModelType } from './models/index';
export default class CanvasLayer extends BaseLayer<ICanvasLayerStyleOptions> {
public type: string = 'CanvasLayer';
public buildModels() {
const modelType = this.getModelType();
this.layerModel = new CanvasModels[modelType](this);
this.models = this.layerModel.initModels();
}
public rebuildModels() {
this.models = this.layerModel.buildModels();
}
protected getConfigSchema() {
return {
properties: {
opacity: {
type: 'number',
minimum: 0,
maximum: 1,
},
},
};
}
protected getDefaultConfig() {
const type = this.getModelType();
const defaultConfig = {
canvas: {},
};
return defaultConfig[type];
}
protected getModelType(): CanvasModelType {
return 'canvas';
}
}

View File

@ -0,0 +1,135 @@
import BaseModel from '../../core/BaseModel';
import {
CanvasUpdateType,
ICanvasLayerStyleOptions,
} from '../../core/interface';
export default class CanvaModel extends BaseModel {
protected updateMode: CanvasUpdateType = CanvasUpdateType.AWAYS;
protected canvas: HTMLCanvasElement;
protected ctx: CanvasRenderingContext2D;
protected prevSize: [number, number];
public renderUpdate = () => {
const {
zIndex = 10,
update = CanvasUpdateType.AWAYS,
} = this.layer.getLayerConfig() as ICanvasLayerStyleOptions;
if (+this.canvas.style.zIndex === zIndex) {
this.canvas.style.zIndex = zIndex + '';
}
if (this.updateMode !== update) {
this.updateMode = update as CanvasUpdateType;
this.unBindListener();
this.bindListener();
}
};
public unBindListener = () => {
this.mapService.off('mapchange', this.renderCanvas);
this.mapService.off('zoomstart', this.clearCanvas);
this.mapService.off('zoomend', this.renderCanvas);
this.mapService.off('movestart', this.clearCanvas);
this.mapService.off('moveend', this.renderCanvas);
};
public bindListener = () => {
if (this.updateMode === CanvasUpdateType.AWAYS) {
this.mapService.on('mapchange', this.renderCanvas);
} else {
this.mapService.on('zoomstart', this.clearCanvas);
this.mapService.on('zoomend', this.renderCanvas);
this.mapService.on('movestart', this.clearCanvas);
this.mapService.on('moveend', this.renderCanvas);
}
};
public clearModels(): void {
if (this.canvas) {
document.removeChild(this.canvas);
// @ts-ignore
this.canvas = null;
}
this.unBindListener();
}
public initModels() {
const {
update = CanvasUpdateType.AWAYS,
} = this.layer.getLayerConfig() as ICanvasLayerStyleOptions;
this.updateMode = update as CanvasUpdateType;
this.initCanvas();
this.renderCanvas();
this.bindListener();
this.mapService.getContainer();
return [];
}
public initCanvas(): void {
const { zIndex } = this.layer.getLayerConfig() as ICanvasLayerStyleOptions;
const size = this.mapService.getSize();
const [width, height] = size;
const {
width: viewWidth,
height: viewHeight,
} = this.rendererService.getViewportSize();
this.prevSize = [viewWidth, viewHeight];
const canvas = document.createElement('canvas');
this.canvas = canvas;
canvas.width = viewWidth;
canvas.height = viewHeight;
canvas.style.pointerEvents = 'none';
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
canvas.style.position = 'absolute';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.zIndex = zIndex + '';
this.mapService.getContainer()?.appendChild(canvas);
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
this.ctx = ctx;
}
public clearCanvas = () => {
if (this.ctx) {
const { width: w, height: h } = this.rendererService.getViewportSize();
this.ctx.clearRect(0, 0, w, h);
}
};
public renderCanvas = () => {
const {
width: viewWidth,
height: viewHeight,
} = this.rendererService.getViewportSize();
if (this.prevSize[0] !== viewWidth || this.prevSize[1] !== viewHeight) {
this.prevSize = [viewWidth, viewHeight];
const size = this.mapService.getSize();
const [width, height] = size;
this.canvas.width = viewWidth;
this.canvas.height = viewHeight;
this.canvas.style.width = width + 'px';
this.canvas.style.height = height + 'px';
}
const {
drawingOnCanvas,
} = this.layer.getLayerConfig() as ICanvasLayerStyleOptions;
if (this.ctx) {
drawingOnCanvas(this.ctx, this.mapService, [viewWidth, viewHeight]);
}
};
public buildModels() {
return this.initModels();
}
protected registerBuiltinAttributes() {
return;
}
}

View File

@ -0,0 +1,8 @@
import CanvasModel from './canvas';
export type CanvasModelType = 'canvas';
const CanvasModels: { [key in CanvasModelType]: any } = {
canvas: CanvasModel,
};
export default CanvasModels;

View File

@ -1118,6 +1118,10 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
this.hooks.beforeRender.call();
this.layerModelNeedUpdate = false;
}
if (this.layerModel.renderUpdate) {
this.layerModel.renderUpdate();
}
this.models.forEach((model) => {
model.draw(
{

View File

@ -1,4 +1,4 @@
import { IAnimateOption } from '@antv/l7-core';
import { IAnimateOption, IMapService } from '@antv/l7-core';
import { generateColorRamp, getMask, IColorRamp } from '@antv/l7-utils';
import { styleColor, styleOffset, styleSingle } from '../core/BaseModel';
import {
@ -106,6 +106,20 @@ export interface IImageLayerStyleOptions {
maskInside?: boolean;
}
export enum CanvasUpdateType {
'AWAYS' = 'aways',
'DRAGEND' = 'dragend',
}
export interface ICanvasLayerStyleOptions {
zIndex: number;
update: CanvasUpdateType | string;
drawingOnCanvas: (
ctx: CanvasRenderingContext2D,
mapService: IMapService,
size: [number, number],
) => void;
}
export interface IHeatMapLayerStyleOptions {
opacity: number;
intensity: number;

View File

@ -1,4 +1,5 @@
import { container, ILayerPlugin, TYPES } from '@antv/l7-core';
import CanvasLayer from './canvas';
import CityBuildingLayer from './citybuliding/building';
import BaseLayer from './core/BaseLayer';
import './glsl.d';
@ -141,6 +142,7 @@ export {
PolygonLayer,
LineLayer,
CityBuildingLayer,
CanvasLayer,
ImageLayer,
ImageTileLayer,
RasterLayer,

View File

@ -91,6 +91,7 @@ export default class L7EarthService implements IEarthService<Map> {
}
public off(type: string, handle: (...args: any[]) => void): void {
this.map.off(EventMap[type] || type, handle);
this.eventEmitter.off(type, handle);
}
public getContainer(): HTMLElement | null {

View File

@ -86,6 +86,7 @@ export default class L7MapService implements IMapService<Map> {
}
public off(type: string, handle: (...args: any[]) => void): void {
this.map.off(EventMap[type] || type, handle);
this.eventEmitter.off(type, handle);
}
public getContainer(): HTMLElement | null {

View File

@ -94,6 +94,7 @@ export default class MapboxService
}
public off(type: string, handle: (...args: any[]) => void): void {
this.map.off(EventMap[type] || type, handle);
this.eventEmitter.off(type, handle);
}
public getContainer(): HTMLElement | null {

View File

@ -0,0 +1,99 @@
import { CanvasLayer, Scene, IMapService } from '@antv/l7';
import { GaodeMap, 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 Mapbox({
pitch: 0,
// style: 'dark',
center: [96.99215001469588, 29.281597225674773],
zoom: 2.194613775109773,
}),
});
this.scene = scene;
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/basement_prod/337ddbb7-aa3f-4679-ab60-d64359241955.json',
)
.then((res) => res.json())
.then((data) => {
const layer = new CanvasLayer({}).style({
zIndex: 10,
update: 'aways',
// update: 'dragend',
drawingOnCanvas: (
ctx: CanvasRenderingContext2D,
mapService: IMapService,
size: [number, number],
) => {
const [width, height] = size;
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = 'rgba(0, 200, 0, 0.2)';
data.features.map((feature: any) => {
let pixelCenter = mapService.lngLatToContainer(
feature.geometry.coordinates,
);
pixelCenter.x *= window.devicePixelRatio;
pixelCenter.y *= window.devicePixelRatio;
if (
pixelCenter.x < 0 ||
pixelCenter.y < 0 ||
pixelCenter.x > width ||
pixelCenter.y > height
)
return;
ctx.beginPath();
ctx.arc(
pixelCenter.x,
pixelCenter.y,
feature.properties.capacity / 200,
0,
Math.PI * 2,
);
ctx.fill();
ctx.closePath();
});
},
});
scene.addLayer(layer);
setTimeout(() => {
console.log('reSet');
layer.style({
update: 'dragend',
});
scene.render();
}, 3000);
});
});
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -4,9 +4,11 @@ import Water from './components/water';
import Ocean from './components/ocean';
import Taifong from './components/taifeng'
import Radar from './components/radar';
import CanvasDemo from './components/canvas';
storiesOf('Object', module)
.add('water', () => <Water />)
.add('Ocean', () => <Ocean />)
.add('Taifong', () => <Taifong />)
.add('Radar', () => <Radar/>)
.add('Radar', () => <Radar/>)
.add('CanvasDemo', () => <CanvasDemo/>)