UNPKG

@orca-fe/x-map

Version:
214 lines (213 loc) 9.96 kB
import { CircleGeometry, DoubleSide, InstancedMesh, MeshBasicMaterial, NormalBlending, Object3D, PlaneBufferGeometry, RingGeometry, TextureLoader, } from 'three'; import ThreeObject from './ThreeObject'; import { lonLat2Mercator } from '../../utils/coord'; import { clamp } from '../../utils/private'; export default class IconObject extends ThreeObject { constructor(options = {}) { super(options); this.keyCache = new WeakMap(); this.createObject = () => { const { data, layer, src, z, hoverSrc, checkedSrc, width, height, maxZoom = 22, minZoom = 0, opacity } = this; if (layer === null || layer === void 0 ? void 0 : layer.map) { const { threeCenter } = layer.map; const { zoom } = layer.map.getViewport(); const scale = 512 / Math.pow(2, clamp(zoom, minZoom, maxZoom)); this.lastScale = scale; this.object3D.clear(); const mercatorData = data.map((point) => { const [x, y] = lonLat2Mercator(point); return [x - threeCenter[0], y - threeCenter[1]]; }); // 加载纹理 const texture = new TextureLoader().load(src); const checkedTexture = checkedSrc ? new TextureLoader().load(checkedSrc) : texture.clone(); const hoverTexture = hoverSrc ? new TextureLoader().load(hoverSrc) : texture.clone(); // const geometry: BufferGeometry = new PlaneBufferGeometry(); const geometry = this.shape === 'circle' ? new CircleGeometry(0.5, 16) : new PlaneBufferGeometry(); geometry.scale(200 * width * scale, 200 * height * scale, 1); this.material.opacity = opacity; this.material.map = texture; this.checkedMaterial.map = checkedTexture; this.hoverMaterial.map = hoverTexture; // this.ringMesh = new InstancedMesh( // new RingGeometry(100 * width * scale, 100 * (width + 10) * scale, 16, 1), // new MeshBasicMaterial({ color: 0x99ff99, transparent: true, depthWrite: false, depthTest: false }), // data.length, // ); this.dataMesh = new InstancedMesh(geometry, this.material, data.length); this.checkedMesh = new InstancedMesh(geometry, this.checkedMaterial, data.length); this.hoverMesh = new InstancedMesh(geometry, this.hoverMaterial, 1); // this.ringMesh.position.z = -1; // this.ringMesh.renderOrder = z - 2; this.dataMesh.renderOrder = z; this.checkedMesh.renderOrder = z + 2; this.hoverMesh.renderOrder = z + 4; this.object3D.add(/* this.ringMesh, */ this.dataMesh, this.checkedMesh, this.hoverMesh); this.object3D.renderOrder = Math.round(z); const obj = new Object3D(); mercatorData.forEach(([x, y], i) => { obj.position.set(x, y, this.z + i * 0.00001); obj.updateMatrix(); if (this.ringMesh) this.ringMesh.setMatrixAt(i, obj.matrix); if (this.dataMesh) this.dataMesh.setMatrixAt(i, obj.matrix); }); } }; this.lastRotate = 0; this.lastPitch = 0; this.lastScale = 1; this.checkedIndex = -1; this.hoverIndex = -1; const { z = 0.01, opacity = 0.9, maxZoom, minZoom, data = [], width = 50, height = 50, src = '', hoverSrc, checkedSrc, shape = 'circle', } = options; this.minZoom = minZoom; this.maxZoom = maxZoom; this.z = z; this.opacity = opacity; this.data = data; this.width = width; this.height = height; this.src = src; this.hoverSrc = hoverSrc; this.checkedSrc = checkedSrc; this.shape = shape; this.material = new MeshBasicMaterial({ side: DoubleSide, blending: NormalBlending, depthWrite: false, depthTest: false, transparent: true, }); this.hoverMaterial = this.material.clone(); this.checkedMaterial = this.material.clone(); this.on('click', (event, intersection) => { const { instanceId, object } = intersection; if (instanceId != null && instanceId >= 0 && object === this.dataMesh) { this.emit('icon-check', event, instanceId); if (!event.defaultPrevented) this.check(instanceId); } }); this.on('mouseenter', (event, intersection) => { const { instanceId, object } = intersection; if (instanceId != null && instanceId >= 0 && object === this.dataMesh) { this.emit('icon-mouseenter', event, instanceId); if (!event.defaultPrevented) this.hover(instanceId); } }); this.on('mouseleave', (event, intersection) => { const { instanceId, object } = intersection; if (instanceId != null && instanceId >= 0 && object === this.dataMesh) { this.emit('icon-mouseleave', event, instanceId); this.hover(-1); } }); } updatePosition() { const { layer, maxZoom = 22, minZoom = 0, width, height } = this; if (layer) { const { map } = layer; if (map) { const { pitch, bearing: rotate, zoom } = map.getViewport(); const scale = 512 / Math.pow(2, clamp(zoom, minZoom, maxZoom)); if (this.ringMesh) { const t = (new Date().getTime() % 2000) / 1000; this.ringMesh.geometry.dispose(); const geometry = new RingGeometry(0, t * 100 * (width - 10) * scale, 16, 1); geometry.rotateX((pitch / 180) * Math.PI); geometry.rotateZ((-rotate / 180) * Math.PI); this.ringMesh.geometry = geometry; this.ringMesh.material.opacity = 1 - 0.5 * t; } if (this.dataMesh) { const geometry = this.shape === 'circle' ? new CircleGeometry(0.5, 16) : new PlaneBufferGeometry(); geometry.scale(200 * width * scale, 200 * height * scale, 1); geometry.rotateX((pitch / 180) * Math.PI); geometry.rotateZ((-rotate / 180) * Math.PI); this.dataMesh.geometry.dispose(); this.dataMesh.geometry = geometry; if (this.hoverMesh) this.hoverMesh.geometry = geometry; if (this.checkedMesh) this.checkedMesh.geometry = geometry; } this.lastScale = scale; this.lastPitch = pitch; this.lastRotate = rotate; } } } // 选中某个节点 check(index) { var _a; const { layer } = this; if (layer === null || layer === void 0 ? void 0 : layer.map) { if (this.checkedIndex !== index) { this.checkedIndex = index; const obj = new Object3D(); obj.scale.set(0, 0, 0); obj.updateMatrix(); if (this.checkedMesh) { this.checkedMesh.count = 1; if (index >= 0) { (_a = this.dataMesh) === null || _a === void 0 ? void 0 : _a.getMatrixAt(index, obj.matrix); } this.checkedMesh.setMatrixAt(0, obj.matrix); this.checkedMesh.instanceMatrix.needsUpdate = true; } layer.updatePosition(); } } } hover(index) { var _a; const { layer } = this; if (layer === null || layer === void 0 ? void 0 : layer.map) { if (this.hoverIndex !== index) { this.hoverIndex = index; const obj = new Object3D(); obj.scale.set(0, 0, 0); obj.updateMatrix(); if (this.hoverMesh) { this.hoverMesh.count = 1; if (index >= 0) { (_a = this.dataMesh) === null || _a === void 0 ? void 0 : _a.getMatrixAt(index, obj.matrix); } this.hoverMesh.setMatrixAt(0, obj.matrix); this.hoverMesh.instanceMatrix.needsUpdate = true; } layer.updatePositionDebounce(); } } } setOptions(options) { const { data, src, z, hoverSrc, checkedSrc, width, height, maxZoom, minZoom, opacity } = options; if (data != null) this.data = data; if (src != null) this.src = src; if (z != null) this.z = z; if (hoverSrc != null) this.hoverSrc = hoverSrc; if (checkedSrc != null) this.checkedSrc = checkedSrc; if (width != null) this.width = width; if (height != null) this.height = height; if (maxZoom != null) this.maxZoom = maxZoom; if (minZoom != null) this.minZoom = minZoom; if (opacity != null) this.opacity = opacity; this.createObject(); const { layer } = this; if (layer) { layer.updatePositionDebounce(); } } }