ArcGIS JS API+Three.js實現動態航線效果
首先我們來看下效果吧~
該示例里有兩個動態效果,一個是光線沿著類似拋物線軌跡運動的效果,一個是光線落地后有一個類似波紋擴散的效果。下面就來詳細看看怎么實現這兩個效果。
動態航線效果
實現動態航線的原理很簡單,就是每隔一段時間就更新線段的位置,連貫起來就是線段流動的效果。那么首先我們就要得到一條曲線軌跡,three.js中有一個CatmullRomCurve3類,可以用來創建一條平滑的曲線。我們需要三個點來創建曲線,除了起點,終點,我們還需要一個中間點。
const startCoordinate = [116.46, 39.92, 0]; // 起點經緯度坐標、高度值
const endCoordinate = [104.06, 30.67, 0]; // 終點經緯度坐標、高度值
const centerCoordinate = [110.26, 35.295, 200000]; // 中間點經緯度坐標、高度值
現在是經緯度加高度值的點,我們需要把點轉換成渲染坐標系中的點。下面代碼是一個轉換方法,將[經度, 緯度, 高度]值轉換成渲染坐標系中的[x, y, z]值。
/**
* 經緯度坐標點轉換為渲染坐標系中的點坐標
* @param {number} longitude 經度
* @param {number} latitude 緯度
* @param {number} height 高度
* @return {array} 返回渲染坐標系中的點坐標[x, y, z]
*/
pointTransform(longitude, latitude, height) {
let transformation = new Array(16);
// 將經緯度坐標轉換為xy值
let pointXY = webMercatorUtils.lngLatToXY(longitude, latitude);
// 先轉換高度為0的點
externalRenderers.renderCoordinateTransformAt(
view,
[pointXY[0], pointXY[1], height], // 坐標在地面上的點[x值, y值, 高度值]
view.spatialReference,
transformation
);
return [transformation[12], transformation[13], transformation[14]];
}
該方法中用到了webMercatorUtils.lngLatToXY
方法,將給定的緯度和經度(十進制度)值轉換為Web Mercator的XY值。還用到了externalRenderers.renderCoordinateTransformAt
方法,用來將坐標轉換成渲染坐標系中的值,具體可點擊查看詳細文檔。
將起點、終點、中間點都轉換成渲染坐標系中的點。
let startPoint = pointTransform(...startCoordinate);
let endPoint = pointTransform(...endCoordinate);
let centerPoint = pointTransform(...centerCoordinate);
得到三個渲染坐標系中的點后,就可以用three.js中的CatmullRomCurve3類來生成一條平滑的曲線。
const curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(...startPoint),
new THREE.Vector3(...centerPoint),
new THREE.Vector3(...endPoint),
]);
有了曲線后,我們需要將曲線分割成許多段,得到分割點的列表數據,后面用這些點數據來更新光線的位置。如下圖所示,黑線代表完整的曲線軌跡,紅色圓點代表分割點,藍色線段代表移動的光線。航線動態效果實際上就是改變藍色線段的位置,舉個例子,當前狀態如下面那條曲線所示,光線由第3,4,5這3個點坐標構成,下一次渲染畫面的時候,狀態就如上面那條曲線所示,光線由第4,5,6這3個點構成,連貫起來就是光線向前運動的感覺。
CatmullRomCurve3的實例上有一個getPoints方法,用來將曲線分割成指定段數,并返回分割點列表。
const points = curve.getPoints(60); // 分割成60段,返回61個點 points.length 為61
用分割后的點數據創建光線。
const highLightGeometry = new THREE.Geometry();
highLightGeometry.vertices = points.slice(0, 3); // 將分割后的前三個點賦值給頂點數據,后面只需改變這個頂點數組
highLightGeometry.verticesNeedUpdate = true; // 如果頂點隊列中的數據被修改,該值需要被設置為 true
highLightGeometry.colors = [
new THREE.Color('#ffff00'),
new THREE.Color('#ffffff'),
new THREE.Color('#ffff00'),
];
let highLight = new THREE.Line(
highLightGeometry,
highLightMaterial
);
scene.add(highLight);
現在已經創建好光線
了,要讓它移動起來,只需要在渲染方法中改變它的頂點坐標就行了,也就是改變highLight.geometry.vertices
的值。
波紋擴散效果
波紋擴撒的效果原理也很簡單,創建一個平面圓,隨著時間改變圓的透明度和大小。圓的位置很好求出來,就是文章前面提到的方法,將圓中心點的經緯度坐標轉換成渲染坐標系中的點坐標就行了。只不過需要注意的是,位置放對了,圓的姿態大概率是錯誤的,正確的姿態是圓平面應該平行于地面,所以還需要根據計算出來的圓中心點坐標和三角函數來計算圓平面調整的角度。
// 計算調整姿態的角度
let deltaX = Math.atan(this.endPoint[2] / this.endPoint[1]);
let deltaZ = Math.atan(
this.endPoint[0] /
Math.sqrt(
this.endPoint[1] * this.endPoint[1] +
this.endPoint[2] * this.endPoint[2]
)
);
// 如果 y < 0 需要加上180°
if (this.endPoint[1] < 0) {
deltaX += Math.PI;
} else {
deltaZ *= -1;
}
// 調整平面圓的姿態
circleMesh.rotation.x = deltaX;
circleMesh.rotation.z = deltaZ;
circleMesh.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI / 2); // 再沿X軸旋轉90°
同樣的,還需要在渲染方法中添加改變透明度和大小的代碼。
render() {
...
...
circleMesh.material.opacity = 透明度; // 透明度
circleMesh.scale.set(縮放比例, 縮放比例, 縮放比例); // 修改縮放比例
...
...
}
完整代碼
如需查看示例效果可點擊下載完整代碼。
如果該文章對您有所幫助,請您一定不要吝嗇您的鼓勵。點贊、評論、分享、收藏、打賞都是您對我的鼓勵和支持。
如果您有GitHub
賬號,還可以關注我~
最后,感謝大家的閱讀,如有錯誤,還請各位批評指正。