一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
讲解另一种常见应用-飞线动画,主要用于展示数据流向
1. 绘制单条飞线
这里曲线的绘制可以借助百度的曲线生成器:lbsyun.baidu.com/solutions/m…
<body>
<div id="map_container"></div>
<script>
const map = initBMap();
const data = initData();
setData(data, map);
// 初始化百度地图
function initBMap() {
// 引入的common库所作的处理
// mapv提供了api,根据名称获取坐标
const cityCenter = mapv.utilCityCenter.getCenterByCityName('上海');
const map = initMap({
center: [cityCenter.lng, cityCenter.lat],
zoom: 6,
style: purpleStyle,
tilt: 30
})
return map;
}
// 准备数据源
function initData() {
let data = [];
// 生成贝塞尔曲线坐标集 - 关键要素
// 1. 实例化贝塞尔曲线对象
const curve = new mapvgl.BezierCurve();
// 2. 设置起始和终点坐标
const start = mapv.utilCityCenter.getCenterByCityName('上海');
const end = mapv.utilCityCenter.getCenterByCityName('北京');
curve.setOptions({
start:[start.lng, start.lat],
end:[end.lng, end.lat]
});
// 3. 生成贝塞尔曲线坐标集
const curveData = curve.getPoints();
data.push({
geometry:{
type: 'LineString',
coordinates: curveData
}
})
return data;
}
// 绘制数据源
function setData(data, map) {
const view = new mapvgl.View({map});
// 初始化飞线对象并添加到图层中
const flyLine = new mapvgl.FlyLineLayer({
// 更多配置项可以看文档
color: 'red',
textureColor: 'blue',
textureWidth: 20,
textureLength: 80,
style: 'chaos',
step: 0.5
});
view.addLayer(flyLine);
flyLine.setData(data);
}
</script>
</body>
// 绘制数据源
function setData(data, map) {
const view = new mapvgl.View({map});
// 初始化飞线对象并添加到图层中
const flyLine = new mapvgl.FlyLineLayer({
// 更多配置项可以看文档
color: 'rgba(33,242,214,0.3)',
textureColor: '#ff0000',
textureWidth: 20,
textureLength: 10,
style: 'chaos',
step: 0.3
});
view.addLayer(flyLine);
flyLine.setData(data);
}
2. 增加更多的线
// 准备数据源
function initData() {
let data = [];
const cities = [
'北京', '天津', '上海', '重庆', '石家庄', '太原', '呼和浩特', '哈尔滨',
'长春', '沈阳', '济南', '南京', '合肥', '杭州', '南昌', '福州', '郑州',
'武汉', '长沙', '广州', '南宁', '西安', '银川', '兰州', '西宁', '乌鲁木齐',
'成都', '贵阳', '昆明', '拉萨', '海口'
];
let randomCount = 100;
// 生成贝塞尔曲线坐标集 - 关键要素
// 1. 实例化贝塞尔曲线对象
const curve = new mapvgl.BezierCurve();
while (randomCount--) {
// 2. 设置起始和终点坐标
const start = mapv.utilCityCenter.getCenterByCityName(cities[parseInt(Math.random() * cities.length)]);
const end = mapv.utilCityCenter.getCenterByCityName(cities[parseInt(Math.random() * cities.length)]);
curve.setOptions({
start: [start.lng, start.lat],
end: [end.lng, end.lat]
});
// 3. 生成贝塞尔曲线坐标集
const curveData = curve.getPoints();
data.push({
geometry: {
type: 'LineString',
coordinates: curveData
}
})
}
return data;
}
3. mapv示例移植mapvgl - 点汇聚线图
这里其实mapv(mapvgl的前身)有很多很好的飞线图案例可以参考,不过官方已经不维护了,但是我们还是可以去学习一下,例如这里:mapv.baidu.com/examples/#b…
点击源码后可以看到他的实现源码,不过需要注意script路径需要改成绝对路径,默认示例里面是相对路径:
上面案例是老的mapv版本,其实不建议学习,不会详细讲解代码。
mapv是基于canvas渲染的,移动视角时会卡顿,mapvgl基于webgl实现,不会有上述问题
但是我们可以整体移植到mapvgl里面去,这个图其实内容还是比较多,他其实是由移动的点加上静态的线构成的
实现静态线layer
这其中主要涉及到一个汇聚算法的移植,我们先实现一个多点汇聚北京的线,接着看一下有没有实现静态线的layer,这里可以基于LineLayer来实现:lbsyun.baidu.com/solutions/m…
<body>
<div id="map_container"></div>
<script>
const map = initBMap();
const data = initData();
setData(data, map);
// 初始化百度地图
function initBMap() {
// 引入的common库所作的处理
// mapv提供了api,根据名称获取坐标
const cityCenter = mapv.utilCityCenter.getCenterByCityName('北京');
const map = initMap({
center: [cityCenter.lng, cityCenter.lat],
zoom: 5,
style: purpleStyle,
tilt: 0
})
return map;
}
// 准备数据源
function initData() {
let data = [];
const cities = [
'北京', '天津', '上海'
];
let randomCount = 100;
const targetCity = mapv.utilCityCenter.getCenterByCityName('北京');
const curve = new mapvgl.BezierCurve();
for (let i = 1; i < cities.length; i++) {
const startCity = mapv.utilCityCenter.getCenterByCityName(cities[i]);
curve.setOptions({
start: [startCity.lng, startCity.lat],
end: [targetCity.lng, targetCity.lat]
})
const curveData = curve.getPoints();
data.push({
geometry: {
type: 'LineString',
coordinates: curveData
}
});
}
return data;
}
// 绘制数据源
function setData(data, map) {
const view = new mapvgl.View({ map });
// 初始化linelayer实现静态线
const lineLayer = new mapvgl.LineLayer({
color: 'rgba(55,50,250,0.3)'
});
view.addLayer(lineLayer);
lineLayer.setData(data);
}
</script>
</body>
实现移动点layer
这里需要注意移动点layer的轨迹其实是和静态线一致的,就是说背后的data是一样的,这里官网上没有找到layer,但是在示例demo里面我们找到一个轨迹图,里面由很多移动点:mapv.baidu.com/gl/examples…
他的背后其实是基于LinePointLayer实现的,但是官方文档没有这个Layer的信息,我们只能参考demo的做法来实现:
// 绘制数据源
function setData(data, map) {
const view = new mapvgl.View({ map });
// 初始化linelayer实现静态线
const lineLayer = new mapvgl.LineLayer({
color: 'rgba(55,50,250,0.3)'
});
const linePointLayer = new mapvgl.LinePointLayer({
size: 8, // 点大小
speed: 12, // 点运动速度
color: 'rgba(255, 255, 0, 0.6)',
animationType: mapvgl.LinePointLayer.ANIMATION_TYPE_SMOOTH, // 点动画类型
shapeType: mapvgl.LinePointLayer.SHAPE_TYPE_CIRCLE, //点形状
blend: 'lighter' // 交会时处理
});
view.addLayer(lineLayer);
view.addLayer(linePointLayer)
lineLayer.setData(data);
linePointLayer.setData(data);
}
实现更多的点
这里仍然基于随机数来生生成一些随机点汇聚目标:
// 准备数据源
function initData() {
let data = [];
const cities = [
'北京', '天津', '上海', '重庆', '石家庄', '太原', '呼和浩特', '哈尔滨',
'长春', '沈阳', '济南', '南京', '合肥', '杭州', '南昌', '福州', '郑州',
'武汉', '长沙', '广州', '南宁', '西安', '银川', '兰州', '西宁', '乌鲁木齐',
'成都', '贵阳', '昆明', '拉萨', '海口'
];
let randomCount = 500;
const targetCity = mapv.utilCityCenter.getCenterByCityName('北京');
const curve = new mapvgl.BezierCurve();
for (let i = 0; i < randomCount; i++) {
const startCity = mapv.utilCityCenter.getCenterByCityName(cities[parseInt(Math.random() * cities.length)]);
curve.setOptions({
start: [startCity.lng - 5 + 10 * Math.random(), startCity.lat - 5 + 10 * Math.random()],
end: [targetCity.lng, targetCity.lat]
})
const curveData = curve.getPoints();
data.push({
geometry: {
type: 'LineString',
coordinates: curveData
}
});
}
return data;
}
边绑定算法实现
这个算法可以帮助降低图的混乱程度,作一个边的汇聚功能,例如某个位置线特别临近,它可以帮助你汇聚在一起到达目的blog.csdn.net/gdp12315_gu…:
我们目前并没有实现边绑定,效果没有上面的示例比较好,下面我们来看看mapv的工具方法来作边绑定
// 准备数据源
function initData() {
let data = [];
const cities = [
'北京', '天津', '上海', '重庆', '石家庄', '太原', '呼和浩特', '哈尔滨',
'长春', '沈阳', '济南', '南京', '合肥', '杭州', '南昌', '福州', '郑州',
'武汉', '长沙', '广州', '南宁', '西安', '银川', '兰州', '西宁', '乌鲁木齐',
'成都', '贵阳', '昆明', '拉萨', '海口'
];
const targetCity = mapv.utilCityCenter.getCenterByCityName('北京');
let nodeData = [{
x: targetCity.lng,
y: targetCity.lat
}]; // 点
let edgeData = [{
source: 0, // 0表示的是nodeData的第0号元素
target: 0 // 0 - 0号元素的线
}]; // 边,表示点点关系
// 我们需要生成一系列node和edge数据
let randomCount = 500;
const curve = new mapvgl.BezierCurve();
for (let i = 0; i < randomCount; i++) {
const startCity = mapv.utilCityCenter.getCenterByCityName(cities[parseInt(Math.random() * cities.length)]);
nodeData.push({
x: startCity.lng + 5 - Math.random() * 10,
y: startCity.lat + 5 - Math.random() * 10
});
edgeData.push({
source: i + 1, // source为上面新push的node
target: 0
})
}
// 基于百度边绑定API获取需要的数据
const bundling = mapv.utilForceEdgeBundling().nodes(nodeData).edges(edgeData);
const results = bundling(); // 获取所有线沿途的点数据 - 一个二维数组
for (let i = 0; i < results.length; i++) {
const line = results[i];
const coordinates = [];
for (let j = 0; j < line.length; j++) {
coordinates.push([line[j].x, line[j].y])
}
data.push({
geometry: {
type: 'LineString',
coordinates
}
});
}
return data;
}
// 绘制数据源
function setData(data, map) {
const view = new mapvgl.View({ map });
// 初始化linelayer实现静态线
const lineLayer = new mapvgl.LineLayer({
color: 'rgba(55,50,250,0.5)'
});
const linePointLayer = new mapvgl.LinePointLayer({
size: 5, // 点大小
speed: 12, // 点运动速度
color: 'rgba(255, 255, 0, 0.6)',
animationType: mapvgl.LinePointLayer.ANIMATION_TYPE_SMOOTH, // 点动画类型
shapeType: mapvgl.LinePointLayer.SHAPE_TYPE_CIRCLE, //点形状
blend: 'lighter' // 交会时处理
});
view.addLayer(lineLayer);
view.addLayer(linePointLayer)
lineLayer.setData(data);
linePointLayer.setData(data);
}
再加上汇聚效果
// 初始化linelayer实现静态线
const lineLayer = new mapvgl.LineLayer({
color: 'rgba(55,50,250,0.5)',
blend: 'lighter'
});
边绑定算法实际应用还是经常使用到的