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

相关推荐
前端snow1 小时前
ai agent --- 实现 openclaw 定时间效果
前端
码云之上1 小时前
换模型之后,Chatbot 为什么要自己做 compact?
前端·agent·前端工程化
星栈2 小时前
自定义 UI-UX-Pro-Max 的 CSV 知识库,把 AI 生成的后台拉回行业该有的样子
前端·agent·weui
研☆香2 小时前
简单的图片上传 删除 预览
前端
ITresearchGuest2 小时前
我用 AI 一小时写了一个世界杯数据可视化平台|前端 VibeCoding 初体验
前端·人工智能·信息可视化
慧一居士3 小时前
Vite项目中使用Less步骤,详细使用示例
前端·css
花间相见3 小时前
【LangChain组件03】—— LangChain Agents 执行与状态:工作流程与状态管理实战
java·前端·langchain
kyriewen4 小时前
面试官让我用 AI 重构一个 8 年陈的 React 组件——他说他不看代码,只看我会不会拆
前端·javascript·面试
计算机魔术师4 小时前
700吉瓦「幽灵用电」:美国AI数据中心的抢电闹剧正在失控
前端
IT_陈寒4 小时前
JavaScript数组排序踩的坑,差点让我加班到凌晨
前端·人工智能·后端