效果展示
实现思路:将区域信息存放在一个数组中并添加是否高亮展示字段,创建标签是添加与scene.children中其他对象不一样的name属性,轮询删除、创建所有标记的标签,并将是否高亮展示复制给下一个标签
1、创建轮播数组
let mapList = []
function initJson() {
const loader = new THREE.FileLoader(); // 文件加载器
loader.load('./json/china/china.json', function (data) {
const jsonData = JSON.parse(data);
jsonData.features.forEach(item => {
if(item.properties.name) {
mapList.push({
name: item.properties.name,
flag: 0,
x: null,
z: null
})
}
mapList[0].flag = 1
})
initMap(jsonData);
});
}
2、生成地图,遍历省份时拿到经纬度,转换为左边,复制到mapLIst中,initmap为遍历省份的方法,initLightPoint方法中获取经纬度/转换坐标
function initMap(chinaJson) {
var matLine = new LineMaterial({
//线条材质
color: '#68c5f6',
linewidth: 0.002,
vertexColors: true, // 定点颜色
dashed: false, //虚线
alphaToCoverage: true,
});
var matLine2 = new LineMaterial({
color: '#45bff8',
linewidth: 0.004,
vertexColors: true,
dashed: false,
alphaToCoverage: true,
});
// d3-geo转化坐标
const projection = d3
.geoMercator()
.center([102.301409, 36.009473])
.scale(100)
.translate([0, 0]);
// 省 450
// 全国 90
// 遍历省份构建模型
chinaJson.features.forEach((elem) => {
// console.log(elem)
const province = new THREE.Object3D(); // 单个省份
const coordinates = elem.geometry.coordinates; //坐标组
var properties = elem.properties; //信息
province.properties = elem.properties.name;
//这里创建标签
initLightPoint(properties, projection);
coordinates.forEach((multiPolygon) => {
multiPolygon.forEach((polygon) => {
// polygon 单个坐标
const positions = [];
var colors = [];
const color = new THREE.Color(); // 默认为白色
var linGeometry = new LineGeometry(); //绘制线条
for (let i = 0; i < polygon.length; i += 1) {
const [x, y] = projection(polygon[i]);
positions.push(x, - y, 4.01); // 定位
color.setHSL(1, 1, 1);
// * HSL中使用渐变
// * h — hue value between 0.0 and 1.0
// * s — 饱和度 between 0.0 and 1.0
// * l — 亮度 between 0.0 and 1.0
colors.push(color.r, color.g, color.b); //颜色
}
const mesh = drawExtrudeMesh(polygon, new THREE.Color(0xa0a0a0), projection)
mesh.name = elem.properties.name
mesh.rotateX(- Math.PI / 2)
mesh.opacity = 0
// console.log(mesh)
//Line2
linGeometry.setPositions(positions);
linGeometry.setColors(colors);
const line = new Line2(linGeometry, matLine);
const line2 = new Line2(linGeometry, matLine2);
line.computeLineDistances();
line.rotateX(- Math.PI / 2);
line2.rotateX(- Math.PI / 2);
line.position.set(0, 0.1, - 3);
line2.position.set(0, - 3.2, - 3);
line2.computeLineDistances();
line.scale.set(1, 1, 1);
// 直接添加在scene上,方便查找
scene.add(mesh)
province.add(line);
province.add(line2);
});
});
map.add(province);
});
scene.add(map);
chinaJson.features.forEach((elem) => {
// 新建一个省份容器:用来存放省份对应的模型和轮廓线
const meshArrs1 = new THREE.Object3D();
const coordinates = elem.geometry.coordinates;
const properties = elem.properties;
// console.log(coordinates)
coordinates.forEach((multiPolygon) => {
multiPolygon.forEach((polygon) => {
const shape = new THREE.Shape();
var v3ps = [];
for (let i = 0; i < polygon.length; i++) {
const [x, y] = projection(polygon[i]);
if (i === 0) {
shape.moveTo(x, - y);
}
shape.lineTo(x, - y);
v3ps.push(new THREE.Vector3(x, - y, 4.02));
}
const extrudeSettings = {
depth: 3, //该属性指定图形可以拉伸多高,默认值是100
bevelEnabled: false, //是否给这个形状加斜面,默认加斜面。
};
//拉升成地图
const geometry = new THREE.ExtrudeGeometry(
shape,
extrudeSettings
);
const mesh = new THREE.Mesh(geometry, [material, material1]);
// console.log(polygon)
mesh.rotateX(- Math.PI / 2);
mesh.name = polygon.name
mesh.position.set(0, 1, - 3);
meshArrs.add(mesh);
});
});
map.add(meshArrs1);
});
scene.add(map);
}
3、initLightPoint方法
function initLightPoint(properties, projection) {
// console.log('创建光柱')
// 创建光柱
const heightScaleFactor = 1 + random(1, 5) / 5;
const lightCenter = properties.centroid || properties.center;
const areaName = properties.name;
// let lightCenter = properties.centroid;
// projection用来把经纬度转换成坐标
const [x, y] = projection(lightCenter);
const light = createLightPillar(x, y, heightScaleFactor);
light.position.z -= 3;
// light.position.y = 13.31;
map.add(light);
// 赋值mapList
mapList.forEach(item => {
if(item.name === areaName) {
item.x = x
item.z = z
}
})
}
4、创建标签
function setPop() {
mapList.forEach(item => {
if(item.name === '' ){
return
}
// 判断是否为高亮显示的标签
if(item.flag === 0) {
const tag = document.createElement('div');
// tag.innerHTML = name;
// tag.className = className
// tag.style.pointerEvents = 'none';
tag.style.border = '1px solid #00EEFF';
tag.style.backgroundColor = '#07113295'
tag.style.padding = '0px 5px'
tag.style.fontSize = '8px'
tag.style.fontWeight = '800'
// tag.style.visibility = 'hidden'
tag.style.position = 'absolute';
tag.style.zIndex = '1'
const label = new CSS2DObject(tag);
let name = ''
// 处理身份名称
if(item.name.split('省').length == 2) {
name = item.name.split('省')[0]
} else if(item.name.split('维吾尔').length == 2) {
name = item.name.split('维吾尔')[0]
} else if(item.name.split('回族').length == 2) {
name = item.name.split('回族')[0]
} else if(item.name.split('壮族').length == 2) {
name = item.name.split('壮族')[0]
} else if(item.name.split('市').length == 2) {
name = item.name.split('市')[0]
} else if(item.name.split('内蒙古').length == 2) {
name = '内蒙古'
} else if(item.name.split('西藏').length == 2) {
name = '西藏'
} else{
name = item.name.split('特别行政区')[0]
}
label.element.innerHTML = `${name} 5397人`;
label.element.style.visibility = 'visible';
label.position.set(item.x, 7, item.z);
label.position.z -= 3;
// 所有新建的标签中都拼接‘标签’,方便查找
label.name = item.name + '标签';
// console.log(label)
scene.add(label);
} else if (item.flag === 1) {
setTimeout(() => {
// console.log(item)
const tag = document.createElement('div');
tag.style.border = '1px solid #00EEFF';
tag.style.backgroundColor = '#CC8D1195'
tag.style.padding = '0px 5px'
tag.style.fontSize = '8px'
tag.style.fontWeight = '800'
tag.style.textAlign = 'center'
// tag.style.visibility = 'hidden'
tag.style.position = 'absolute';
tag.style.zIndex = '99999'
let name = ''
if(item.name.split('省').length == 2) {
name = item.name.split('省')[0]
} else if(item.name.split('维吾尔').length == 2) {
name = item.name.split('维吾尔')[0]
} else if(item.name.split('回族').length == 2) {
name = item.name.split('回族')[0]
} else if(item.name.split('壮族').length == 2) {
name = item.name.split('壮族')[0]
} else if(item.name.split('市').length == 2) {
name = item.name.split('市')[0]
} else if(item.name.split('内蒙古').length == 2) {
name = '内蒙古'
} else if(item.name.split('西藏').length == 2) {
name = '西藏'
} else{
name = item.name.split('特别行政区')[0]
}
const label = new CSS2DObject(tag);
label.element.innerHTML = `${name}游客接待量 <br/> 12560人`;
label.element.style.visibility = 'visible';
label.position.set(item.x, 8, item.z);
label.position.z -= 3;
label.name = item.name + '标签';
// console.log(label)
scene.add(label);
}, 10);
}
})
}
5、轮播
function setLb() {
setInterval(() => {
// 轮询判断是否为省份信息标签,如果是就删除掉,remove为scene中的方法
for(let i = 0; i < scene.children.length; i++) {
if(scene.children[i].name.indexOf('标签') != -1) {
scene.remove(scene.children[i])
i -= 1
}
}
// 将当前高亮显示开关后移一位
for(let i = 0; i < mapList.length; i++ ) {
if(mapList[i].flag / 1 == 1){
mapList[i].flag = 0
if(i == mapList.length - 1) {
mapList[0].flag = 1
} else {
mapList[i + 1].flag = 1
i= 0
}
// 创建标签
setPop()
break
}
}
// 5秒轮播一次
}, 5000);
}