391 lines
9.0 KiB
TypeScript
391 lines
9.0 KiB
TypeScript
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;
|
||
}
|
||
}
|