mirror of
https://github.com/akveo/ngx-admin.git
synced 2025-09-22 05:50:48 +02:00
feat(temperatureDragger): fix some UX issues
This commit is contained in:
parent
03bf3c6590
commit
fe7ef91ada
3 changed files with 36 additions and 25 deletions
|
@ -11,7 +11,7 @@
|
||||||
</filter>
|
</filter>
|
||||||
|
|
||||||
<clipPath id="sliderClip">
|
<clipPath id="sliderClip">
|
||||||
<path [attr.d]="styles.clipPathStr"></path>
|
<path [attr.d]="styles.clipPathStr" stroke="black"></path>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
|
|
||||||
</defs>
|
</defs>
|
||||||
|
@ -24,8 +24,10 @@
|
||||||
<path [attr.d]="styles.nonSelectedArc.d" [attr.fill]="styles.nonSelectedArc.color"></path>
|
<path [attr.d]="styles.nonSelectedArc.d" [attr.fill]="styles.nonSelectedArc.color"></path>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<circle [attr.cx]="styles.knobPosition.x" [attr.cy]="styles.knobPosition.y" [attr.r]="pinRadius"
|
<circle [attr.cx]="styles.thumbPosition.x" [attr.cy]="styles.thumbPosition.y" [attr.r]="pinRadius"
|
||||||
[attr.stroke-width]="1 / scaleFactor" fill="#FFFFFF" stroke="#666666"></circle>
|
[attr.stroke-width]="1 / scaleFactor" fill="#FFFFFF" stroke="#666666"></circle>
|
||||||
|
<circle [attr.cx]="styles.thumbPosition.x" [attr.cy]="styles.thumbPosition.y" [attr.r]="pinDashRadius"
|
||||||
|
[attr.stroke-width]="2 / scaleFactor" fill="none" stroke="#FFFFFF" stroke-linecap="round" stroke-dasharray="1, 6" *ngIf="pinDashRadius"></circle>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,7 +17,9 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
@Input() disableArcColor: string = '#999999';
|
@Input() disableArcColor: string = '#999999';
|
||||||
@Input() bottomAngle: number = 90;
|
@Input() bottomAngle: number = 90;
|
||||||
@Input() arcThickness: number = 20; // CSS pixels
|
@Input() arcThickness: number = 20; // CSS pixels
|
||||||
@Input() knobRadius: number = 15; // CSS pixels
|
@Input() thumbRadius: number = 15; // CSS pixels
|
||||||
|
@Input() thumbDashRadius: number = null;
|
||||||
|
@Input() maxLeap: number = 0.4;
|
||||||
|
|
||||||
value: number = 50;
|
value: number = 50;
|
||||||
@Output('valueChange') valueChange = new EventEmitter<Number>();
|
@Output('valueChange') valueChange = new EventEmitter<Number>();
|
||||||
|
@ -30,13 +32,13 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
|
|
||||||
@Output() power = new EventEmitter<boolean>();
|
@Output() power = new EventEmitter<boolean>();
|
||||||
|
|
||||||
@HostListener('mouseup', ['$event'])
|
@HostListener('window:mouseup', ['$event'])
|
||||||
onMouseUp(event) {
|
onMouseUp(event) {
|
||||||
this.recalculateValue(event);
|
this.recalculateValue(event);
|
||||||
this.isMouseDown = false;
|
this.isMouseDown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('mousemove', ['$event'])
|
@HostListener('window:mousemove', ['$event'])
|
||||||
onMouseMove(event: MouseEvent) {
|
onMouseMove(event: MouseEvent) {
|
||||||
this.recalculateValue(event);
|
this.recalculateValue(event);
|
||||||
}
|
}
|
||||||
|
@ -56,6 +58,7 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
translateYValue = 0;
|
translateYValue = 0;
|
||||||
thickness = 6;
|
thickness = 6;
|
||||||
pinRadius = 10;
|
pinRadius = 10;
|
||||||
|
pinDashRadius = null;
|
||||||
colors: any = [];
|
colors: any = [];
|
||||||
|
|
||||||
styles = {
|
styles = {
|
||||||
|
@ -64,7 +67,7 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
clipPathStr: '',
|
clipPathStr: '',
|
||||||
gradArcs: [],
|
gradArcs: [],
|
||||||
nonSelectedArc: {},
|
nonSelectedArc: {},
|
||||||
knobPosition: { x: 0, y: 0 },
|
thumbPosition: { x: 0, y: 0 },
|
||||||
blurRadius: 15,
|
blurRadius: 15,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,7 +92,7 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
mouseDown(event) {
|
mouseDown(event) {
|
||||||
this.isMouseDown = true;
|
this.isMouseDown = true;
|
||||||
if (!this.off) {
|
if (!this.off) {
|
||||||
this.recalculateValue(event);
|
this.recalculateValue(event, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,8 +129,9 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
const svgBoundingRect = this.svgRoot.nativeElement.getBoundingClientRect();
|
const svgBoundingRect = this.svgRoot.nativeElement.getBoundingClientRect();
|
||||||
const svgAreaFactor = svgBoundingRect.width / svgBoundingRect.height;
|
const svgAreaFactor = svgBoundingRect.width / svgBoundingRect.height;
|
||||||
const svgHeight = VIEW_BOX_SIZE / svgAreaFactor;
|
const svgHeight = VIEW_BOX_SIZE / svgAreaFactor;
|
||||||
const knobMargin = this.knobRadius > this.arcThickness
|
const thumbMaxRadius = Math.max(this.thumbRadius, this.thumbDashRadius);
|
||||||
? (this.knobRadius - this.arcThickness / 2) / this.scaleFactor
|
const thumbMargin = 2 * thumbMaxRadius > this.arcThickness
|
||||||
|
? (thumbMaxRadius - this.arcThickness / 2) / this.scaleFactor
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
this.scaleFactor = svgBoundingRect.width / VIEW_BOX_SIZE;
|
this.scaleFactor = svgBoundingRect.width / VIEW_BOX_SIZE;
|
||||||
|
@ -139,13 +143,13 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
: ( 2 * Math.sin(halfAngle) / (1 + Math.cos(halfAngle)) );
|
: ( 2 * Math.sin(halfAngle) / (1 + Math.cos(halfAngle)) );
|
||||||
if (circleFactor > svgAreaFactor) {
|
if (circleFactor > svgAreaFactor) {
|
||||||
if (this.bottomAngleRad > Math.PI) {
|
if (this.bottomAngleRad > Math.PI) {
|
||||||
this.radius = (VIEW_BOX_SIZE - 2 * knobMargin) / (2 * Math.sin(halfAngle));
|
this.radius = (VIEW_BOX_SIZE - 2 * thumbMargin) / (2 * Math.sin(halfAngle));
|
||||||
} else {
|
} else {
|
||||||
this.radius = VIEW_BOX_SIZE / 2 - knobMargin;
|
this.radius = VIEW_BOX_SIZE / 2 - thumbMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.radius = (svgHeight - 2 * knobMargin) / (1 + Math.cos(halfAngle));
|
this.radius = (svgHeight - 2 * thumbMargin) / (1 + Math.cos(halfAngle));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.translateXValue = VIEW_BOX_SIZE / 2 - this.radius;
|
this.translateXValue = VIEW_BOX_SIZE / 2 - this.radius;
|
||||||
|
@ -154,10 +158,10 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
this.styles.arcTranslateStr = `translate(${this.translateXValue}, ${this.translateYValue})`;
|
this.styles.arcTranslateStr = `translate(${this.translateXValue}, ${this.translateYValue})`;
|
||||||
|
|
||||||
this.thickness = this.arcThickness / this.scaleFactor;
|
this.thickness = this.arcThickness / this.scaleFactor;
|
||||||
this.pinRadius = this.knobRadius / this.scaleFactor;
|
this.pinRadius = this.thumbRadius / this.scaleFactor;
|
||||||
|
this.pinDashRadius = this.thumbDashRadius && this.thumbDashRadius / this.scaleFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private calculateClipPathSettings() {
|
private calculateClipPathSettings() {
|
||||||
const halfAngle = this.bottomAngleRad / 2;
|
const halfAngle = this.bottomAngleRad / 2;
|
||||||
const innerRadius = this.radius - this.thickness;
|
const innerRadius = this.radius - this.thickness;
|
||||||
|
@ -295,14 +299,14 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
const radiusOffset = this.thickness / 2;
|
const radiusOffset = this.thickness / 2;
|
||||||
const curveRadius = this.radius - radiusOffset;
|
const curveRadius = this.radius - radiusOffset;
|
||||||
const actualAngle = (2 * Math.PI - this.bottomAngleRad) * this.getValuePercentage() + this.bottomAngleRad / 2;
|
const actualAngle = (2 * Math.PI - this.bottomAngleRad) * this.getValuePercentage() + this.bottomAngleRad / 2;
|
||||||
this.styles.knobPosition = {
|
this.styles.thumbPosition = {
|
||||||
x: curveRadius * (1 - Math.sin(actualAngle)) + radiusOffset,
|
x: curveRadius * (1 - Math.sin(actualAngle)) + radiusOffset,
|
||||||
y: curveRadius * (1 + Math.cos(actualAngle)) + radiusOffset,
|
y: curveRadius * (1 + Math.cos(actualAngle)) + radiusOffset,
|
||||||
};
|
};
|
||||||
this.invalidateNonSelectedArc();
|
this.invalidateNonSelectedArc();
|
||||||
}
|
}
|
||||||
|
|
||||||
private recalculateValue(event) {
|
private recalculateValue(event, allowJumping = false) {
|
||||||
if (this.isMouseDown && !this.off) {
|
if (this.isMouseDown && !this.off) {
|
||||||
const rect = this.svgRoot.nativeElement.getBoundingClientRect();
|
const rect = this.svgRoot.nativeElement.getBoundingClientRect();
|
||||||
const center = {
|
const center = {
|
||||||
|
@ -314,18 +318,19 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
actualAngle = actualAngle + 2 * Math.PI;
|
actualAngle = actualAngle + 2 * Math.PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = 0;
|
const previousRelativeValue = this.getValuePercentage();
|
||||||
|
let relativeValue = 0;
|
||||||
if (actualAngle < this.bottomAngleRad / 2) {
|
if (actualAngle < this.bottomAngleRad / 2) {
|
||||||
value = 0;
|
relativeValue = 0;
|
||||||
} else if (actualAngle > 2 * Math.PI - this.bottomAngleRad / 2) {
|
} else if (actualAngle > 2 * Math.PI - this.bottomAngleRad / 2) {
|
||||||
value = 1;
|
relativeValue = 1;
|
||||||
} else {
|
} else {
|
||||||
value = (actualAngle - this.bottomAngleRad / 2) / (2 * Math.PI - this.bottomAngleRad);
|
relativeValue = (actualAngle - this.bottomAngleRad / 2) / (2 * Math.PI - this.bottomAngleRad);
|
||||||
}
|
}
|
||||||
|
|
||||||
value = value * (this.max - this.min) + this.min;
|
const value = this.toValueNumber(relativeValue);
|
||||||
|
|
||||||
if (this.value !== value) {
|
if (this.value !== value && (allowJumping || Math.abs(relativeValue - previousRelativeValue) < this.maxLeap)) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.valueChange.emit(this.value);
|
this.valueChange.emit(this.value);
|
||||||
this.invalidatePinPosition();
|
this.invalidatePinPosition();
|
||||||
|
@ -337,6 +342,10 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
return (this.value - this.min) / (this.max - this.min);
|
return (this.value - this.min) / (this.max - this.min);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private toValueNumber(factor) {
|
||||||
|
return factor * (this.max - this.min) + this.min;
|
||||||
|
}
|
||||||
|
|
||||||
private static toRad(angle) {
|
private static toRad(angle) {
|
||||||
return Math.PI * angle / 180;
|
return Math.PI * angle / 180;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
|
|
||||||
<div class="temperature-container">
|
<div class="temperature-container">
|
||||||
<ngx-temperature-dragger [(value)]="temperature" (power)="temperatureOff = !$event" [min]="12" [max]="30"
|
<ngx-temperature-dragger [(value)]="temperature" (power)="temperatureOff = !$event" [min]="12" [max]="30"
|
||||||
[arcThickness]="20" [knobRadius]="15" [bottomAngle]="90" [disableArcColor]="themeConfig.layoutBg"
|
[arcThickness]="20" [thumbRadius]="15" [thumbDashRadius]="22" [bottomAngle]="90" [disableArcColor]="themeConfig.layoutBg"
|
||||||
[fillColors]="[themeConfig.colorInfo, themeConfig.colorSuccess, themeConfig.colorWarning]">
|
[fillColors]="[themeConfig.colorInfo, themeConfig.colorSuccess, themeConfig.colorWarning]">
|
||||||
|
|
||||||
<div class="temperature" [ngClass]="{ 'off': temperatureOff }">
|
<div class="temperature" [ngClass]="{ 'off': temperatureOff }">
|
||||||
<div class="value">
|
<div class="value">
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
<span>Humidity placeholder</span>
|
<span>Humidity placeholder</span>
|
||||||
|
|
||||||
<!--<ngx-temperature-dragger [(value)]="humidity" (power)="humidityOff = !$event" [min]="0" [max]="100"-->
|
<!--<ngx-temperature-dragger [(value)]="humidity" (power)="humidityOff = !$event" [min]="0" [max]="100"-->
|
||||||
<!--[arcThickness]="20" [knobRadius]="15" [bottomAngle]="90" [disableArcColor]="themeConfig.layoutBg"-->
|
<!--[arcThickness]="20" [thumbRadius]="15" [bottomAngle]="90" [disableArcColor]="themeConfig.layoutBg"-->
|
||||||
<!--[fillColors]="[themeConfig.colorInfo, themeConfig.colorSuccess, themeConfig.colorWarning]">-->
|
<!--[fillColors]="[themeConfig.colorInfo, themeConfig.colorSuccess, themeConfig.colorWarning]">-->
|
||||||
|
|
||||||
<!--<div class="temperature" [ngClass]="{ 'off': humidityOff }">-->
|
<!--<div class="temperature" [ngClass]="{ 'off': humidityOff }">-->
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue