@orca-fe/x-map
Version:
214 lines (213 loc) • 9.96 kB
JavaScript
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();
}
}
}