别再瞎写了!Cesium 模型 360° 环绕,4 套源码全公开,项目直接用

0 阅读4分钟

之前在地图上展示的园区模型增加了选中效果,但是对于展示性质的大屏而言,内容一直是静态展示的效果肯定不好。

image.png

所以模型自动环绕展示是绝对的核心亮点功能!无论是展厅大屏演示、项目汇报、还是产品展示,流畅的360°环绕浏览都能让你的三维场景瞬间提升质感。

简单整理了4种开箱即用的环绕方案,从极简入门到专业级平滑动画全覆盖,适配不同项目需求,复制代码直接运行!

flyToLocation + 定时器(极简)

利用之前封装 cesium-viewer 组件做的 flyToLocation 方法实现此功能。

之前封装的 flyToLocation 方法其实就是 cesium.camera.flyTo 方法。

这个方案代码最简单,无需复杂数学计算。支持分段式环绕,适合快速实现需求

可控制停留时间、飞行速度、环绕点数。

image.png

完整代码

// 环绕展示工厂模型
const displayFactoryModel = () => {
    if (!cesiumViewRef.value) return;
    
    const centerLon = 117.104273; // 模型中心点经度
    const centerLat = 36.437867; // 模型中心点纬度
    const radius = 150; // 环绕半径(米)
    const height = 80; // 相机高度
    const duration = 2; // 每个角度飞行时间(秒)
    
    let currentAngle = 0;
    const totalAngles = 8; // 环绕8个点
    const angleStep = 360 / totalAngles;
    
    const flyToNextPoint = () => {
        // 计算当前角度的位置
        const rad = currentAngle * Math.PI / 180;
        const offsetX = radius * Math.cos(rad);
        const offsetY = radius * Math.sin(rad);
        
        // 计算新的经纬度(简化计算,适用于小范围)
        const newLon = centerLon + (offsetX / 111320 / Math.cos(centerLat * Math.PI / 180));
        const newLat = centerLat + (offsetY / 111320);
        
        // 计算相机朝向(朝向中心点)
        const heading = (currentAngle + 180) % 360;
        
        cesiumViewRef.value.flyToLocation({
            longitude: newLon,
            latitude: newLat,
            height: height,
            duration: duration,
            heading: heading,
            pitch: -30, // 稍微俯视的角度
            onComplete: () => {
                currentAngle = (currentAngle + angleStep) % 360;
                setTimeout(flyToNextPoint, 500); // 短暂停留后继续
            }
        });
    };
    
    flyToNextPoint();
};

lookAt + 帧动画(推荐)

这个方案相较于上面的方案比较好的一点就是真正的360°无缝平滑环绕

这种方案无卡顿、无跳跃,动画效果最自然,并且支持无限循环环绕。生产环境推荐首选方案

完整代码

// 环绕展示工厂模型
const displayFactoryModel = () => {
    if (!cesiumViewer.value) return;
    
    const center = Cesium.Cartesian3.fromDegrees(117.104273, 36.437867, 0);
    const radius = 150; // 环绕半径
    const height = 80; // 相机高度
    const duration = 20; // 完整环绕一周的时间(秒)
    
    let startTime = null;
    let animationId = null;
    
    const animateOrbit = (timestamp) => {
        if (!startTime) startTime = timestamp;
        const elapsed = (timestamp - startTime) / 1000;
        
        // 计算当前角度(0到2π)
        const angle = (elapsed / duration) * 2 * Math.PI;
        
        // 计算相机位置(圆形轨道)
        const cameraX = center.x + radius * Math.cos(angle);
        const cameraY = center.y + radius * Math.sin(angle);
        const cameraZ = center.z + height;
        const cameraPosition = new Cesium.Cartesian3(cameraX, cameraY, cameraZ);
        
        // 设置相机位置和朝向(看向中心点)
        cesiumViewer.value.camera.lookAt(
            cameraPosition,
            center, // 看向中心点
            new Cesium.Cartesian3(0, 0, 1)  // up方向
        );
        
        // 继续动画
        animationId = requestAnimationFrame(animateOrbit);
    };
    
    // 开始动画
    animationId = requestAnimationFrame(animateOrbit);
    
    // 返回停止函数(可选)
    return () => {
        if (animationId) {
            cancelAnimationFrame(animationId);
        }
    };
};

flyTo + 曲线路径

基于样条曲线生成平滑路径。支持自定义轨迹点、飞行总时长。

这个方案可以实现复杂环绕、俯冲、拉升等动作,但是如果不是动态模型运动没太大必要用这个。

完整代码

// 环绕展示工厂模型
const displayFactoryModel = () => {
    if (!cesiumViewer.value) return;
    
    const center = Cesium.Cartesian3.fromDegrees(117.104273, 36.437867, 0);
    const radius = 150;
    const height = 80;
    const points = 12; // 路径点数
    const duration = 30; // 总飞行时间
    
    // 创建路径点
    const positions = [];
    for (let i = 0; i <= points; i++) {
        const angle = (i / points) * 2 * Math.PI;
        const x = center.x + radius * Math.cos(angle);
        const y = center.y + radius * Math.sin(angle);
        const z = center.z + height;
        positions.push(new Cesium.Cartesian3(x, y, z));
    }
    
    // 相机沿路径飞行
    cesiumViewer.value.camera.flyTo({
        destination: positions,
        orientation: {
            heading: Cesium.Math.toRadians(0),
            pitch: Cesium.Math.toRadians(-30),
            roll: 0.0
        },
        duration: duration,
        complete: () => {
            console.log('✅ 环绕展示完成');
        }
    });
};

camera.rotate 旋转(极简)

这种方案代码量最少,一行核心逻辑。直接使用原地旋转视角,不改变相机位置。

但是展示效果一般,只能原地自转,不能环绕。

完整代码

// 环绕展示工厂模型
const displayFactoryModel = () => {
    if (!cesiumViewer.value) return;
    
    // 先飞到模型上方
    cesiumViewRef.value.flyToLocation({
        longitude: 117.104273,
        latitude: 36.437867,
        height: 80,
        duration: 3,
        heading: 0,
        pitch: -30,
        onComplete: () => {
            // 开始旋转
            let angle = 0;
            const rotateInterval = setInterval(() => {
                angle = (angle + 1) % 360;
                cesiumViewer.value.camera.setView({
                    orientation: {
                        heading: Cesium.Math.toRadians(angle),
                        pitch: Cesium.Math.toRadians(-30),
                        roll: 0.0
                    }
                });
            }, 50); // 每50ms旋转1度
            
            // 10秒后停止
            setTimeout(() => {
                clearInterval(rotateInterval);
                console.log('✅ 旋转结束');
            }, 10000);
        }
    });
};

总结

模型环绕展示是Cesium展示模型非常好的一种方案,尤其是做会议室大屏使用。

展示修效果还是相当不错的,如果没有特殊要求,可以考虑使用 lookAt+帧动画,这是综合体验最优的方案。

强烈建议大家使用此方案,流畅不卡顿,用户体验直接拉满!