如何在Egui中实现可手动偏移的无限空滚动?
在Egui中实现可手动偏移的无限滚动空区域
要实现这种无需添加标准条目、可手动控制偏移且自动管理内容生命周期的无限滚动,核心是利用Egui的滚动偏移控制和可见区域计算,手动管理内容的加载与销毁,和Egui绘图系统的可见区域渲染逻辑思路一致。
核心实现步骤
维护滚动状态与内容块缓存
定义状态结构体保存滚动偏移量和已加载的内容块,用哈希表快速管理内容块的增删: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), } }创建自定义滚动区域并处理可见范围
使用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
相关产品推荐
相关产品推荐

