mapboxGL中雷电动画展示
2020-04-11 本文已影响0人
牛老师讲GIS
概述
在网站ventusky中,点开降雨量图层,你会发现在降雨上面会有一些闪烁的点(小级别)或者闪烁的线(大级别),这些点或者线按照我的分析应该是根据降水相态(取雷阵雨)或者闪电数据叠加而成的,算是一个比较不错的亮点,本文参考此效果,结合github上的代码,在mapbox GL中进行了业务封装实现类似的效果。
效果
点 线说明: 因为数据是随机造的,所以效果上不如实际数据那么好;
实现
1、雷电数据
一般的雷电数据都是散点数据,会包含:经纬度、强度等信息,本文在做测试数据的时候只用了经纬度,数据格式如下:
var data = [
[112.08111460679623, 20.81427375298513],
[112.71805970936151, 20.09658100108571],
[113.9003630369973, 19.673781290716917],
[114.10018895152848, 21.004828899791406],
[115.36575307688554, 21.605996657108136],
[116.46479560680149, 22.01182925045798],
[116.34406745010693, 19.75608056730792]
];
2、工具封装
class Lightning {
constructor(c) {
this.config = c;
}
Cast(context, from, to, map) {
if (!from || !to) {
return;
}
//Main vector
var v = new Vector(from.X1, from.Y1, to.X1, to.Y1);
//skip cas if not close enough
if (this.config.Threshold && v.Length() > context.canvas.width * this.config.Threshold) {
return;
}
var vLen = v.Length();
var refv = from;
var lR = (vLen / context.canvas.width)
//count of segemnets
var segments = Math.floor(this.config.Segments * lR);
//lenth of each
var l = vLen / segments;
var zoom = map.getZoom();
if(zoom > 6) {
for (let i = 1; i <= segments; i++) {
//position in the main vector
var dv = v.Multiply((1 / segments) * i);
//add position noise
if (i != segments) {
dv.Y1 += l * Math.random();
dv.X1 += l * Math.random();
}
//new vector for segment
var r = new Vector(refv.X1, refv.Y1, dv.X1, dv.Y1);
//background blur
this.Line(context, r, {
Color: this.config.GlowColor,
With: this.config.GlowWidth * lR,
Blur: this.config.GlowBlur * lR,
BlurColor: this.config.GlowColor,
Alpha: this.Random(this.config.GlowAlpha, this.config.GlowAlpha * 2) / 100
});
//main line
this.Line(context, r, {
Color: this.config.Color,
With: this.config.Width,
Blur: this.config.Blur,
BlurColor: this.config.BlurColor,
Alpha: this.config.Alpha
});
refv = r;
}
} else {
this.Circle(context, to, lR);
}
}
Line(context, v, c) {
context.beginPath();
context.strokeStyle = c.Color;
context.lineWidth = c.With;
context.moveTo(v.X, v.Y);
context.lineTo(v.X1, v.Y1);
context.globalAlpha = c.Alpha;
context.shadowBlur = c.Blur;
context.shadowColor = c.BlurColor;
context.stroke();
}
Circle(context, p, lR) {
context.beginPath();
context.arc(p.X1 + Math.random() * 10 * lR, p.Y1 + Math.random() * 10 * lR, 5, 0, 2 * Math.PI, false);
context.fillStyle = 'white';
context.shadowBlur = 100;
context.shadowColor = "#2319FF";
context.fill();
}
Random(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
}
class Vector {
constructor(x, y, x1, y1) {
this.X = x;
this.Y = y;
this.X1 = x1;
this.Y1 = y1;
}
dX() {
return this.X1 - this.X;
}
dY() {
return this.Y1 - this.Y;
}
Normalized() {
var l = this.Length();
return new Vector(this.X, this.Y, this.X + (this.dX() / l), this.Y + (this.dY() / l));
}
Length() {
return Math.sqrt(Math.pow(this.dX(), 2) + Math.pow(this.dY(), 2));
}
Multiply(n) {
return new Vector(this.X, this.Y, this.X + this.dX() * n, this.Y + this.dY() * n);
}
Clone() {
return new Vector(this.x, this.y, this.X1, this.Y1);
}
}
var LightnMap = {
_map: null,
_data: [],
_canvas: null,
_context: null,
_options: {
Alpha: "0.5",
Blur: "5",
BlurColor: "white",
Color: "white",
GlowAlpha: "30",
GlowBlur: "10",
GlowColor: "#000055",
GlowWidth: "40",
Segments: "600",
Threshold: "0.5",
Width: "2"
},
_lts: [],
_animation: null,
_timer: 0,
init: function(map, data) {
const self = this;
self._map = map;
self._data = data;
let canvas = document.createElement('canvas');
canvas.id = 'lightCanvas';
canvas.width = map.getCanvas().width;
canvas.height = map.getCanvas().height;
canvas.style.position = 'absolute';
canvas.style.top = 0;
canvas.style.left = 0;
map.getCanvasContainer().appendChild(canvas);
self._context = canvas.getContext("2d");
self._canvas = canvas;
self._clearAndRestart();
map.on("dragstart", function() {
self._clear();
});
map.on("dragend", function() {
self._clearAndRestart();
});
map.on("zoomstart", function() {
self._clear();
});
map.on("zoomend", function() {
self._clearAndRestart();
});
map.on("resize", function() {
self._clear();
});
},
_getPoints: function(x, y, size) {
var self = this;
points = [];
var num = self._randomNum(1, 4);
for (var i = 0; i < num; i++) {
points.push(new Vector(
x + self._randomNum(-size, size),
y + self._randomNum(-size, size),
x + self._randomNum(-size, size),
y + self._randomNum(-size, size)));
}
return points;
},
_clearAndRestart: function() {
var self = this;
self._clear();
// 初始化雷电数据
self._lts = [];
for (var i = 0; i < self._data.length; i++) {
var d = self._data[i];
var xy = self._map.project(d);
var x = xy.x + self._randomNum(-50, 50);
var y = xy.y + self._randomNum(-50, 50);
var size = self._randomNum(20, 50);
var lt = {
lt: new Lightning(self._options),
x: x,
y: y,
size: size
};
self._lts.push(lt);
}
self._animation = window.requestAnimationFrame(function() {
self._Animate();
});
},
_Animate() {
var self = this;
self._clear();
for (var i = 0; i < self._data.length; i++) {
var lt = self._lts[i];
var x = lt.x;
var y = lt.y;
var size = lt.size;
var points = self._getPoints(x, y, size);
var target = new Vector(
x - size,
y - size,
x + self._randomNum(-10, 10),
y + self._randomNum(-10, 10)
);
points.forEach(p => {
lt.lt.Cast(self._context, p, target, self._map);
});
}
if (self._timer) clearTimeout(self._timer);
self._timer = setTimeout(() => {
self._Animate();
}, 100);
},
_randomNum(minNum, maxNum) {
switch (arguments.length) {
case 1:
return parseInt(Math.random() * minNum + 1, 10);
break;
case 2:
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
break;
default:
return 0;
break;
}
},
_clear: function() {
const self = this;
self._context.clearRect(0, 0, self._canvas.width, self._canvas.height);
if (self._animation) cancelAnimationFrame(self._animation);
if (self._timer) clearTimeout(self._timer);
},
destory: function() {
this._canvas.parentNode.removeChild(this._canvas);
}
}
3、调用实现
LightnMap.init(map, data);
技术博客
CSDN:http://blog.csdn.NET/gisshixisheng
联系方式
类型 | 内容 |
---|---|
1004740957 | |
公众号 | lzugis15 |
niujp08@qq.com | |
webgis群 | 452117357 |