feat(editor): 添加 ECS UI 系统和编辑器更新优化 (#238)
This commit is contained in:
390
packages/ui/src/components/widgets/UISliderComponent.ts
Normal file
390
packages/ui/src/components/widgets/UISliderComponent.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user