首先Three.js (想要升值加薪麻??那就卷他!)
思路:通过各地区的JSON信息,画出不规则的点,连成线。 在通过线成面,在通过拉伸实现地图的3D效果。
HTML部分
<template>
<canvas id="c2d" :width="width" :height="height" class="canvas"></canvas>
</template>
JS部分👻
import * as THREE from 'three'
import china from '@/assets/map/china.json'
import { geoMercator } from 'd3-geo'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
const width = (document.documentElement.clientWidth / 7) * 3 + 100
const height = document.documentElement.clientHeight - 300
const projection = geoMercator().center([120.498, 29.0918]).translate([0, 0])
const historyIntersects = ref([])
const url = new URL('@/assets/map/china.json', import.meta.url).href
const rundNum = ref(0)
const sceneMolud = ref()
核心代码🙄 这里没写好 可以把init里面的代码很多拆分出来。 这样就不会显着臃肿了
function init() {
const map = new THREE.Object3D()
const scene = new THREE.Scene()
sceneMolud.value = scene
const canvas = document.querySelector('#c2d') as HTMLElement
const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
alpha: true,
})
const fov = 28
const aspect = 1
const near = 0.1
const far = 10000
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
camera.position.set(180, 0, 300)
camera.lookAt(0, 0, 0)
const controls = new OrbitControls(camera, canvas)
controls.update()
{
const color = 0xffffff
const intensity = 1
const light = new THREE.AmbientLight(color, intensity)
scene.add(light)
}
function render() {
renderer.render(scene, camera)
requestAnimationFrame(render)
}
requestAnimationFrame(render)
const loader = new THREE.FileLoader()
loader.load(url, (data: any) => {
const jsondata = JSON.parse(data)
operationData(jsondata)
})
function operationData(jsondata: { features: any }) {
const features = jsondata.features
features.forEach(
(feature: {
properties: { name: string }
geometry: { coordinates: any; type: string }
}) => {
const province = new THREE.Object3D()
const coordinates = feature.geometry.coordinates
const color = 'rgb(55, 143, 240, 0.2)'
if (feature.geometry.type === 'MultiPolygon') {
coordinates.forEach((coordinate: any[]) => {
coordinate.forEach(rows => {
const mesh = drawExtrudeMesh(rows, color, feature.properties.name)
const line = lineDraw(rows, 'rgb(145, 180, 185)')
province.add(line)
province.add(mesh)
})
})
}
if (feature.geometry.type === 'Polygon') {
coordinates.forEach((coordinate: any) => {
const mesh = drawExtrudeMesh(
coordinate,
color,
feature.properties.name,
)
const line = lineDraw(coordinate, 'rgb(145, 180, 185 )')
province.add(line)
province.add(mesh)
})
}
map.add(province)
},
)
scene.add(map)
}
function drawExtrudeMesh(polygon: any[], color: string, name: string) {
const shape = new THREE.Shape()
polygon.forEach((row, i) => {
const [x, y] = projection(row)!
if (i === 0) {
shape.moveTo(x, -y)
}
shape.lineTo(x, -y)
})
const dt = new URL('@/assets/map/img_bg_map.jpg', import.meta.url).href
const texture = new THREE.TextureLoader().load(dt)
texture.center.set(0.5, 0.5)
texture.repeat.set(0.01, 0.015)
texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.RepeatWrapping
const geometry = new THREE.ExtrudeGeometry(shape, {
depth: 10,
bevelEnabled: false,
})
const material = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 0.8,
map: texture,
// FrontSide DoubleSide
side: THREE.DoubleSide,
})
const mesh = new THREE.Mesh(geometry, material)
mesh.name = name
return mesh
}
function lineDraw(polygon: any[], color: string) {
const lineGeometry = new THREE.BufferGeometry()
const pointsArray = new Array()
polygon.forEach((row: any) => {
const [x, y] = projection(row)!
pointsArray.push(new THREE.Vector3(x, -y, 10))
})
lineGeometry.setFromPoints(pointsArray)
const lineMaterial = new THREE.LineBasicMaterial({
color: color,
transparent: true,
linewidth: 20,
fog: true,
// opacity: 1,
})
return new THREE.Line(lineGeometry, lineMaterial)
}
async function addTownNameMarker() {
rundNum.value++
const markers = new THREE.Group()
markers.position.y = 0.8
const ky = new URL('@assets/images/login_img_logo.png', import.meta.url)
.href
const points = china.features.map(item => {
return {
name: item.properties.name,
lnglat: item.properties.center as [number, number],
}
})
for (let i = 0; i < points.length - 1; i++) {
let [x, y] = projection(points[i].lnglat)!
let canvas = document.createElement('canvas')
canvas.width = 200
canvas.height = 100
const ctx = canvas.getContext('2d')!
ctx.fillStyle = '#6ff3c5'
ctx.font = '22px 微软雅黑'
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText(points[i].name, 100, 70, 200)
switch (points[i].name) {
case '青海':
x = x - 10
y = y + 1
break
case '浙江':
x = x + 1
y = y + 5
break
default:
break
}
const textTexture = new THREE.Texture(canvas)
textTexture.magFilter = THREE.NearestFilter
textTexture.minFilter = THREE.NearestFilter
const textMaterial = new THREE.SpriteMaterial({ color: 0xffffff })
textMaterial.map = textTexture
textMaterial.transparent = true
textMaterial.depthWrite = false
textMaterial.sizeAttenuation = false
const textSprite = new THREE.Sprite(textMaterial)
textSprite.position.set(x, -y + 3, 15)
textSprite.scale.set(0.08, 0.04, 1)
textTexture.needsUpdate = true
textSprite.name = points[i].name
markers.add(textSprite)
canvas = null as any
}
scene.add(markers)
}
function calcMeshCenter(group: any) {
const box3 = new THREE.Box3()
box3.expandByObject(group)
const center = new THREE.Vector3(0, 0, 0)
box3.getCenter(center)
group.position.x = group.position.x - center.x
group.position.y = group.position.y - center.y
group.position.z = group.position.z - center.z
}
addTownNameMarker()
calcMeshCenter(sceneMolud.value)
renderer.domElement.addEventListener('dblclick', function (event) {
const px = event.offsetX
const py = event.offsetY
const x = (px / width) * 2 - 1
const y = -(py / height) * 2 + 1
const raycaster = new THREE.Raycaster()
raycaster.setFromCamera(new THREE.Vector2(x, y), camera)
const intersects = raycaster.intersectObjects(scene.children, true) as any
if (intersects[0] === undefined) return
for (let i = 0; i < historyIntersects.value.length; i++) {
const targe = historyIntersects.value[i]
if (targe.object.type === 'Mesh') {
targe.object.material.color.set('rgb(55, 143, 240, 0.2)')
targe.object.position.set(1, 1, 1)
break
}
}
historyIntersects.value = intersects
for (let i = 0; i < intersects.length; i++) {
const element = intersects[i]
if (element.object.type === 'Mesh') {
element.object.material.color.set('rgb(0, 140, 255)')
element.object.position.set(1, 1, 2)
break
}
}
})
}
onMounted(() => {
init()
})