Keel GitHub

Carousel

在线示例:这是真实的 Keel 程序编译成 WebAssembly,可以直接操作。首次加载约 10 MB。全屏打开

轮播:一次显示一张,带上一张、下一张按钮和指示点。

car := kit.Carousel(slide1, slide2, slide3).Height(200).Autoplay(4 * time.Second)
  • 点击按钮或指示点切换;轮播获得焦点后,横向用 ← →、竖向用 ↑ ↓ 切换,默认首尾循环;Home/End 选择第一张/最后一张。
  • Autoplay(d) 每隔 d 切到下一张。指针悬停在轮播上时暂停;开启减少动画时不自动播放。
  • SetDisabled(true) 禁止按钮、指示点和按键切换,同时停止自动播放并移除焦点;父容器禁用、内容离开视口或被模态层遮挡时也暂停自动播放,恢复后重新计时。
  • Value() / SetValue(i)(不触发回调),OnChange(fn)。
  • "上一张""下一张"的名称来自 locale。

Agent:容器角色 group,名字是"当前/总数"(如 2/3);按钮名为"上一张""下一张",指示点的名字是序号。

验证:go run ./examples/components -section carousel,加 -theme dark 检查深色。

Vertical(true) 把导航放到内容右侧:上箭头、纵向指示点和下箭头。Vertical(false) 恢复内容下方的水平导航。Height 仍控制内容高度(默认 200dp),导航至少保留 72dp;非正或非有限高度忽略。指示点区域可竖向滚动,以容纳较多项目。切换方向保留当前索引及已有键盘焦点,不触发 OnChange;自动播放、悬停暂停、禁用和减少动画规则保持不变。

默认是单项切换,ItemsPerView 可开启等尺寸多项视口;已支持指针拖动吸附,连续滚动在停顿后吸附,见下文。前后控件和分页项可独立组合,见下文。

Loop(false) 关闭循环:到第一项时禁用上一项,到最后一项时禁用下一项,键盘越界保持原选择。Loop(true) 恢复默认循环;切换模式不改变索引,也不触发 OnChange。空列表和单项列表的前后按钮始终禁用,空列表的 Agent 计数为 0/0。

Previous() / Next() 与内置按钮共用导航逻辑,只在索引实际变化时调用 OnChange,并遵守自身禁用状态。CanPrevious() / CanNext() 返回结合列表数量、循环模式、边界和自身禁用状态的可用性。它们不读取父容器继承的禁用状态;外部控件应放在相同的禁用容器中,或由应用另行禁用。SetValue 仍用于无回调的程序设置,即使禁用也可调用。

非循环自动播放到最后一项后停止;程序或用户返回前面的项后,从下一帧重新计时。其他悬停、减少动画和可见性暂停规则不变。

car.Loop(false)
previous := kit.Button("上一项", car.Previous)
previous.SetDisabled(!car.CanPrevious()) // 渲染前按当前状态更新

独立组合

保留返回的控件实例,在每帧渲染它们:

content := car.Content()
previous := car.PreviousControl(nil)
next := car.NextControl(kit.Button("下一项", nil).Variant(kit.ButtonSecondary).Size(40))
first := car.PaginationItem(0, nil)
second := car.PaginationItem(1, nil)
// 在自己的布局中分别调用 content/previous/next/first/second.Render(cx)。

Content() 只显示内容区,保留方向键、Home/End、焦点和自动播放。同一个 CarouselView 只挂载一次 Content 或完整 Carousel;独立控件可创建多份。传入 nil 时,前后控件按当前轴显示箭头,分页项显示从 1 开始的数字并突出当前页;分页索引从 0 开始,无效索引禁用。

传入 Button 时继承尺寸、文字、图标、富内容、名称、外观、加载和自身禁用配置。组件接管 ID 和点击回调,不执行原按钮回调,也不修改原实例。每帧读取配置,因此加载/禁用和外观可动态更新;自定义分页外观自行配置,选中状态仍以 toggle/selected 语义公开。点击当前页不重复回调,程序 SetValue 不触发回调。

轮播自身禁用会同步到所有关联控件;父容器禁用只沿实际布局树继承,组合时应把内容和控件放在同一禁用容器内。自动播放暂停检测范围仍是内容区,外部控件的悬停不自动暂停。控制按钮的 Space/Enter 执行操作;方向导航由内容区焦点处理。

同屏多项

ItemsPerView(2) 将当前轴分成两个等尺寸槽位;Gap(8) 设置项目间距(默认 SpaceMd)。横向按可用宽度分配,竖向按 Height 分配;窗口尺寸变化后重新测量。正整数生效,1 恢复原单项模式;间距只接受有限非负值,在极窄视口中会压缩。项目少于槽位数时保留空余空间。

按钮、分页、方向键、自动播放仍按一个项目移动。选中项尽量对齐开头,末尾限制偏移以填满视口,因此最后几项可能共享同一个偏移;Value 和回调仍区分每个索引。Loop 在索引首尾循环,不复制轨道项目。SetValue 在禁用时仍可更新显示。

默认所有项目使用稳定的轨道身份,超出视口的内容被裁剪,不做虚拟化。轨道偏移由选择或指针拖动控制;连续滚动可在停顿后同步选中。

分数比例与混合尺寸

Basis(2.0/3) 让每个槽位约占视口的三分之二,露出后面项目的一部分;ItemBasis(1, 0.5) 将第二项单独设为半个视口。比例范围为 (0,1],比例不超过视口,超视口项目可用 ItemSize;0 清除对应覆盖,非法比例或索引忽略。横向比例对应宽度,竖向对应高度。

car.Basis(2.0/3).ItemBasis(1, 0.5).Gap(8)
car.ItemBasis(1, 0) // 恢复默认比例
car.ItemsPerView(2) // 清除全局 Basis,逐项覆盖仍保留

比例包含间距份额:项目主轴尺寸为 (视口尺寸 + 间距) × 比例 − 间距;例如 240dp 视口、8dp 间距、1/2 比例得到 116dp 项目。有效间距按最小比例收敛以适应窄视口。导航按每项及间距实际舍入后的像素尺寸累加,不假定项目等宽/等高;末端偏移仍受视口边界限制。

调整比例不改变当前索引、不触发 OnChange;轨道模式内部保留输入状态与焦点。关闭 Draggable 和 Scrollable、清除所有逐项覆盖且 ItemsPerView 为 1、Basis 为 0 时恢复旧单项模式,会重建项目层级。固定 dp 尺寸通过 ItemSize 配置。

固定尺寸项目

ItemSize(index, dp) 为指定项目设置主轴尺寸,不包含间距。横向对应宽度,竖向对应高度;窗口缩放或方向切换时 dp 数值保持不变。固定尺寸优先于 ItemBasis 和全局比例,ItemSize(index, 0) 清除固定值并恢复原比例。无效索引、负数及非有限值忽略;修改不改变选择,也不触发 OnChange。

car.Basis(0.5).ItemSize(0, 180).ItemSize(2, 640)
car.ItemSize(0, 0) // 恢复半个视口

固定尺寸可以超过视口。选中超大项目时显示它的开头,其余部分被裁剪;导航到下一项时按超大项目的完整尺寸加间距计算偏移。此批没有增加项目内部的自由滚动,超大内容需要应用自己提供内层滚动容器;连续滚动在停顿后吸附,见下文。关闭 Draggable 和 Scrollable、清除所有逐项比例/固定尺寸且 ItemsPerView 为 1、Basis 为 0 时,才恢复旧单项模式。

指针拖动吸附

Draggable(true) 默认开启。可从内容区的非交互区域开始鼠标或触摸拖动;沿当前轴移动至少 4dp 后,轨道跟随指针,释放时选择距离最近的吸附点。移动期间不改变 Value,也不调用 OnChange;释放后索引实际变化才回调一次。重合吸附点优先保留当前项,否则选第一个最近项。吸附立即完成,没有速度惯性或过渡动画。

取消拖动、程序 SetValue、改变轴/尺寸配置或禁用会恢复当前选中的定位,不提交旧拖动。拖动期间暂停自动播放,结束后重新计时。Draggable(false) 禁用拖动;仍可使用按钮、分页和键盘。子按钮、输入框及其他交互控件优先处理自己的事件,不从这些控件上启动轮播拖动。

拖动到轨道首尾会限制偏移;Loop 当前只影响按钮/键盘/自动播放,不提供拖动跨首尾的连续轨道。指针拖动仍不支持边界移交;滚动事件的边界路由见下文。

滚轮与连续滚动

Scrollable(true) 默认开启。水平轮播接收水平滚动,竖向轮播接收竖向滚动,另一轴交给外层。连续位移实时更新轨道;连续 140ms 没有新事件后选择最近吸附点,索引实际变化才触发一次回调。滚动期间暂停自动播放,结束后重新计时。程序切换、配置改变或禁用取消旧滚动,不允许其延迟回调覆盖新选择。

Gio 0.10.3 只提供滚动位移,不能区分鼠标滚轮和触控板,也没有手势结束标记,因此 140ms 是停顿判定,并非原生手势结束通知。WheelStep(true) 显式改为每个滚动事件切换一项,不按位移大小跳多项;水平轮播在此模式下也接收竖向滚轮。它不是自动设备检测,也不能保证合并后的事件等于一个机械刻度。

连续模式从首尾向外开始滚动时把事件交给外层;已在内部开始的滚动保留到停顿,防止到达边界时外层突然移动。逐项模式沿用 Loop;非循环边界交给外层,循环时在首尾换页。交互子项里的滚动容器优先接收自己的滚动。Scrollable(false) 关闭这一路输入,不关闭指针拖动。

当前不提供连续滚动跨首尾的循环轨道或吸附过渡动画;原生触控板手感、系统惯性与不同设备的事件合并仍需真机验收。