cesium 多路径 实体移动

646 阅读2分钟

使用cesium完成一个简单的多路径小圆点移动动画

<template>
    <div class="cesiumModel" id="cesiumModel"></div>
</template>

<script>
// 这里我把路径单独提成1个文件方便维护,实际项目中也可能是后台返回的数据
import { rainPath } from '../utils/path' 

export default {
    data() {
        return {
            viewer: null,
            paths: [], // 路径数组
            entities: [], // 小圆点实体数组
            numberOfPoints: 2, // 每条路径的圆点数量
            animationFrame: null,
        };
    },
    mounted() {
        this.init();
    },
    methods: {
        init() {
            this.viewer = new Cesium.Viewer("cesiumModel", {
                animation: false,
                timeline: false,
                sceneModePicker: false,
                requestRenderMode: false,
            });
            // 添加点击事件 方便后期获取位置参数
            this.viewer.scene.canvas.addEventListener("click", this.onMapClick);
            // 定义路径数据
            this.paths = sewagePath
            // 初始化实体
            this.createMovingPoints();
            // 设置视角
            this.viewer.camera.setView({
                destination: Cesium.Cartesian3.fromDegrees(111.755575, 40.737001, 4000),
            });
            // 开始动画
            this.startAnimation();
        },
        onMapClick(event) {
            const scene = this.viewer.scene;
            const camera = this.viewer.camera;
            // 获取点击位置的屏幕坐标
            const mousePosition = new Cesium.Cartesian2(event.clientX, event.clientY);
            // 从屏幕坐标获取地球上的三维坐标
            const cartesian = scene.pickPosition(mousePosition);
            if (Cesium.defined(cartesian)) {
                // 转换为经纬度坐标
                const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
                const longitude = Cesium.Math.toDegrees(cartographic.longitude);
                const latitude = Cesium.Math.toDegrees(cartographic.latitude);
                const height = cartographic.height;
                console.log(`经度: ${longitude}, 纬度: ${latitude}, 高度: ${height}`);
                // 在地图上添加一个点作为标记
                this.viewer.entities.add({
                    position: cartesian,
                    point: {
                        pixelSize: 10,
                        color: Cesium.Color.RED,
                    },
                });
            }
        },
        // 创建实体
        createMovingPoints() {
            this.paths.forEach((path, pathIndex) => {
                const { points } = path;
                const segments = points.length - 1;
                const segmentLength = 1 / segments; // 每段路径的插值范围
                for (let i = 0; i < path.num; i++) {
                    // 计算实体在总路径中的位置
                    const globalFraction = i / path.num;
                    // 确定当前在哪一段路径上
                    const segmentIndex = Math.floor(globalFraction / segmentLength);
                    const segmentFraction = (globalFraction % segmentLength) / segmentLength;
                    // 确保插值在有效范围内
                    if (segmentIndex >= segments) continue;
                    const startPoint = points[segmentIndex];
                    const endPoint = points[segmentIndex + 1];
                    // 计算插值位置
                    const position = Cesium.Cartesian3.lerp(
                        startPoint,
                        endPoint,
                        segmentFraction,
                        new Cesium.Cartesian3()
                    );
                    // 创建实体
                    const arrow = this.viewer.entities.add({
                        position: position,
                        billboard: { // 不一定用billboard,也可以用point等多种实现方式
                            image: "/static/1.png", //这里填写你的图片地址
                            width: 20,
                            height: 20,
                            rotation: path.rotation
                        },
                    });
                    // 存储实体及路径信息
                    this.entities.push({ entity: arrow, pathIndex, globalFraction });
                }
            });
        },

        // 动画逻辑
        startAnimation() {
            const animate = () => {
                this.paths.forEach((path, pathIndex) => {
                    const { points, speed } = path;
                    // 更新偏移量
                    path.currentOffset += speed;
                    if (path.currentOffset >= 1.0) {
                        path.currentOffset = 0.0;
                    }
                    // 每条路径更新圆点位置
                    this.entities.forEach(({ entity, pathIndex: entityPathIndex, globalFraction }) => {
                        if (pathIndex === entityPathIndex) {
                            // 计算全局偏移
                            const offsetFraction = (globalFraction + path.currentOffset) % 1.0;
                            // 确定所在路径段
                            const segments = points.length - 1;
                            const segmentLength = 1 / segments;
                            const segmentIndex = Math.floor(offsetFraction / segmentLength);
                            const segmentFraction =
                                (offsetFraction % segmentLength) / segmentLength;
                            // 确保插值在有效范围内
                            if (segmentIndex >= segments) return;

                            const startPoint = points[segmentIndex];
                            const endPoint = points[segmentIndex + 1];

                            // 计算插值位置
                            const newPosition = Cesium.Cartesian3.lerp(
                                startPoint,
                                endPoint,
                                segmentFraction,
                                new Cesium.Cartesian3()
                            );

                            // 更新实体位置
                            entity.position = newPosition;
                        }
                    });
                });
                // 请求重新渲染
                this.viewer.scene.requestRender();
                // 循环动画
                this.animationFrame = requestAnimationFrame(animate);
            };
            animate();
        },
    },
    beforeDestroy() {
        cancelAnimationFrame(this.animationFrame);
    },
}
</script>

<style>
.cesiumModel {
    width: 100%;
    height: 100vh;
}
</style>
export const rainPath = [ // 根据自己实际情况添加
    {
        points: [ // 路径
            Cesium.Cartesian3.fromDegrees(-75.0, 39.0),
            Cesium.Cartesian3.fromDegrees(-75.1, 39.1),
            Cesium.Cartesian3.fromDegrees(-75.2, 39.2),
        ],
        currentOffset: 0, // 偏移量
        num: 10, // 数量
        speed: 0.01, // 速度
        rotation: -300 // 旋转
    }
]