Files
esengine/packages/ui/src/components/widgets/UISliderComponent.ts

391 lines
9.0 KiB
TypeScript
Raw Normal View History

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;
}
}