You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Egui中实现可手动偏移的无限空滚动?

在Egui中实现可手动偏移的无限滚动空区域

要实现这种无需添加标准条目、可手动控制偏移且自动管理内容生命周期的无限滚动,核心是利用Egui的滚动偏移控制和可见区域计算,手动管理内容的加载与销毁,和Egui绘图系统的可见区域渲染逻辑思路一致。

核心实现步骤

  1. 维护滚动状态与内容块缓存
    定义状态结构体保存滚动偏移量和已加载的内容块,用哈希表快速管理内容块的增删:

    use egui::{Vec2, Rect, Align2, Color32, Id, Sense};
    use std::collections::HashMap;
    
    // 自定义内容块数据结构,按需定义
    #[derive(Debug, Clone)]
    struct BlockData {
        // 示例字段:块内的内容数据
        content: String,
    }
    
    #[derive(Default)]
    struct InfiniteScrollState {
        scroll_offset: Vec2,
        loaded_blocks: HashMap<i32, BlockData>,
    }
    
    // 模拟获取远程/生成新内容的函数
    fn fetch_block_data(block_idx: i32) -> BlockData {
        BlockData {
            content: format!("无限滚动块 #{}", block_idx),
        }
    }
    
  2. 创建自定义滚动区域并处理可见范围
    使用ScrollArea并手动控制偏移,通过ui.clip_rect()获取当前可见区域,计算需要加载/销毁的内容块范围:

    // 在你的App的update函数中
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            // 从Egui上下文获取或初始化滚动状态
            let state = ui.ctx_mut().data_mut(|d| {
                d.get_temp_mut::<InfiniteScrollState>(Id::new("custom_infinite_scroll"))
                    .unwrap_or_default()
            });
    
            const BLOCK_HEIGHT: f32 = 100.0; // 每个内容块的高度
            const BUFFER_BLOCKS: i32 = 1; // 预加载的缓冲块数量,避免滚动时出现空白
    
            // 创建垂直滚动区域,绑定自定义偏移量
            egui::ScrollArea::vertical()
                .scroll_offset(state.scroll_offset)
                .show(ui, |ui| {
                    // 获取当前滚动区域的可见裁剪矩形
                    let visible_rect = ui.clip_rect();
    
                    // 计算需要加载的内容块范围(包含缓冲块)
                    let top_block = (visible_rect.top() / BLOCK_HEIGHT).floor() as i32 - BUFFER_BLOCKS;
                    let bottom_block = (visible_rect.bottom() / BLOCK_HEIGHT).ceil() as i32 + BUFFER_BLOCKS;
    
                    // 销毁超出范围的内容块,释放内存
                    state.loaded_blocks.retain(|&idx, _| idx >= top_block && idx <= bottom_block);
    
                    // 加载新的内容块
                    for block_idx in top_block..=bottom_block {
                        if !state.loaded_blocks.contains_key(&block_idx) {
                            state.loaded_blocks.insert(block_idx, fetch_block_data(block_idx));
                        }
                    }
    
                    // 绘制所有已加载的内容块
                    for (&block_idx, block) in &state.loaded_blocks {
                        let block_y = block_idx as f32 * BLOCK_HEIGHT;
                        // 分配块的矩形区域(可选,用于交互检测)
                        let block_rect = Rect::from_min_max(
                            vec2(visible_rect.left(), block_y),
                            vec2(visible_rect.right(), block_y + BLOCK_HEIGHT),
                        );
                        ui.allocate_rect(block_rect, Sense::hover());
    
                        // 用Egui绘图API绘制内容,替代标准条目
                        ui.painter().text(
                            block_rect.center(),
                            Align2::CENTER_CENTER,
                            &block.content,
                            ui.style().body_text_style,
                            Color32::WHITE,
                        );
                    }
    
                    // 更新滚动偏移量,同步用户的滚动操作
                    state.scroll_offset = ui.scroll_offset();
                });
        });
    }
    

关键细节说明

  • 可见区域驱动内容管理:通过clip_rect()获取当前用户能看到的区域,只加载该区域及周围缓冲块的内容,完全复用了Egui绘图只渲染可见内容的逻辑。
  • 手动偏移控制:通过scroll_offset()直接获取或设置滚动位置,无需依赖条目数量撑开滚动区域,实现空区域的自由滚动。
  • 内容生命周期优化:用retain()销毁超出范围的内容块,避免内存无限增长,保证滚动流畅性。
  • 自定义交互与绘制:如果需要给空滚动区域添加交互(比如点击、hover),可以通过ui.allocate_rect()分配区域并绑定Sense,再结合绘图API实现自定义内容。

内容的提问来源于stack exchange,提问作者filif96770

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 14:17:20