详细解析 cesiumViewer.render() 和 requestAnimationFrame(render)

详细解析 cesiumViewer.render() 和 requestAnimationFrame(render)

1. cesiumViewer.render()

作用:

手动触发一次 Cesium 场景的渲染。在默认情况下,Cesium 会自动处理渲染循环,但当你禁用默认渲染循环时,需要手动调用此方法。

参数:无参数
典型用法:
javascript 复制代码
// 创建 Viewer 时禁用默认渲染循环
const viewer = new Cesium.Viewer('cesiumContainer', {
    useDefaultRenderLoop: false
});

// 手动渲染一帧
viewer.render();

2. requestAnimationFrame(render)

作用:

浏览器提供的 API,用于在下一次浏览器重绘前执行指定的回调函数,通常用于创建平滑的动画或渲染循环。

参数:
  • callback: 下一次重绘时调用的函数
  • 回调函数会接收一个参数(时间戳,表示开始执行回调的时间)

3. 组合使用详解

基本渲染循环:
javascript 复制代码
function render() {
    // 执行场景渲染
    viewer.render();
    // 请求下一帧继续渲染
    requestAnimationFrame(render);
}

// 启动渲染循环
render();
更完整的示例:
javascript 复制代码
class CustomRenderLoop {
    constructor(viewer) {
        this.viewer = viewer;
        this.isRendering = false;
        this.requestId = null;
        this.lastTime = 0;
        this.frameCount = 0;
        this.fps = 0;
    }

    start() {
        if (this.isRendering) return;
        
        this.isRendering = true;
        this.lastTime = performance.now();
        this.animate();
    }

    stop() {
        if (this.requestId) {
            cancelAnimationFrame(this.requestId);
            this.requestId = null;
        }
        this.isRendering = false;
    }

    animate(currentTime = 0) {
        if (!this.isRendering) return;

        // 计算帧率
        this.frameCount++;
        const deltaTime = currentTime - this.lastTime;
        if (deltaTime >= 1000) { // 每秒更新一次FPS
            this.fps = Math.round((this.frameCount * 1000) / deltaTime);
            console.log(`FPS: ${this.fps}`);
            this.frameCount = 0;
            this.lastTime = currentTime;
        }

        try {
            // 执行渲染
            this.viewer.render();
            
            // 执行场景更新前的回调
            if (this.onPreRender) this.onPreRender(currentTime);
            
            // 执行自定义更新逻辑
            this.update(currentTime);
            
            // 执行场景更新后的回调
            if (this.onPostRender) this.onPostRender(currentTime);
            
        } catch (error) {
            console.error('渲染错误:', error);
            this.stop();
            return;
        }

        // 请求下一帧
        this.requestId = requestAnimationFrame(this.animate.bind(this));
    }

    update(currentTime) {
        // 在这里添加自定义的更新逻辑
        // 例如:更新实体位置、相机动画等
    }
}

// 使用示例
const viewer = new Cesium.Viewer('cesiumContainer', {
    useDefaultRenderLoop: false
});

const renderLoop = new CustomRenderLoop(viewer);
renderLoop.start();

// 添加自定义渲染回调
renderLoop.onPreRender = (time) => {
    // 在渲染前执行的操作
    console.log(`渲染前时间: ${time}`);
};

// 停止渲染循环
// renderLoop.stop();

4. 性能优化技巧

按需渲染(只在有变化时渲染):
javascript 复制代码
let needsRender = false;

// 监听场景变化
viewer.scene.preRender.addEventListener(() => {
    needsRender = true;
});

function smartRender() {
    if (needsRender) {
        viewer.render();
        needsRender = false;
    }
    requestAnimationFrame(smartRender);
}

smartRender();
节流渲染(限制帧率):
javascript 复制代码
function createThrottledRender(targetFPS = 60) {
    const interval = 1000 / targetFPS;
    let lastTime = 0;
    
    return function throttledRender(currentTime) {
        if (currentTime - lastTime >= interval) {
            viewer.render();
            lastTime = currentTime;
        }
        requestAnimationFrame(throttledRender);
    };
}

const throttledRender = createThrottledRender(30); // 限制到30FPS
throttledRender();

5. 实际应用场景

场景1:与其他动画库集成
javascript 复制代码
// 与Three.js集成
function renderBoth() {
    // 渲染Cesium场景
    viewer.render();
    
    // 渲染Three.js场景
    threeRenderer.render(threeScene, threeCamera);
    
    requestAnimationFrame(renderBoth);
}
场景2:响应式渲染
javascript 复制代码
let isUserInteracting = false;

// 监听用户交互
viewer.screenSpaceEventHandler.setInputAction(() => {
    isUserInteracting = true;
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);

function responsiveRender() {
    if (isUserInteracting) {
        // 用户交互时全速渲染
        viewer.render();
        isUserInteracting = false;
    } else {
        // 空闲时降低渲染频率
        setTimeout(() => {
            requestAnimationFrame(responsiveRender);
            viewer.render();
        }, 100); // 100ms间隔
        return;
    }
    
    requestAnimationFrame(responsiveRender);
}

6. 注意事项

  1. 性能影响 :频繁调用 render() 可能影响性能,尤其是在移动设备上
  2. 内存泄漏:确保在不需要时停止渲染循环
  3. 错误处理:在渲染循环中添加错误处理,防止崩溃
  4. 兼容性requestAnimationFrame 在不同浏览器中的表现可能略有差异

7. 最佳实践

javascript 复制代码
// 使用Promise包装的渲染循环
class PromiseBasedRenderLoop {
    constructor(viewer) {
        this.viewer = viewer;
        this.shouldStop = false;
    }
    
    async run() {
        while (!this.shouldStop) {
            await new Promise(resolve => {
                requestAnimationFrame(() => {
                    viewer.render();
                    resolve();
                });
            });
            
            // 可以在这里添加帧之间的延迟
            // await this.delay(16); // ~60FPS
        }
    }
    
    delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    
    stop() {
        this.shouldStop = true;
    }
}

这种组合方式让你可以完全控制Cesium的渲染过程,适用于需要精细控制渲染逻辑的高级应用场景。

相关推荐
爱上好庆祝11 小时前
学习js的第五天
前端·css·学习·html·css3·js
C澒11 小时前
IntelliPro 产研协作平台:基于 AI Agent 的低代码智能化配置方案设计与实现
前端·低代码·ai编程
一袋米扛几楼9812 小时前
【Git】规范化协作:详解 GitHub 工作流中的 Issue、Branch 与 Pull Request 最佳实践
前端·git·github·issue
网络点点滴12 小时前
前端与后端的区别与联系
前端
EnCi Zheng12 小时前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen12 小时前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技12 小时前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人13 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实13 小时前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端
Sarvartha13 小时前
三目运算符
linux·服务器·前端