feat(editor): 添加 ECS UI 系统和编辑器更新优化 (#238)

This commit is contained in:
YHH
2025-11-26 11:08:10 +08:00
committed by GitHub
parent 3fb6f919f8
commit 7b14fa2da4
62 changed files with 8745 additions and 235 deletions

View File

@@ -0,0 +1,390 @@
import { Component, ECSComponent, Property, Serializable, Serialize } from '@esengine/ecs-framework';
/**
* 滑块方向
* Slider orientation
*/
export enum UISliderOrientation {
Horizontal = 'horizontal',
Vertical = 'vertical'
}
/**
* UI 滑块组件
* UI Slider Component - Value slider with handle
*/
@ECSComponent('UISlider')
@Serializable({ version: 1, typeId: 'UISlider' })
export class UISliderComponent extends Component {
// ===== 数值 Values =====
/**
* 当前值
* Current value
*/
@Serialize()
@Property({ type: 'number', label: 'Value' })
public value: number = 0;
/**
* 最小值
* Minimum value
*/
@Serialize()
@Property({ type: 'number', label: 'Min Value' })
public minValue: number = 0;
/**
* 最大值
* Maximum value
*/
@Serialize()
@Property({ type: 'number', label: 'Max Value' })
public maxValue: number = 100;
/**
* 步进值0 = 连续)
* Step value (0 = continuous)
*/
@Serialize()
@Property({ type: 'number', label: 'Step', min: 0 })
public step: number = 0;
/**
* 目标值(用于动画)
* Target value (for animation)
*/
public targetValue: number = 0;
/**
* 显示值(动画插值后)
* Display value (interpolated)
*/
public displayValue: number = 0;
// ===== 方向 Orientation =====
/**
* 滑块方向
* Slider orientation
*/
@Serialize()
@Property({
type: 'enum',
label: 'Orientation',
options: [
{ value: 'horizontal', label: 'Horizontal' },
{ value: 'vertical', label: 'Vertical' }
]
})
public orientation: UISliderOrientation = UISliderOrientation.Horizontal;
// ===== 轨道样式 Track Style =====
/**
* 轨道颜色
* Track color
*/
@Serialize()
@Property({ type: 'color', label: 'Track Color' })
public trackColor: number = 0x444444;
/**
* 轨道透明度
* Track alpha
*/
@Serialize()
@Property({ type: 'number', label: 'Track Alpha', min: 0, max: 1, step: 0.01 })
public trackAlpha: number = 1;
/**
* 轨道高度(水平)或宽度(垂直)
* Track thickness
*/
@Serialize()
@Property({ type: 'number', label: 'Track Thickness', min: 1 })
public trackThickness: number = 4;
/**
* 轨道圆角
* Track corner radius
*/
@Serialize()
@Property({ type: 'number', label: 'Track Radius', min: 0 })
public trackRadius: number = 2;
// ===== 填充样式 Fill Style =====
/**
* 填充颜色(已滑过的部分)
* Fill color (passed portion)
*/
@Serialize()
@Property({ type: 'color', label: 'Fill Color' })
public fillColor: number = 0x4A90D9;
/**
* 填充透明度
* Fill alpha
*/
@Serialize()
@Property({ type: 'number', label: 'Fill Alpha', min: 0, max: 1, step: 0.01 })
public fillAlpha: number = 1;
// ===== 手柄样式 Handle Style =====
/**
* 手柄宽度
* Handle width
*/
@Serialize()
@Property({ type: 'number', label: 'Handle Width', min: 1 })
public handleWidth: number = 16;
/**
* 手柄高度
* Handle height
*/
@Serialize()
@Property({ type: 'number', label: 'Handle Height', min: 1 })
public handleHeight: number = 16;
/**
* 手柄颜色
* Handle color
*/
@Serialize()
@Property({ type: 'color', label: 'Handle Color' })
public handleColor: number = 0xFFFFFF;
/**
* 手柄悬停颜色
* Handle hover color
*/
@Serialize()
@Property({ type: 'color', label: 'Handle Hover Color' })
public handleHoverColor: number = 0xE0E0E0;
/**
* 手柄按下颜色
* Handle pressed color
*/
@Serialize()
@Property({ type: 'color', label: 'Handle Pressed Color' })
public handlePressedColor: number = 0xCCCCCC;
/**
* 手柄圆角
* Handle corner radius
*/
@Serialize()
@Property({ type: 'number', label: 'Handle Radius', min: 0 })
public handleRadius: number = 8;
/**
* 手柄边框宽度
* Handle border width
*/
@Serialize()
@Property({ type: 'number', label: 'Handle Border Width', min: 0 })
public handleBorderWidth: number = 0;
/**
* 手柄边框颜色
* Handle border color
*/
@Serialize()
@Property({ type: 'color', label: 'Handle Border Color' })
public handleBorderColor: number = 0x000000;
/**
* 手柄阴影
* Handle shadow enabled
*/
@Serialize()
@Property({ type: 'boolean', label: 'Handle Shadow' })
public handleShadow: boolean = true;
// ===== 交互状态 Interaction State =====
/**
* 手柄是否被悬停
* Whether handle is hovered
*/
public handleHovered: boolean = false;
/**
* 是否正在拖拽
* Whether currently dragging
*/
public dragging: boolean = false;
/**
* 拖拽起始值
* Drag start value
*/
public dragStartValue: number = 0;
/**
* 拖拽起始位置
* Drag start position
*/
public dragStartPosition: number = 0;
// ===== 动画 Animation =====
/**
* 过渡时长(秒)
* Transition duration in seconds
*/
@Serialize()
@Property({ type: 'number', label: 'Transition Duration', min: 0, step: 0.01 })
public transitionDuration: number = 0.1;
// ===== 刻度 Ticks =====
/**
* 是否显示刻度
* Whether to show ticks
*/
@Serialize()
@Property({ type: 'boolean', label: 'Show Ticks' })
public showTicks: boolean = false;
/**
* 刻度数量(不包括首尾)
* Number of ticks (excluding ends)
*/
@Serialize()
@Property({ type: 'integer', label: 'Tick Count', min: 0 })
public tickCount: number = 4;
/**
* 刻度颜色
* Tick color
*/
@Serialize()
@Property({ type: 'color', label: 'Tick Color' })
public tickColor: number = 0x666666;
/**
* 刻度大小
* Tick size
*/
@Serialize()
@Property({ type: 'number', label: 'Tick Size', min: 1 })
public tickSize: number = 4;
// ===== 文本 Text =====
/**
* 是否显示值文本
* Whether to show value text
*/
@Serialize()
@Property({ type: 'boolean', label: 'Show Value' })
public showValue: boolean = false;
/**
* 值文本格式
* Value text format
*/
@Serialize()
@Property({ type: 'string', label: 'Value Format' })
public valueFormat: string = '{value}';
/**
* 小数位数
* Decimal places
*/
@Serialize()
@Property({ type: 'integer', label: 'Decimal Places', min: 0 })
public decimalPlaces: number = 0;
// ===== 回调 Callbacks =====
/**
* 值改变回调
* Value change callback
*/
public onChange?: (value: number) => void;
/**
* 拖拽开始回调
* Drag start callback
*/
public onDragStart?: (value: number) => void;
/**
* 拖拽结束回调
* Drag end callback
*/
public onDragEnd?: (value: number) => void;
/**
* 获取进度百分比 (0-1)
* Get progress as percentage (0-1)
*/
public getProgress(): number {
const range = this.maxValue - this.minValue;
if (range <= 0) return 0;
return Math.max(0, Math.min(1, (this.displayValue - this.minValue) / range));
}
/**
* 从百分比设置值
* Set value from percentage
*/
public setProgress(progress: number): this {
const range = this.maxValue - this.minValue;
return this.setValue(this.minValue + range * Math.max(0, Math.min(1, progress)));
}
/**
* 设置值
* Set value
*/
public setValue(value: number, animate: boolean = true): this {
let newValue = Math.max(this.minValue, Math.min(this.maxValue, value));
// 应用步进
if (this.step > 0) {
newValue = Math.round((newValue - this.minValue) / this.step) * this.step + this.minValue;
}
this.targetValue = newValue;
if (!animate) {
this.value = newValue;
this.displayValue = newValue;
}
return this;
}
/**
* 获取格式化的值文本
* Get formatted value text
*/
public getFormattedValue(): string {
const formattedValue = this.displayValue.toFixed(this.decimalPlaces);
return this.valueFormat.replace('{value}', formattedValue);
}
/**
* 计算手柄位置(归一化 0-1
* Calculate handle position (normalized 0-1)
*/
public getHandlePosition(): number {
return this.getProgress();
}
/**
* 获取当前手柄颜色
* Get current handle color based on state
*/
public getCurrentHandleColor(): number {
if (this.dragging) return this.handlePressedColor;
if (this.handleHovered) return this.handleHoverColor;
return this.handleColor;
}
}