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

相关推荐
a1117766 小时前
医院挂号预约系统(开源 Fastapi+vue2)
前端·vue.js·python·html5·fastapi
0思必得06 小时前
[Web自动化] Selenium处理iframe和frame
前端·爬虫·python·selenium·自动化·web自动化
行走的陀螺仪8 小时前
uni-app + Vue3编辑页/新增页面给列表页传参
前端·vue.js·uni-app
We་ct9 小时前
LeetCode 205. 同构字符串:解题思路+代码优化全解析
前端·算法·leetcode·typescript
2301_8127314110 小时前
CSS3笔记
前端·笔记·css3
ziblog10 小时前
CSS3白云飘动动画特效
前端·css·css3
越努力越幸运50810 小时前
CSS3学习之网格布局grid
前端·学习·css3
半斤鸡胗10 小时前
css3基础
前端·css
ziblog10 小时前
CSS3创意精美页面过渡动画效果
前端·css·css3
akangznl10 小时前
第四章 初识css3
前端·css·css3·html5