import { now } from '../core/util';
import CanvasLayer, { CanvasLayerOptionsType } from './CanvasLayer';
import CanvasLayerRenderer from '../renderer/layer/canvaslayer/CanvasLayerRenderer';
import Point from '../geo/Point';
const TEMP_POINT = new Point(0, 0);
/**
* @property {Object} options - configuration options
* @property {Boolean} [options.animation=true] - if the layer is an animated layer
* @memberOf ParticleLayer
* @instance
*/
const options: ParticleLayerOptionsType = {
'animation': true
};
/**
* 粒子图层
* 提供了一些渲染粒子的接口方法。
* 你可以直接使用它,但不能以这种方式用JSON序列化/反序列化一个 particelayer
* 更建议使用子类来扩展它
*
* @english
* @classdesc
* A layer to draw particles.
* ParticleLayer provides some interface methods to render particles.
* You can use it directly, but can't serialize/deserialize a ParticleLayer with JSON in this way.
* It is more recommended to extend it with a subclass.
* @example
* import { ParticleLayer } from 'maptalks';
* var layer = new ParticleLayer('particle');
*
* layer.getParticles = function (t) {
* return particles[t];
* };
* layer.addTo(map);
* @category layer
* @extends CanvasLayer
* @param {String} id - layer's id
* @param {Object} [options=null] - options defined in [options]{@link ParticleLayer#options}
*/
class ParticleLayer extends CanvasLayer {
options: ParticleLayerOptionsType;
/**
* 获取t时刻的例子位置
*
* @english
* Interface method to get particles's position at time t.
* @param t - current time in milliseconds
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getParticles(t?: number) {
}
draw(context: CanvasRenderingContext2D, view: any) {
const points: any = this.getParticles(now());
if (!points || points.length === 0) {
const renderer = this._getRenderer();
if (renderer) {
this._getRenderer()._shouldClear = true;
}
return;
}
const map = this.getMap();
let extent = view.extent;
if (view.maskExtent) {
extent = view.extent.intersection(view.maskExtent);
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore 当前 map 接口中目前没有_pointToContainerPoint方法
extent = extent.convertTo(c => map._pointToContainerPoint(c, undefined, 0, TEMP_POINT));
const e = 2 * Math.PI;
for (let i = 0, l = points.length; i < l; i++) {
const pos = points[i].point;
if (extent.contains(pos)) {
const color = points[i].color || this.options['lineColor'] || '#fff',
r = points[i].r;
if (context.fillStyle !== color) {
context.fillStyle = color;
}
if (r <= 2) {
context.fillRect(pos.x - r / 2, pos.y - r / 2, r, r);
} else {
context.beginPath();
context.arc(pos.x, pos.y, r / 2, 0, e);
context.fill();
}
}
}
this._fillCanvas(context);
}
//@internal
_fillCanvas(context: CanvasRenderingContext2D) {
const g = context.globalCompositeOperation;
context.globalCompositeOperation = 'destination-out';
const trail = this.options['trail'] || 30;
context.fillStyle = 'rgba(0, 0, 0, ' + (1 / trail) + ')';
context.fillRect(0, 0, context.canvas.width, context.canvas.height);
context.globalCompositeOperation = g;
}
}
ParticleLayer.mergeOptions(options);
ParticleLayer.registerRenderer('canvas', class extends CanvasLayerRenderer {
//@internal
_shouldClear: boolean;
layer: ParticleLayer;
draw() {
if (!this.canvas || !this.layer.options['animation'] || this._shouldClear) {
this.prepareCanvas();
this._shouldClear = false;
}
this.prepareDrawContext();
this._drawLayer();
}
drawOnInteracting() {
this.draw();
this._shouldClear = false;
}
onSkipDrawOnInteracting() {
this._shouldClear = true;
}
});
export default ParticleLayer;
export type ParticleLayerOptionsType = CanvasLayerOptionsType & {
animation?: boolean;
}