详细解析 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的渲染过程,适用于需要精细控制渲染逻辑的高级应用场景。

相关推荐
前端付豪2 小时前
必知Node应用性能提升及API test 接口测试
前端·react.js·node.js
王同学 学出来2 小时前
vue+nodejs项目在服务器实现docker部署
服务器·前端·vue.js·docker·node.js
一道雷2 小时前
让 Vant 弹出层适配 Uniapp Webview 返回键
前端·vue.js·前端框架
bug总结2 小时前
uniapp+动态设置顶部导航栏使用详解
java·前端·javascript
晴殇i2 小时前
深入理解MessageChannel:JS双向通信的高效解决方案
前端·javascript·程序员
毕设十刻2 小时前
基于Vue的民宿管理系统st4rf(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
kkkAloha2 小时前
倒计时 | setInterval
前端·javascript·vue.js
云轩奕鹤2 小时前
智析单词书 - AI 驱动的深度英语词汇学习平台
前端·ai·产品·思维
逆光如雪2 小时前
控制台快速查看自己的log,提高开发效率
前端