Skip to content

电子围栏效果

示例

示例源码
vue
<template>
  <div style="height: 500px" class="vw-full vh-full">
    <div style="height: 400px">
      <mb-map
        :zoom="13"
        :center="mapCenter"
        :bearing="0"
        :pitch="45"
        @created="createdHandler"
      >
        <mb-tianditu-layer />
      </mb-map>
    </div>
  </div>
</template>
<script setup lang="ts">
import * as THREE from 'three'
import type { MapboxInstance } from '@mapbox-vue3/core'

const mapCenter = [116.390799, 39.915876]
let map: MapboxInstance

const createdHandler = (mapbox: MapboxInstance) => {
  map = mapbox
  loaderModels()
}
const loaderModels = () => {
  const boundary1 = [
    [116.385394, 39.92098],
    [116.38591, 39.911632],
    [116.389942, 39.911565],
    [116.390457, 39.908734],
    [116.392174, 39.908733],
    [116.39226, 39.911959],
    [116.396294, 39.911894],
    [116.395691, 39.921438],
    [116.385394, 39.92098],
  ]

  addThreeLayer(boundary1)
}
const addThreeLayer = (boundary1: number[][]) => {
  const threeLayer = new map.mapboxgl.supermap.ThreeLayer('threeLayer')
  threeLayer.on('initialized', render)
  let light: THREE.PointLight

  function render(this: any) {
    const renderer = threeLayer.getThreeRenderer(),
      scene = threeLayer.getScene(),
      camera = threeLayer.getCamera()

    light = new THREE.PointLight(0xffffff, 0.8)
    light.position.copy(camera.position)
    scene.add(light)
    scene.add(new THREE.AmbientLight(0xffffff))

    const centerBoundary = this.getCoordinatesCenter(boundary1)
    const centerPoint = this.lngLatToPosition(centerBoundary)
    const outer = boundary1.map((coords) =>
      this.lngLatToPosition({
        lng: coords[0],
        lat: coords[1],
      }).sub(centerPoint)
    )

    const polygonMesh = addFencing(outer.map((p) => [p.x, 0, p.y]))
    polygonMesh.rotation.x = Math.PI / 2
    polygonMesh.rotation.y = Math.PI * 2.98

    threeLayer.setPosition(polygonMesh, [116.390714, 39.916535])

    scene.add(polygonMesh)
    renderer.render(scene, camera)
  }

  threeLayer.on('render', () => {
    light && light.position.copy(threeLayer.renderer.camera.position)
  })
  map.addLayer(threeLayer)
}
const addFencing = (points: number[][]) => {
  const height = 1500 // 高度 height
  // 围栏距离 fence distance
  const pointDistance: number[] = []
  const distance = points.reduce((totalDistance, point, index) => {
    let segmentDistance = 0
    if (index > 0) {
      const lastPoint = new THREE.Vector3(...points[index - 1])
      const currPoint = new THREE.Vector3(...point)
      segmentDistance = lastPoint.distanceTo(currPoint)
    }
    totalDistance += segmentDistance
    pointDistance.push(totalDistance)
    return totalDistance
  }, 0)

  const geometry = new THREE.BufferGeometry()
  const posArr: number[] = []
  const uvArr: number[] = []

  points.forEach((point, index) => {
    if (index == 0) return
    const lastPoint = points[index - 1]

    // 三角面1 triangle1
    posArr.push(...lastPoint)
    uvArr.push(pointDistance[index - 1] / distance, 0)
    posArr.push(...point)
    uvArr.push(pointDistance[index] / distance, 0)
    posArr.push(lastPoint[0], lastPoint[1] + height, lastPoint[2])
    uvArr.push(pointDistance[index - 1] / distance, 1)

    // 三角面2 triangle2
    posArr.push(...point)
    uvArr.push(pointDistance[index] / distance, 0)
    posArr.push(point[0], point[1] + height, point[2])
    uvArr.push(pointDistance[index] / distance, 1)
    posArr.push(lastPoint[0], lastPoint[1] + height, lastPoint[2])
    uvArr.push(pointDistance[index - 1] / distance, 1)
  })
  geometry.setAttribute(
    'position',
    new THREE.BufferAttribute(new Float32Array(posArr), 3)
  )
  geometry.setAttribute(
    'uv',
    new THREE.BufferAttribute(new Float32Array(uvArr), 2)
  )
  geometry.computeVertexNormals()

  const texture = new THREE.TextureLoader().load(
    `${__RESOURCE_URL__}images/wall.png`
  )
  texture.wrapS = THREE.RepeatWrapping
  texture.wrapT = THREE.RepeatWrapping

  const material = new THREE.MeshBasicMaterial({
    color: 0x00ff00,
    map: texture,
    transparent: true,
    opacity: 0.9,
    depthWrite: false,
    side: THREE.DoubleSide,
  })

  const mesh = new THREE.Mesh(geometry, material)
  return mesh
}
</script>