Mapboxgl + TweenJS 实现图层属性的动画效果(地块抬升)-爱代码爱编程
(1)主要过程:使用统一的数据源,通过mapboxgl.setFilter方法对选中的图层进行筛选;在图层属性变化的过程中(setPaintProperty)使用TweenJS动画补间实现动态变化
(2)效果:
(3)代码
HTML 、CSS:
<!DOCTYPE html>
<html lang="ZH-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>mapbox地块点击抬升动画效果</title>
<link rel="stylesheet" href="../lib/js/geoglobe/mapbox-gl.css" type="text/css" />
<script type="text/javascript" src="../lib/js/geoglobe/mapbox-gl.js"></script>
<script type="text/javascript" src="../lib/js/tween/tween.umd.js"></script>
</head>
<style>
html,
body {
height: 100vh;
width: 100%;
padding: 0;
margin: 0;
}
#map {
width: 100%;
height: 100%;
}
</style>
<body>
<div id="map"></div>
<script src="./data/wuhanPolygon.js"></script> <!-- 行政区划数据 -->
<script src="./js/mapboxClickUp.js"></script>
</body>
</html>
完整 JS:
let map;
// mapbox token 请自行到官网申请
mapboxgl.accessToken = '***************************';
const colorStops = {
stops: [
[0, '#ffffcc'],
[1, '#a1dab4'],
[2, '#41b6c4'],
[3, '#2c7fb8'],
[4, '#ffffcc'],
[5, '#253494'],
[6, '#fed976'],
[7, '#feb24c'],
[8, '#fd8d3c'],
[9, '#f03b20'],
[10, '#bd0026'],
[11, '#253494'],
[12, '#ffffcc'],
],
property: 'FID',
};
let featureArr = ['', '']; // 用于要素筛选
map = new mapboxgl.Map({
container: 'map',
style: {
version: 8,
sources: {},
layers: [],
},
center: [114.31369190771244, 30.57799365914005],
zoom: 8.5,
pitch: 45,
});
init();
animate();
function init() {
map.on('load', () => {
mapLoadInit();
addStateLayer();
});
map.on('click', (e) => {
console.log([e.lngLat.lng, e.lngLat.lat]);
console.log(map.getZoom());
});
}
// 添加底图图层
function mapLoadInit() {
map.addLayer({
id: 'static-layer',
type: 'raster',
source: {
type: 'raster',
tiles: [
'https://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}',
],
tileSize: 256,
},
paint: {
'raster-opacity': 1,
},
});
}
function addStateLayer() {
// 数据处理。行政区划数据是用arcgis导出的,需要把json格式转化为mapboxgl支持的格式
let wuhanFeatures = [];
wuhanPolygon.features.forEach((element) => {
wuhanFeatures.push({
type: 'Feature',
properties: element.attributes,
geometry: {
type: 'Polygon',
coordinates: element.geometry.rings,
},
});
});
map.addSource('wuhan', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: wuhanFeatures,
},
});
map.addLayer({
id: 'wuhan',
source: 'wuhan',
type: 'fill',
paint: {
'fill-color': colorStops,
'fill-opacity': 0.5,
},
});
// 三维图层
map.addLayer({
id: 'wuhan_extrusion',
source: 'wuhan',
type: 'fill-extrusion',
paint: {
'fill-extrusion-color': colorStops,
'fill-extrusion-opacity': 0,
'fill-extrusion-height': 0,
'fill-extrusion-base': 0,
},
});
map.on('click', 'wuhan', (e) => {
let featureName = e.features[0].properties.District;
// 保留前一次点击和当前点击的要素名称
featureArr.push(featureName);
featureArr.shift();
// 筛选前一次点击和当前点击的要素,前一次点击的要素做下降动画,当前点击要素做抬升动画
tweenDown();
});
// 修改鼠标移入图层的样式
map.on('mouseenter', 'wuhan', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'wuhan', function () {
map.getCanvas().style.cursor = '';
});
}
// 动画补间
function tweenUp() {
map.setFilter('wuhan_extrusion', ['==', 'District', featureArr[1]]);
let object = { b: 0, o: 0 };
new TWEEN.Tween(object)
.to({ b: 5000, o: 0.8 }, 2000)
.easing(TWEEN.Easing.Quadratic.InOut)
.onStart(() => {})
.onUpdate(() => {
map.getLayer('wuhan_extrusion') &&
map.setPaintProperty('wuhan_extrusion', 'fill-extrusion-height', object.b);
map.getLayer('wuhan_extrusion') &&
map.setPaintProperty('wuhan_extrusion', 'fill-extrusion-opacity', object.o);
})
.onComplete(() => {})
.start();
}
function tweenDown() {
map.setFilter('wuhan_extrusion', ['==', 'District', featureArr[0]]);
let object = { b: 5000, o: 0.8 };
new TWEEN.Tween(object)
.to({ b: 0, o: 0 }, 400)
.onUpdate(() => {
map.getLayer('wuhan_extrusion') &&
map.setPaintProperty('wuhan_extrusion', 'fill-extrusion-height', object.b);
map.getLayer('wuhan_extrusion') &&
map.setPaintProperty('wuhan_extrusion', 'fill-extrusion-opacity', object.o);
})
.onComplete(() => {
tweenUp();
})
.start();
}
function animate() {
requestAnimationFrame(animate);
TWEEN.update();
}
(4)目前还存在的问题: 每次点击图层后 会闪一下,推测是筛选图层或者是动画函数的逻辑问题,后续再优化吧