跳到主要内容

滚动视图

滚动视图仅显示较大内容的一部分,用户可拖动或滚动鼠标滚轮查看其余内容。列表、背包、长文本和地图都可以使用,它由多个实体组成:

scroll view 组元素,并带滚动视图组件
├── viewport 作为遮罩的图像元素:可见区域
│ └── content 大于视口的元素,启用输入
│ └── … 滚动的内容项
└── scrollbar 图像元素,并带滚动条组件(可选)
└── handle 启用输入的图像元素,可沿滚动条拖动

视口是遮罩,使内容仅在其内绘制。内容应启用输入,便于从空白区域拖动。

滚动视图通过修改内容局部位置进行滚动。滚到左上角时,位置为 0, 0。因此内容应锚定视口左上角或沿顶部横向锚定,轴心设为左上 [0, 1]。其他轴心会使初始内容部分超出视口。

滚动视图

创建滚动视图​

以下 300 × 400 的滚动视图垂直滚动更高的一列内容,右侧带滚动条:

// The scroll view, 300 × 400 in the middle of the screen
const scrollView = new pc.Entity('scroll view');
scrollView.addComponent('element', {
type: pc.ELEMENTTYPE_GROUP,
anchor: [0.5, 0.5, 0.5, 0.5],
pivot: [0.5, 0.5],
width: 300,
height: 400
});
screen.addChild(scrollView);

// The viewport fills the scroll view, apart from 20 units on the right for the scrollbar
const viewport = new pc.Entity('viewport');
viewport.addComponent('element', {
type: pc.ELEMENTTYPE_IMAGE,
anchor: [0, 0, 1, 1],
margin: [0, 0, 20, 0],
mask: true
});
scrollView.addChild(viewport);

// The content hangs from the top-left corner of the viewport
const content = new pc.Entity('content');
content.addComponent('element', {
type: pc.ELEMENTTYPE_GROUP,
anchor: [0, 1, 0, 1],
pivot: [0, 1],
width: 280,
height: 1200,
useInput: true
});
viewport.addChild(content);

// A scrollbar along the right edge, and its handle
const scrollbar = new pc.Entity('scrollbar');
scrollbar.addComponent('element', {
type: pc.ELEMENTTYPE_IMAGE,
anchor: [1, 0, 1, 1],
pivot: [1, 1],
margin: [0, 0, 0, 0],
width: 20,
color: new pc.Color(0.16, 0.18, 0.23)
});
scrollView.addChild(scrollbar);

const handle = new pc.Entity('handle');
handle.addComponent('element', {
type: pc.ELEMENTTYPE_IMAGE,
anchor: [0, 1, 1, 1],
pivot: [1, 1],
margin: [0, 0, 0, 0],
color: new pc.Color(0.5, 0.55, 0.65),
useInput: true
});
scrollbar.addChild(handle);
scrollbar.addComponent('scrollbar', {
orientation: pc.ORIENTATION_VERTICAL,
handleEntity: handle
});

scrollView.addComponent('scrollview', {
viewportEntity: viewport,
contentEntity: content,
verticalScrollbarEntity: scrollbar,
horizontal: false,
vertical: true,
scrollMode: pc.SCROLL_MODE_BOUNCE,
bounceAmount: 0.1,
friction: 0.05
});

创建应用时注册 pc.ScrollViewComponentSystem 和 pc.ScrollbarComponentSystem,并提供 pc.Mouse 以支持鼠标拖动。代码创建的滚动视图没有完整默认设置:需设置 horizontal 或 vertical 才启用对应轴;未设置 scrollMode、bounceAmount 和 friction 时会记录警告、无法回弹,释放后立即停止。应如示例一样全部传入。

滚动行为​

属性效果
horizontal, vertical允许滚动的轴
scrollModeClamp 在边界停止;Bounce 允许超出并弹回;Infinite 允许无限滚动
bounceAmountBounce 的回弹缓慢程度,0 为立即返回,0.1 类似手机滚动,更大值回弹更慢
friction甩动后减速快慢,范围为 0 到 1,1 为立即停止
dragThreshold拖动多少屏幕单位后停止内部元素输入,默认为 10
useMouseWheel, mouseWheelSensitivity是否使用滚轮,以及各轴灵敏度

拖动开始时,内容立即跟随指针。移动超过 dragThreshold 后,内部元素停止接收输入直到拖动结束,避免滚列表时误按按钮;几乎不移动的按下仍可触发按钮。指针位于视图内启用输入的元素,例如内容区域时,滚轮会滚动内容,且不产生回弹。

滚动条​

滚动条由作为轨道的图像元素、Scrollbar 组件和滑块子项组成。视图根据内容可见比例设置滑块大小,根据滚动位置设置滑块位置,拖动滑块即可滚动。Visibility,即 horizontalScrollbarVisibility 和 verticalScrollbarVisibility,可在内容放得下时隐藏滚动条:

  • pc.SCROLLBAR_VISIBILITY_SHOW_ALWAYS 始终显示,默认值。
  • pc.SCROLLBAR_VISIBILITY_SHOW_WHEN_REQUIRED 在内容不大于视口时隐藏。

滚动条也能独立作为滑块使用,详见滑块。

内容尺寸​

内容尺寸决定可滚范围,滚动视图不会自动调整。如果内容实体比条目总高度短,就无法滚到末尾。使用布局组排列时,可监听 reflow,根据每次布局后返回的子项边界设置尺寸:

content.addComponent('layoutgroup', {
orientation: pc.ORIENTATION_VERTICAL,
spacing: [0, 10],
padding: [10, 10, 10, 10],
widthFitting: pc.FITTING_STRETCH
});
content.layoutgroup.on('reflow', ({ bounds }) => {
// Make the content as tall as its rows, plus the top and bottom padding
content.element.height = bounds.w + 20;
});

按上述方式将内容锚定顶部并使用顶部轴心后,内容变高会向下增长,首行位置保持不变。

通过代码滚动​

scroll 是 pc.Vec2 滚动位置,各轴范围为 0 到 1。垂直方向上,0 为顶部,1 为底部:

// Scroll to the end of a chat log
scrollView.scrollview.scroll = new pc.Vec2(0, 1);

// React to the user scrolling
scrollView.scrollview.on('set:scroll', (scroll) => {
loadMoreButton.enabled = scroll.y > 0.95;
});

另请参阅​