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

相关推荐
掘金安东尼1 天前
让 JavaScript 更容易「善后」的新能力
前端·javascript·面试
掘金安东尼1 天前
用 HTMX 为 React Data Grid 加速实时更新
前端·javascript·面试
灵感__idea1 天前
Hello 算法:众里寻她千“百度”
前端·javascript·算法
yinuo1 天前
轻松接入大语言模型API -04
前端
袋鼠云数栈UED团队1 天前
基于 Lexical 实现变量输入编辑器
前端·javascript·架构
cipher1 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
UrbanJazzerati1 天前
非常友好的Vue 3 生命周期详解
前端·面试
AAA阿giao1 天前
从零构建一个现代登录页:深入解析 Tailwind CSS + Vite + Lucide React 的完整技术栈
前端·css·react.js
兆子龙1 天前
像 React Hook 一样「自动触发」:用 Git Hook 拦住忘删的测试代码与其它翻车现场
前端·架构
兆子龙1 天前
用 Auto.js 实现挂机脚本:从找图点击到循环自动化
前端·架构