feat(editor): 添加 ECS UI 系统和编辑器更新优化 (#238)
This commit is contained in:
370
packages/ui/src/components/widgets/UIScrollViewComponent.ts
Normal file
370
packages/ui/src/components/widgets/UIScrollViewComponent.ts
Normal file
@@ -0,0 +1,370 @@
|
||||
import { Component, ECSComponent, Property, Serializable, Serialize } from '@esengine/ecs-framework';
|
||||
|
||||
/**
|
||||
* 滚动条可见性
|
||||
* Scrollbar visibility mode
|
||||
*/
|
||||
export enum UIScrollbarVisibility {
|
||||
/** 总是显示 Always visible */
|
||||
Always = 'always',
|
||||
/** 自动显示(内容超出时)Auto show when content exceeds */
|
||||
Auto = 'auto',
|
||||
/** 总是隐藏 Always hidden */
|
||||
Hidden = 'hidden'
|
||||
}
|
||||
|
||||
/**
|
||||
* UI 滚动视图组件
|
||||
* UI ScrollView Component - Scrollable container
|
||||
*/
|
||||
@ECSComponent('UIScrollView')
|
||||
@Serializable({ version: 1, typeId: 'UIScrollView' })
|
||||
export class UIScrollViewComponent extends Component {
|
||||
// ===== 滚动位置 Scroll Position =====
|
||||
|
||||
/**
|
||||
* 水平滚动位置
|
||||
* Horizontal scroll position
|
||||
*/
|
||||
public scrollX: number = 0;
|
||||
|
||||
/**
|
||||
* 垂直滚动位置
|
||||
* Vertical scroll position
|
||||
*/
|
||||
public scrollY: number = 0;
|
||||
|
||||
/**
|
||||
* 目标水平滚动位置(动画用)
|
||||
* Target horizontal scroll position (for animation)
|
||||
*/
|
||||
public targetScrollX: number = 0;
|
||||
|
||||
/**
|
||||
* 目标垂直滚动位置(动画用)
|
||||
* Target vertical scroll position (for animation)
|
||||
*/
|
||||
public targetScrollY: number = 0;
|
||||
|
||||
// ===== 内容尺寸 Content Size =====
|
||||
|
||||
/**
|
||||
* 内容宽度
|
||||
* Content width
|
||||
*/
|
||||
public contentWidth: number = 0;
|
||||
|
||||
/**
|
||||
* 内容高度
|
||||
* Content height
|
||||
*/
|
||||
public contentHeight: number = 0;
|
||||
|
||||
// ===== 滚动配置 Scroll Configuration =====
|
||||
|
||||
/**
|
||||
* 是否启用水平滚动
|
||||
* Whether horizontal scroll is enabled
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'boolean', label: 'Horizontal Scroll' })
|
||||
public horizontalScroll: boolean = false;
|
||||
|
||||
/**
|
||||
* 是否启用垂直滚动
|
||||
* Whether vertical scroll is enabled
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'boolean', label: 'Vertical Scroll' })
|
||||
public verticalScroll: boolean = true;
|
||||
|
||||
/**
|
||||
* 滚动条可见性
|
||||
* Scrollbar visibility mode
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({
|
||||
type: 'enum',
|
||||
label: 'Scrollbar Visibility',
|
||||
options: [
|
||||
{ value: 'always', label: 'Always' },
|
||||
{ value: 'auto', label: 'Auto' },
|
||||
{ value: 'hidden', label: 'Hidden' }
|
||||
]
|
||||
})
|
||||
public scrollbarVisibility: UIScrollbarVisibility = UIScrollbarVisibility.Auto;
|
||||
|
||||
/**
|
||||
* 是否启用惯性滚动
|
||||
* Whether inertia scrolling is enabled
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'boolean', label: 'Inertia' })
|
||||
public inertia: boolean = true;
|
||||
|
||||
/**
|
||||
* 惯性减速率
|
||||
* Inertia deceleration rate
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Deceleration Rate', min: 0, max: 1, step: 0.001 })
|
||||
public decelerationRate: number = 0.135;
|
||||
|
||||
/**
|
||||
* 是否启用弹性边界
|
||||
* Whether elastic bounds are enabled
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'boolean', label: 'Elastic Bounds' })
|
||||
public elasticBounds: boolean = true;
|
||||
|
||||
/**
|
||||
* 弹性系数
|
||||
* Elasticity coefficient
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Elasticity', min: 0, max: 1, step: 0.01 })
|
||||
public elasticity: number = 0.1;
|
||||
|
||||
// ===== 滚动条样式 Scrollbar Style =====
|
||||
|
||||
/**
|
||||
* 滚动条宽度
|
||||
* Scrollbar width
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Scrollbar Width', min: 1 })
|
||||
public scrollbarWidth: number = 8;
|
||||
|
||||
/**
|
||||
* 滚动条颜色
|
||||
* Scrollbar color
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'color', label: 'Scrollbar Color' })
|
||||
public scrollbarColor: number = 0x888888;
|
||||
|
||||
/**
|
||||
* 滚动条透明度
|
||||
* Scrollbar alpha
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Scrollbar Alpha', min: 0, max: 1, step: 0.01 })
|
||||
public scrollbarAlpha: number = 0.5;
|
||||
|
||||
/**
|
||||
* 滚动条悬停透明度
|
||||
* Scrollbar hover alpha
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Scrollbar Hover Alpha', min: 0, max: 1, step: 0.01 })
|
||||
public scrollbarHoverAlpha: number = 0.8;
|
||||
|
||||
/**
|
||||
* 滚动条圆角
|
||||
* Scrollbar corner radius
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Scrollbar Radius', min: 0 })
|
||||
public scrollbarRadius: number = 4;
|
||||
|
||||
/**
|
||||
* 滚动条轨道颜色
|
||||
* Scrollbar track color
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'color', label: 'Scrollbar Track Color' })
|
||||
public scrollbarTrackColor: number = 0x333333;
|
||||
|
||||
/**
|
||||
* 滚动条轨道透明度
|
||||
* Scrollbar track alpha
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Scrollbar Track Alpha', min: 0, max: 1, step: 0.01 })
|
||||
public scrollbarTrackAlpha: number = 0.3;
|
||||
|
||||
// ===== 交互状态 Interaction State =====
|
||||
|
||||
/**
|
||||
* 是否正在拖拽滚动
|
||||
* Whether currently dragging to scroll
|
||||
*/
|
||||
public dragging: boolean = false;
|
||||
|
||||
/**
|
||||
* 拖拽起始滚动位置 X
|
||||
* Drag start scroll X
|
||||
*/
|
||||
public dragStartScrollX: number = 0;
|
||||
|
||||
/**
|
||||
* 拖拽起始滚动位置 Y
|
||||
* Drag start scroll Y
|
||||
*/
|
||||
public dragStartScrollY: number = 0;
|
||||
|
||||
/**
|
||||
* 滚动速度 X(用于惯性)
|
||||
* Scroll velocity X (for inertia)
|
||||
*/
|
||||
public velocityX: number = 0;
|
||||
|
||||
/**
|
||||
* 滚动速度 Y(用于惯性)
|
||||
* Scroll velocity Y (for inertia)
|
||||
*/
|
||||
public velocityY: number = 0;
|
||||
|
||||
/**
|
||||
* 水平滚动条是否被悬停
|
||||
* Whether horizontal scrollbar is hovered
|
||||
*/
|
||||
public horizontalScrollbarHovered: boolean = false;
|
||||
|
||||
/**
|
||||
* 垂直滚动条是否被悬停
|
||||
* Whether vertical scrollbar is hovered
|
||||
*/
|
||||
public verticalScrollbarHovered: boolean = false;
|
||||
|
||||
/**
|
||||
* 是否正在拖拽滚动条
|
||||
* Whether dragging scrollbar
|
||||
*/
|
||||
public draggingScrollbar: boolean = false;
|
||||
|
||||
// ===== 滚轮配置 Wheel Configuration =====
|
||||
|
||||
/**
|
||||
* 滚轮滚动速度
|
||||
* Mouse wheel scroll speed
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Wheel Speed', min: 1 })
|
||||
public wheelSpeed: number = 40;
|
||||
|
||||
/**
|
||||
* 是否平滑滚动
|
||||
* Whether to use smooth scrolling
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'boolean', label: 'Smooth Scroll' })
|
||||
public smoothScroll: boolean = true;
|
||||
|
||||
/**
|
||||
* 平滑滚动时长(秒)
|
||||
* Smooth scroll duration in seconds
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Smooth Scroll Duration', min: 0, step: 0.01 })
|
||||
public smoothScrollDuration: number = 0.2;
|
||||
|
||||
/**
|
||||
* 获取最大水平滚动位置
|
||||
* Get maximum horizontal scroll position
|
||||
*/
|
||||
public getMaxScrollX(viewportWidth: number): number {
|
||||
return Math.max(0, this.contentWidth - viewportWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最大垂直滚动位置
|
||||
* Get maximum vertical scroll position
|
||||
*/
|
||||
public getMaxScrollY(viewportHeight: number): number {
|
||||
return Math.max(0, this.contentHeight - viewportHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置滚动位置
|
||||
* Set scroll position
|
||||
*/
|
||||
public setScroll(x: number, y: number, animate: boolean = true): this {
|
||||
this.targetScrollX = x;
|
||||
this.targetScrollY = y;
|
||||
if (!animate) {
|
||||
this.scrollX = x;
|
||||
this.scrollY = y;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到顶部
|
||||
* Scroll to top
|
||||
*/
|
||||
public scrollToTop(animate: boolean = true): this {
|
||||
return this.setScroll(this.scrollX, 0, animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到底部
|
||||
* Scroll to bottom
|
||||
*/
|
||||
public scrollToBottom(viewportHeight: number, animate: boolean = true): this {
|
||||
return this.setScroll(this.scrollX, this.getMaxScrollY(viewportHeight), animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到指定位置(百分比)
|
||||
* Scroll to position by percentage
|
||||
*/
|
||||
public scrollToPercent(percentX: number, percentY: number, viewportWidth: number, viewportHeight: number, animate: boolean = true): this {
|
||||
const x = this.getMaxScrollX(viewportWidth) * Math.max(0, Math.min(1, percentX));
|
||||
const y = this.getMaxScrollY(viewportHeight) * Math.max(0, Math.min(1, percentY));
|
||||
return this.setScroll(x, y, animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要显示水平滚动条
|
||||
* Whether horizontal scrollbar should be visible
|
||||
*/
|
||||
public needsHorizontalScrollbar(viewportWidth: number): boolean {
|
||||
if (!this.horizontalScroll) return false;
|
||||
if (this.scrollbarVisibility === UIScrollbarVisibility.Hidden) return false;
|
||||
if (this.scrollbarVisibility === UIScrollbarVisibility.Always) return true;
|
||||
return this.contentWidth > viewportWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要显示垂直滚动条
|
||||
* Whether vertical scrollbar should be visible
|
||||
*/
|
||||
public needsVerticalScrollbar(viewportHeight: number): boolean {
|
||||
if (!this.verticalScroll) return false;
|
||||
if (this.scrollbarVisibility === UIScrollbarVisibility.Hidden) return false;
|
||||
if (this.scrollbarVisibility === UIScrollbarVisibility.Always) return true;
|
||||
return this.contentHeight > viewportHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取垂直滚动条手柄尺寸和位置
|
||||
* Get vertical scrollbar handle size and position
|
||||
*/
|
||||
public getVerticalScrollbarMetrics(viewportHeight: number): { size: number; position: number } {
|
||||
const maxScroll = this.getMaxScrollY(viewportHeight);
|
||||
if (maxScroll <= 0) return { size: viewportHeight, position: 0 };
|
||||
|
||||
const size = Math.max(20, (viewportHeight / this.contentHeight) * viewportHeight);
|
||||
const availableTrack = viewportHeight - size;
|
||||
const position = (this.scrollY / maxScroll) * availableTrack;
|
||||
|
||||
return { size, position };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取水平滚动条手柄尺寸和位置
|
||||
* Get horizontal scrollbar handle size and position
|
||||
*/
|
||||
public getHorizontalScrollbarMetrics(viewportWidth: number): { size: number; position: number } {
|
||||
const maxScroll = this.getMaxScrollX(viewportWidth);
|
||||
if (maxScroll <= 0) return { size: viewportWidth, position: 0 };
|
||||
|
||||
const size = Math.max(20, (viewportWidth / this.contentWidth) * viewportWidth);
|
||||
const availableTrack = viewportWidth - size;
|
||||
const position = (this.scrollX / maxScroll) * availableTrack;
|
||||
|
||||
return { size, position };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user