UNPKG

ngx-circular-slider

Version:

check out the repo on stackblitz: [demo](https://stackblitz.com/github/craftworksgmbh/ngx-circular-slider)

467 lines (460 loc) 18.1 kB
import { Component, EventEmitter, Input, NgModule, Output, ViewChild } from '@angular/core'; import { Observable as Observable$1 } from 'rxjs/Observable'; import 'rxjs/add/observable/fromEvent'; import 'rxjs/add/observable/merge'; import 'rxjs/add/operator/switchMapTo'; import 'rxjs/add/operator/takeUntil'; import 'rxjs/add/operator/throttleTime'; import 'rxjs/add/operator/do'; import { interpolateHcl } from 'd3-interpolate'; import { CommonModule } from '@angular/common'; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ var THROTTLE_DEFAULT = 50; var DEFAULT_PROPS = { segments: 6, strokeWidth: 40, radius: 145, gradientColorFrom: '#ff9800', gradientColorTo: '#ffcf00', bgCircleColor: '#171717', showClockFace: true, clockFaceColor: '#9d9d9d' }; var NgxCircularSliderComponent = /** @class */ (function () { function NgxCircularSliderComponent() { this.update = new EventEmitter(); this.props = DEFAULT_PROPS; this.startAngle = 0; this.angleLength = 0; } /** * @param {?} evt * @return {?} */ NgxCircularSliderComponent.extractMouseEventCoords = /** * @param {?} evt * @return {?} */ function (evt) { var /** @type {?} */ coords = (evt instanceof MouseEvent ? { x: evt.clientX, y: evt.clientY } : { x: evt.changedTouches.item(0).clientX, y: evt.changedTouches.item(0).clientY }); return coords; }; /** * @return {?} */ NgxCircularSliderComponent.prototype.ngOnInit = /** * @return {?} */ function () { this.setCircleCenter(); this.onUpdate(); this.setObservables(); }; /** * @param {?} changes * @return {?} */ NgxCircularSliderComponent.prototype.ngOnChanges = /** * @param {?} changes * @return {?} */ function (changes) { if (changes.props) { this.props = (changes.props.firstChange ? Object.assign(DEFAULT_PROPS, changes.props.currentValue) : DEFAULT_PROPS); } this.onUpdate(); }; /** * @return {?} */ NgxCircularSliderComponent.prototype.ngOnDestroy = /** * @return {?} */ function () { this.closeStreams(); }; /** * @return {?} */ NgxCircularSliderComponent.prototype.onUpdate = /** * @return {?} */ function () { this.calcStartAndStop(); this.createSegments(); this.update.emit({ startAngle: this.startAngle, angleLength: this.angleLength }); }; /** * @return {?} */ NgxCircularSliderComponent.prototype.setObservables = /** * @return {?} */ function () { var _this = this; var /** @type {?} */ mouseMove$ = Observable$1.merge(Observable$1.fromEvent(document, 'mousemove'), Observable$1.fromEvent(document, 'touchmove')); var /** @type {?} */ mouseUp$ = Observable$1.merge(Observable$1.fromEvent(document, 'mouseup'), Observable$1.fromEvent(document, 'touchend')); this.startSubscription = Observable$1.merge(Observable$1.fromEvent(this.startIcon.nativeElement, 'touchstart'), Observable$1.fromEvent(this.startIcon.nativeElement, 'mousedown')).switchMapTo(mouseMove$ .takeUntil(mouseUp$)) .throttleTime(THROTTLE_DEFAULT) .subscribe(function (res) { _this.handleStartPan(res); }); this.stopSubscription = Observable$1.merge(Observable$1.fromEvent(this.stopIcon.nativeElement, 'touchstart'), Observable$1.fromEvent(this.stopIcon.nativeElement, 'mousedown')).switchMapTo(mouseMove$ .takeUntil(mouseUp$)) .throttleTime(THROTTLE_DEFAULT) .subscribe(function (res) { _this.handleStopPan(res); }); }; /** * @return {?} */ NgxCircularSliderComponent.prototype.closeStreams = /** * @return {?} */ function () { if (this.startSubscription) { this.startSubscription.unsubscribe(); this.startSubscription = null; } if (this.stopSubscription) { this.stopSubscription.unsubscribe(); this.stopSubscription = null; } }; /** * @param {?} evt * @return {?} */ NgxCircularSliderComponent.prototype.handleStartPan = /** * @param {?} evt * @return {?} */ function (evt) { var /** @type {?} */ coords = NgxCircularSliderComponent.extractMouseEventCoords(evt); this.setCircleCenter(); var /** @type {?} */ currentAngleStop = (this.startAngle + this.angleLength) % (2 * Math.PI); var /** @type {?} */ newAngle = Math.atan2(coords.y - this.circleCenterY, coords.x - this.circleCenterX) + Math.PI / 2; if (newAngle < 0) { newAngle += 2 * Math.PI; } var /** @type {?} */ newAngleLength = currentAngleStop - newAngle; if (newAngleLength < 0) { newAngleLength += 2 * Math.PI; } this.startAngle = newAngle; this.angleLength = newAngleLength % (2 * Math.PI); this.onUpdate(); }; /** * @param {?} evt * @return {?} */ NgxCircularSliderComponent.prototype.handleStopPan = /** * @param {?} evt * @return {?} */ function (evt) { var /** @type {?} */ coords = NgxCircularSliderComponent.extractMouseEventCoords(evt); this.setCircleCenter(); var /** @type {?} */ newAngle = Math.atan2(coords.y - this.circleCenterY, coords.x - this.circleCenterX) + Math.PI / 2; var /** @type {?} */ newAngleLength = (newAngle - this.startAngle) % (2 * Math.PI); if (newAngleLength < 0) { newAngleLength += 2 * Math.PI; } this.angleLength = newAngleLength; this.onUpdate(); }; /** * @return {?} */ NgxCircularSliderComponent.prototype.calcStartAndStop = /** * @return {?} */ function () { this.start = this.calculateArcCircle(0, this.props.segments, this.props.radius, this.startAngle, this.angleLength); this.stop = this.calculateArcCircle(this.props.segments - 1, this.props.segments, this.props.radius, this.startAngle, this.angleLength); }; /** * @param {?} index * @param {?} segments * @param {?} gradientColorFrom * @param {?} gradientColorTo * @return {?} */ NgxCircularSliderComponent.prototype.calculateArcColor = /** * @param {?} index * @param {?} segments * @param {?} gradientColorFrom * @param {?} gradientColorTo * @return {?} */ function (index, segments, gradientColorFrom, gradientColorTo) { var /** @type {?} */ interpolate = interpolateHcl(gradientColorFrom, gradientColorTo); return { fromColor: interpolate(index / segments), toColor: interpolate((index + 1) / segments), }; }; /** * @param {?} indexInput * @param {?} segments * @param {?} radius * @param {?=} startAngleInput * @param {?=} angleLengthInput * @return {?} */ NgxCircularSliderComponent.prototype.calculateArcCircle = /** * @param {?} indexInput * @param {?} segments * @param {?} radius * @param {?=} startAngleInput * @param {?=} angleLengthInput * @return {?} */ function (indexInput, segments, radius, startAngleInput, angleLengthInput) { if (startAngleInput === void 0) { startAngleInput = 0; } if (angleLengthInput === void 0) { angleLengthInput = 2 * Math.PI; } // Add 0.0001 to the possible angle so when start = stop angle, whole circle is drawn var /** @type {?} */ startAngle = startAngleInput % (2 * Math.PI); var /** @type {?} */ angleLength = angleLengthInput % (2 * Math.PI); var /** @type {?} */ index = indexInput + 1; var /** @type {?} */ fromAngle = angleLength / segments * (index - 1) + startAngle; var /** @type {?} */ toAngle = angleLength / segments * index + startAngle; var /** @type {?} */ fromX = radius * Math.sin(fromAngle); var /** @type {?} */ fromY = -radius * Math.cos(fromAngle); var /** @type {?} */ realToX = radius * Math.sin(toAngle); var /** @type {?} */ realToY = -radius * Math.cos(toAngle); // add 0.005 to start drawing a little bit earlier so segments stick together var /** @type {?} */ toX = radius * Math.sin(toAngle + 0.005); var /** @type {?} */ toY = -radius * Math.cos(toAngle + 0.005); return { fromX: fromX, fromY: fromY, toX: toX, toY: toY, realToX: realToX, realToY: realToY }; }; /** * @return {?} */ NgxCircularSliderComponent.prototype.createSegments = /** * @return {?} */ function () { this.segments = []; for (var /** @type {?} */ i = 0; i < this.props.segments; i++) { var /** @type {?} */ id = i; var /** @type {?} */ colors = this.calculateArcColor(id, this.props.segments, this.props.gradientColorFrom, this.props.gradientColorTo); var /** @type {?} */ arcs = this.calculateArcCircle(id, this.props.segments, this.props.radius, this.startAngle, this.angleLength); this.segments.push({ id: id, d: "M " + arcs.fromX.toFixed(2) + " " + arcs.fromY.toFixed(2) + " A " + this.props.radius + " " + this.props.radius + " \n 0 0 1 " + arcs.toX.toFixed(2) + " " + arcs.toY.toFixed(2), colors: Object.assign({}, colors), arcs: Object.assign({}, arcs) }); } }; /** * @return {?} */ NgxCircularSliderComponent.prototype.setCircleCenter = /** * @return {?} */ function () { // todo: nicer solution to use document.body? var /** @type {?} */ bodyRect = document.body.getBoundingClientRect(); var /** @type {?} */ elemRect = this.circle.nativeElement.getBoundingClientRect(); var /** @type {?} */ px = elemRect.left - bodyRect.left; var /** @type {?} */ py = elemRect.top - bodyRect.top; var /** @type {?} */ halfOfContainer = this.getContainerWidth() / 2; this.circleCenterX = px + halfOfContainer; this.circleCenterY = py + halfOfContainer; }; /** * @return {?} */ NgxCircularSliderComponent.prototype.getContainerWidth = /** * @return {?} */ function () { var _a = this.props, strokeWidth = _a.strokeWidth, radius = _a.radius; return strokeWidth + radius * 2 + 2; }; /** * @param {?} index * @return {?} */ NgxCircularSliderComponent.prototype.getGradientId = /** * @param {?} index * @return {?} */ function (index) { return "gradient" + index; }; /** * @param {?} index * @return {?} */ NgxCircularSliderComponent.prototype.getGradientUrl = /** * @param {?} index * @return {?} */ function (index) { return "url(#gradient" + index + ")"; }; /** * @return {?} */ NgxCircularSliderComponent.prototype.getTranslate = /** * @return {?} */ function () { return " translate(\n " + (this.props.strokeWidth / 2 + this.props.radius + 1) + ",\n " + (this.props.strokeWidth / 2 + this.props.radius + 1) + " )"; }; /** * @param {?} x * @param {?} y * @return {?} */ NgxCircularSliderComponent.prototype.getTranslateFrom = /** * @param {?} x * @param {?} y * @return {?} */ function (x, y) { return " translate(" + x + ", " + y + ")"; }; NgxCircularSliderComponent.decorators = [ { type: Component, args: [{ selector: 'ngx-cs-slider', template: "<svg #circle [attr.height]=\"getContainerWidth()\" [attr.width]=\"getContainerWidth()\"> <defs> <linearGradient *ngFor=\"let segment of segments\" [attr.key]=\"segment.id\" [attr.id]=\"getGradientId(segment.id)\" [attr.x1]=\"segment.arcs.fromX.toFixed(2)\" [attr.y1]=\"segment.arcs.fromY.toFixed(2)\" [attr.x2]=\"segment.arcs.toX.toFixed(2)\" [attr.y2]=\"segment.arcs.toY.toFixed(2)\"> <stop offset=\"0%\" [attr.stop-color]=\"segment.colors.fromColor\"/> <stop offset=\"1\" [attr.stop-color]=\"segment.colors.toColor\"/> </linearGradient> </defs> <!-- circle-donut --> <g [attr.transform]=\"getTranslate()\"> <circle [attr.r]=\"props.radius\" [attr.stroke-width]=\"props.strokeWidth\" [attr.stroke]=\"props.bgCircleColor\" fill=\"transparent\"/> <g ngx-cs-clock-face *ngIf=\"props.showClockFace\" [radius]=\"(props.radius-props.strokeWidth/2)\" [stroke]=\"props.clockFaceColor\"> </g> <path *ngFor=\"let segment of segments\" [attr.d]=\"segment.d\" [attr.key]=\"segment.id\" [attr.stroke-width]=\"this.props.strokeWidth\" [attr.stroke]=\"getGradientUrl(segment.id)\" fill=\"transparent\"/> <!-- start icon --> <g #startIcon [attr.fill]=\"this.props.gradientColorFrom\" [attr.transform]=\"getTranslateFrom(start.fromX, start.fromY)\"> <circle [attr.r]=\"((this.props.strokeWidth - 1) / 2)\" [attr.fill]=\"this.props.bgCircleColor\" [attr.stroke]=\"this.props.gradientColorFrom\" stroke-width=\"1\"/> <ng-content select=\".acs-start-icon\"></ng-content> </g> <!-- stop icon --> <g #stopIcon [attr.fill]=\"props.gradientColorTo\" [attr.transform]=\"getTranslateFrom(stop.toX, stop.toY)\"> <circle [attr.r]=\"((this.props.strokeWidth - 1) / 2)\" [attr.fill]=\"this.props.bgCircleColor\" [attr.stroke]=\"this.props.gradientColorTo\" stroke-width=\"1\"/> <ng-content select=\".acs-stop-icon\"></ng-content> </g> </g> </svg> ", styles: [":host { display: inline-block; } "] },] }, ]; /** @nocollapse */ NgxCircularSliderComponent.ctorParameters = function () { return []; }; NgxCircularSliderComponent.propDecorators = { "props": [{ type: Input },], "startAngle": [{ type: Input },], "angleLength": [{ type: Input },], "update": [{ type: Output },], "circle": [{ type: ViewChild, args: ['circle',] },], "stopIcon": [{ type: ViewChild, args: ['stopIcon',] },], "startIcon": [{ type: ViewChild, args: ['startIcon',] },], }; return NgxCircularSliderComponent; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ var DEFAULT_RANGE = 48; var DEFAULT_TIME_RANGE = 12; /** * @record */ var NgXCSClockFaceComponent = /** @class */ (function () { function NgXCSClockFaceComponent() { this.clockLines = []; this.clockTexts = []; } /** * @return {?} */ NgXCSClockFaceComponent.prototype.ngOnChanges = /** * @return {?} */ function () { this.faceRadius = this.radius - 5; this.textRadius = this.radius - 26; this.createClockLines(); this.createClockTexts(); }; /** * @return {?} */ NgXCSClockFaceComponent.prototype.createClockLines = /** * @return {?} */ function () { for (var /** @type {?} */ i = 0; i < DEFAULT_RANGE; i++) { var /** @type {?} */ cos = Math.cos(2 * Math.PI / DEFAULT_RANGE * i); var /** @type {?} */ sin = Math.sin(2 * Math.PI / DEFAULT_RANGE * i); this.clockLines.push({ id: i, strokeWidth: (i % 4 === 0 ? 3 : 1), x1: cos * this.faceRadius, y1: sin * this.faceRadius, x2: cos * (this.faceRadius - 7), y2: sin * (this.faceRadius - 7) }); } }; /** * @return {?} */ NgXCSClockFaceComponent.prototype.createClockTexts = /** * @return {?} */ function () { for (var /** @type {?} */ i = 0; i < DEFAULT_TIME_RANGE; i++) { this.clockTexts.push({ id: i, x: this.textRadius * Math.cos(2 * Math.PI / 12 * i - Math.PI / 2 + Math.PI / 6), y: this.textRadius * Math.sin(2 * Math.PI / 12 * i - Math.PI / 2 + Math.PI / 6) }); } }; NgXCSClockFaceComponent.decorators = [ { type: Component, args: [{ selector: '[ngx-cs-clock-face]', template: "<svg:g> <line *ngFor=\"let clockLine of clockLines\" [attr.key]=\"clockLine.id\" [attr.stroke]=\"stroke\" [attr.stroke-width]=\"clockLine.strokeWidth\" [attr.x1]=\"clockLine.x1\" [attr.y1]=\"clockLine.y1\" [attr.x2]=\"clockLine.x2\" [attr.y2]=\"clockLine.y2\"> </line> <svg:g transform=\"translate(0, 5)\"> <text *ngFor=\"let clockText of clockTexts\" [attr.key]=\"clockText.id\" [attr.fill]=\"stroke\" font-size=\"16\" text-anchor=\"middle\" [attr.x]=\"clockText.x\" [attr.y]=\"clockText.y\"> {{clockText.id + 1}} </text> </svg:g> </svg:g> ", styles: [""] },] }, ]; /** @nocollapse */ NgXCSClockFaceComponent.ctorParameters = function () { return []; }; NgXCSClockFaceComponent.propDecorators = { "radius": [{ type: Input },], "stroke": [{ type: Input },], }; return NgXCSClockFaceComponent; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ var NgxCircularSliderModule = /** @class */ (function () { function NgxCircularSliderModule() { } NgxCircularSliderModule.decorators = [ { type: NgModule, args: [{ imports: [ CommonModule ], declarations: [ NgxCircularSliderComponent, NgXCSClockFaceComponent ], exports: [ NgxCircularSliderComponent ] },] }, ]; return NgxCircularSliderModule; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ export { NgxCircularSliderComponent, NgxCircularSliderModule };