应公司要求,在发生告警时,告警会自动推送给2公里范围内人员,而地图上需要展示2公里范围距离覆盖了哪些人员,因此需要在百度地图上实现2公里警覆盖效果。查阅了百度地图API发现,存咋BMap.Circle这个对象可实现绘圆形覆盖,如下:
实现代码:
//绘制覆盖物*****************************************
var circle = new BMap.Circle(point, //中心点,这个点是marker的点
2000, //半径,单位米
{
fillColor:"blue", //圆形填充颜色
fillOpacity: 0.2, //填充透明度
strokeWeight: 1 ,
strokeColor:"blue", //线条颜色,为了保证感觉无线条,和填充颜色一致即可
strokeOpacity: 0.2, //线条透明度,为了保证感觉无线条,和填充颜色透明度一致即可
enableEditing:false
});
map.addOverlay(circle);
但这样只是静态的,如果想做成动态的水波扩散效果怎么办?查阅百度地图circle类的API,有如下几个方法:
然后就想到如果想做成动态的,就可以通过JS动画去实现这个效果,每一帧改变一下circle半径即可。然后多个circle覆盖在一起,按时间差进行绘制,向外扩散时又同步缩减透明度,即可实现水波纹效果。
自己编写了一个js如下:
let requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame;
let cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame;
/**
* sos告警圆形范围绘制(只有存在map对象时才可以使用)
* @param radius 半径
* @param level 层数
* @param point BMap.Point对象,圆的中心点
* @param color 颜色对象,包含{fillColor,fillOpacity}
* @constructor
*/
function CircleShow(radius,level,point,color){
if(!window.map || !window.BMap|| !window.BMap.Circle){
return undefined;
}
this.radius = radius;
this.level = new Number(level);
this.point = point;
this.color = color;
if(Number.isNaN(this.level)){
this.level = 1;
}//至少1层
if(!this.color || !this.color.fillColor){
this.color = {
fillColor:"blue",//默认蓝色
fillOpacity:0.5 //默认初始透明度0.5
}
}
//计算平均每段扩展距离的透明度
this.endOpacity = 0.1; //终止透明度设置为0.1
this.speedOpacity = (this.color.fillOpacity - this.endOpacity)/this.radius; //每米的透明度
//先加一层白色的覆盖物,加在图片上表示覆盖范围
this.circle0 = new BMap.Circle(this.point,this.radius,{
fillColor:"white",
fillOpacity: 0.2,
strokeWeight: 1 ,
strokeColor:"white",
strokeOpacity: 0.2,
enableEditing:false
});
map.addOverlay(this.circle0);
//按层数循环构造覆盖物,并加在图片上
this.circles = new Array();
for(let i=1; i< this.level; i++){
let circle = new BMap.Circle(this.point,0,{
fillColor:this.color.fillColor,
fillOpacity: this.color.fillOpacity,
strokeWeight: 1,
strokeColor:this.color.fillColor,
strokeOpacity: this.color.fillOpacity,
enableEditing:false
});
this.circles.push(circle);
map.addOverlay(circle);
}
this.clock=new Array(this.level);
}
/**
* 动画启动
* @param distance 波纹间隔时间(单位ms)
* @param t0 扩散一次所需的时间
*/
CircleShow.prototype.start = function (distance,t0){
let _self = this;
/**
* 定义动画函数
* @param startTime 启动的初始时间
* @param circle 圆形覆盖物对象
* @param index 序号
*/
function animateStart(startTime,circle,index){
//计算时间差
let time = new Date().getTime()-startTime;
if(time<0){
circle.setRadius(0); //半径
circle.setFillOpacity(_self.color.fillColor); //透明度
circle.setStrokeOpacity(_self.color.fillOpacity); //透明度
//如果未达到执行实现则直接等待
_self.clock[index] = window.requestAnimationFrame(animateStart.bind(null,startTime,circle,index));
return;
}
//计算当前半径
//匀减速运动下,每隔t时间,应该扩散的半径:
//r=r0*(2*t*t0-t*t)/t0
//其中,r0为最终的扩散半径,即this.radius
let r = Math.floor(_self.radius*(2*time/t0-time*time/t0/t0));
let opacity = 0;
if(time >= t0){
//达到运行时间之后
//设置圆形覆盖物的样式
circle.setRadius(_self.radius); //半径
circle.setFillOpacity(_self.endOpacity); //透明度
circle.setStrokeOpacity(_self.endOpacity); //透明度
startTime = new Date().getTime() + distance; //起始时间设置为当前时间加上1倍的时间间隔
_self.clock[index] = window.requestAnimationFrame(animateStart.bind(null,startTime,circle,index));
}else{
//计算透明度
let opacity = _self.color.fillOpacity -
Number.parseFloat((_self.speedOpacity * r).toFixed(5)); //四舍五入小数点后5位
//设置圆形覆盖物的样式
circle.setRadius(r); //半径
circle.setFillOpacity(opacity); //透明度
circle.setStrokeOpacity(opacity); //透明度
_self.clock[index] = window.requestAnimationFrame(animateStart.bind(null,startTime,circle,index));
}
}
//循环每一层执行动画
for (let [index,circle] of this.circles.entries()) {
animateStart(new Date().getTime()+index*distance, circle, index);
}
};
/**
* 停止动画.
*/
CircleShow.prototype.stop = function(){
for(let caf of this.clock){
window.cancelAnimationFrame(caf);
}
//重置覆盖物样式
for(let circle of this.circles){
circle.setRadius(0); //半径
circle.setFillOpacity(this.color.fillOpacity); //透明度
circle.getStrokeOpacity(this.color.fillOpacity); //透明度
}
this.clock=null;
};
/**
* 移除覆盖物.
*/
CircleShow.prototype.remove = function(){
//停止动画
for(let caf of this.clock){
window.cancelAnimationFrame(caf);
}
map.removeOverlay(this.circle0);
for(let circle of this.circles){
map.removeOverlay(circle);
}
};
然后,在主代码中,直接使用如下代码即可:
//加载告警动画
//参数:
//半径、层数、中心点、{填充颜色、初始透明度}
var circles = new CircleShow(2000, 4, point, {fillColor:'blue',fillOpacity:0.5});
//参数:每一层播放的间隔时间、每一层扩散至最大所花费的总时间。
circles.start(1500,5000);
//circels.stop(); //停止动画,并重置为初始状态;
//circels.remove(); //移除动画
说明:我这里往外扩散采用的是匀加速运动方式,可以设置为匀速运动或者使用贝塞尔曲线公式去计算,大家可以自己玩。
最终的效果如下: