UNPKG

@syncfusion/ej2-progressbar

Version:
290 lines (280 loc) 16.1 kB
import { ProgressBar } from '../progressbar'; import { lineCapRadius, completeAngle } from '../model/constant'; import { getPathArc, Pos, degreeToLocation } from '../utils/helper'; import { PathOption, LinearGradient, GradientColor } from '@syncfusion/ej2-svg-base'; import { RangeColorModel } from '../model'; /** * Progressbar Segment */ export class Segment { /** * Creates a linear segment element for the progress bar. * * @param {ProgressBar} progress - The progress bar control. * @param {string} id - The id of the segment element. * @param {number} width - The width of the segment. * @param {number} opacity - The opacity of the segment. * @param {number} thickness - The thickness of the segment. * @param {number} progressWidth - The width of the progress. * @returns {Element} - The created linear segment element. */ public createLinearSegment( progress: ProgressBar, id: string, width: number, opacity: number, thickness: number, progressWidth: number ): Element { let locX: number = (progress.enableRtl) ? ((progress.cornerRadius === 'Round') ? (progress.progressRect.x + progress.progressRect.width) - ((lineCapRadius / 2) * thickness) : (progress.progressRect.x + progress.progressRect.width)) : ((progress.cornerRadius === 'Round') ? (progress.progressRect.x + (lineCapRadius / 2) * thickness) : progress.progressRect.x); const locY: number = (progress.progressRect.y + (progress.progressRect.height / 2)); const gapWidth: number = (progress.gapWidth || progress.themeStyle.linearGapWidth); const avlWidth: number = progressWidth / progress.segmentCount; let avlSegWidth: number = (progressWidth - ((progress.segmentCount - 1) * gapWidth)); avlSegWidth = (avlSegWidth - ((progress.cornerRadius === 'Round') ? progress.segmentCount * (lineCapRadius * thickness) : 0)) / progress.segmentCount; const gap: number = (progress.cornerRadius === 'Round') ? (gapWidth + (lineCapRadius * thickness)) : gapWidth; const segmentGroup: Element = progress.renderer.createGroup({ 'id': progress.element.id + id }); const count: number = Math.ceil(width / avlWidth); let segWidth: number; let color: string; let j: number = 0; let option: PathOption; let segmentPath: Element; let tolWidth: number = (progress.cornerRadius === 'Round') ? (width - (lineCapRadius * thickness)) : width; const linearThickness: number = progress.progressThickness || progress.themeStyle.linearProgressThickness; for (let i: number = 0; i < count; i++) { segWidth = (tolWidth < avlSegWidth) ? tolWidth : avlSegWidth; if (j < progress.segmentColor.length) { color = progress.segmentColor[j as number]; j++; } else { j = 0; color = progress.segmentColor[j as number]; j++; } option = new PathOption( progress.element.id + id + i, 'none', linearThickness, color, opacity, '0', this.getLinearSegmentPath(locX, locY, segWidth, progress.enableRtl) ); segmentPath = progress.renderer.drawPath(option); if (progress.cornerRadius === 'Round') { segmentPath.setAttribute('stroke-linecap', 'round'); } segmentGroup.appendChild(segmentPath); locX += (progress.enableRtl) ? -avlSegWidth - gap : avlSegWidth + gap; tolWidth -= avlSegWidth + gap; tolWidth = (tolWidth < 0) ? 0 : tolWidth; } return segmentGroup; } private getLinearSegmentPath(x: number, y: number, width: number, enableRtl: boolean): string { return 'M' + ' ' + x + ' ' + y + ' ' + 'L' + (x + ((enableRtl) ? -width : width)) + ' ' + y; } /** * Creates a circular segment element for the progress bar. * * @param {ProgressBar} progress - The progress bar control. * @param {string} id - The id of the segment element. * @param {number} x - The x-coordinate of the center of the circle. * @param {number} y - The y-coordinate of the center of the circle. * @param {number} r - The radius of the circle. * @param {number} value - The value determining the angle of the segment. * @param {number} opacity - The opacity of the segment. * @param {number} thickness - The thickness of the segment. * @param {number} totalAngle - The total angle covered by the progress. * @param {number} progressWidth - The width of the progress. * @returns {Element} - The created circular segment element. */ public createCircularSegment( progress: ProgressBar, id: string, x: number, y: number, r: number, value: number, opacity: number, thickness: number, totalAngle: number, progressWidth: number ): Element { let start: number = progress.startAngle; let end: number = this.widthToAngle(progress.minimum, progress.maximum, value, progress.totalAngle); end -= (progress.cornerRadius === 'Round' && progress.totalAngle === completeAngle) ? this.widthToAngle(0, progressWidth, ((lineCapRadius / 2) * thickness), totalAngle) : 0; let size: number = (progressWidth - ( (progress.totalAngle === completeAngle) ? progress.segmentCount : progress.segmentCount - 1) * (progress.gapWidth || progress.themeStyle.circularGapWidth) ); size = (size - ((progress.cornerRadius === 'Round') ? (((progress.totalAngle === completeAngle) ? progress.segmentCount : progress.segmentCount - 1) * lineCapRadius * thickness) : 0)) / progress.segmentCount; let avlTolEnd: number = this.widthToAngle(0, progressWidth, (progressWidth / progress.segmentCount), totalAngle); avlTolEnd -= (progress.cornerRadius === 'Round' && progress.totalAngle === completeAngle) ? this.widthToAngle(0, progressWidth, ((lineCapRadius / 2) * thickness), totalAngle) : 0; const avlEnd: number = this.widthToAngle(0, progressWidth, size, totalAngle); let gap: number = this.widthToAngle( 0, progressWidth, (progress.gapWidth || progress.themeStyle.circularGapWidth), totalAngle ); gap += (progress.cornerRadius === 'Round') ? this.widthToAngle(0, progressWidth, (lineCapRadius * thickness), totalAngle) : 0; const segmentGroup: Element = progress.renderer.createGroup({ 'id': progress.element.id + id }); const gapCount: number = Math.floor(end / avlTolEnd); const count: number = Math.ceil((end - gap * gapCount) / avlEnd); let segmentPath: string; let circularSegment: Element; let segmentEnd: number; let avlSegEnd: number = (start + ((progress.enableRtl) ? -avlEnd : avlEnd)) % 360; let color: string; let j: number = 0; let option: PathOption; const circularThickness: number = progress.progressThickness || progress.themeStyle.circularProgressThickness; for (let i: number = 0; i < count; i++) { segmentEnd = (progress.enableRtl) ? ((progress.startAngle - end > avlSegEnd) ? progress.startAngle - end : avlSegEnd) : ((progress.startAngle + end < avlSegEnd) ? progress.startAngle + end : avlSegEnd ); segmentPath = getPathArc(x, y, r, start, segmentEnd, progress.enableRtl); if (j < progress.segmentColor.length) { color = progress.segmentColor[j as number]; j++; } else { j = 0; color = progress.segmentColor[j as number]; j++; } option = new PathOption( progress.element.id + id + i, 'none', circularThickness, color, opacity, '0', segmentPath ); circularSegment = progress.renderer.drawPath(option); if (progress.cornerRadius === 'Round') { circularSegment.setAttribute('stroke-linecap', 'round'); } segmentGroup.appendChild(circularSegment); start = segmentEnd + ((progress.enableRtl) ? -gap : gap); avlSegEnd += (progress.enableRtl) ? -avlEnd - gap : avlEnd + gap; } return segmentGroup; } private widthToAngle(min: number, max: number, value: number, totalAngle: number): number { const angle: number = ((value - min) / (max - min)) * totalAngle; return angle; } public createLinearRange(totalWidth: number, progress: ProgressBar, progressWidth: number): Element { const posX: number = progress.progressRect.x + ((progress.enableRtl) ? progress.progressRect.width : 0); const startY: number = (progress.progressRect.y + (progress.progressRect.height / 2)); const rangeGroup: Element = progress.renderer.createGroup({ 'id': progress.element.id + '_LinearRangeGroup' }); const range: RangeColorModel[] = progress.rangeColors; const thickness: number = progress.progressThickness || progress.themeStyle.linearProgressThickness; const opacity: number = progress.themeStyle.progressOpacity; const rangeMin: number = progress.minimum; const rangeMax: number = progress.value; const gradX: number = (progress.enableRtl) ? 0.1 : -0.1; let gradient: Element; let validRange: boolean; let rangePath: Element; let option: PathOption; let startPos: number; let endPos: number; let startX: number; let endX: number; let color: string; let endColor: string; for (let i: number = 0; i < range.length; i++) { validRange = (range[i as number].start >= rangeMin && range[i as number].start <= rangeMax && range[i as number].end >= rangeMin && range[i as number].end <= rangeMax); startPos = totalWidth * progress.calculateProgressRange(range[i as number].start, rangeMin, rangeMax); endPos = totalWidth * progress.calculateProgressRange(range[i as number].end, rangeMin, rangeMax); startX = posX + ((progress.enableRtl) ? -startPos : startPos); endX = posX + ((progress.enableRtl) ? -endPos : endPos); startX = (validRange) ? ((progress.isGradient && i > 0) ? startX + gradX : startX) : posX; endX = (validRange) ? endX : posX; color = (progress.isGradient) ? 'url(#lineRangeGrad_' + i + ')' : range[i as number].color; option = new PathOption( progress.element.id + '_LinearRange_' + i, 'none', thickness, color, opacity, '0', 'M' + ' ' + startX + ' ' + startY + ' ' + 'L' + endX + ' ' + startY ); rangePath = progress.renderer.drawPath(option); if (progress.cornerRadius === 'Round' && progressWidth) { rangePath.setAttribute('stroke-linecap', 'round'); } rangeGroup.appendChild(rangePath); if (progress.isGradient) { if (range.length - 1 === i) { endColor = range[i as number].color; } else { endColor = range[i + 1].color; } gradient = this.setLinearGradientColor(i, range[i as number].color, endColor, startX, endX, progress); rangeGroup.appendChild(gradient); } } return rangeGroup; } public createCircularRange(centerX: number, centerY: number, radius: number, progress: ProgressBar): Element { const rangeGroup: Element = progress.renderer.createGroup({ 'id': progress.element.id + '_CircularRangeGroup' }); const range: RangeColorModel[] = progress.rangeColors; const thickness: number = progress.progressThickness || progress.themeStyle.linearProgressThickness; const opacity: number = progress.themeStyle.progressOpacity; const rangeMin: number = progress.minimum; const rangeMax: number = progress.value; const start: number = progress.startAngle; const tolAngle: number = this.widthToAngle(progress.minimum, progress.maximum, progress.value, progress.totalAngle); let gradient: Element; let startAngle: number; let endAngle: number; let rangePath: Element; let isValidRange: boolean; let option: PathOption; let color: string; let endColor: string; for (let i: number = 0; i < range.length; i++) { isValidRange = (range[i as number].start >= rangeMin && range[i as number].start <= rangeMax && range[i as number].end >= rangeMin && range[i as number].end <= rangeMax); startAngle = this.widthToAngle(rangeMin, rangeMax, range[i as number].start, tolAngle); endAngle = this.widthToAngle(rangeMin, rangeMax, range[i as number].end, tolAngle); startAngle = (isValidRange) ? (start + ((progress.enableRtl) ? -startAngle : startAngle)) % 360 : start; endAngle = (isValidRange) ? (start + ((progress.enableRtl) ? -endAngle : endAngle)) % 360 : start; color = (progress.isGradient) ? 'url(#circleRangeGrad_' + i + ')' : range[i as number].color; option = new PathOption( progress.element.id + '_CircularRange_' + i, 'none', thickness, color, opacity, '0', getPathArc(centerX, centerY, radius, startAngle, endAngle, progress.enableRtl) ); rangePath = progress.renderer.drawPath(option); if (progress.cornerRadius === 'Round' && startAngle !== endAngle) { rangePath.setAttribute('stroke-linecap', 'round'); } rangeGroup.appendChild(rangePath); if (progress.isGradient) { if (range.length - 1 === i) { endColor = range[i as number].color; } else { endColor = range[i + 1].color; } gradient = this.setCircularGradientColor( i, range[i as number].color, endColor, startAngle, endAngle, radius, centerX, centerY, progress ); rangeGroup.appendChild(gradient); } } return rangeGroup; } private setLinearGradientColor( id: number, startColor: string, endColor: string, start: number, end: number, progress: ProgressBar ): Element { const stopColor: GradientColor[] = []; const option: LinearGradient = { id: 'lineRangeGrad_' + id + '', x1: start.toString(), x2: end.toString() }; stopColor[0] = { color: startColor, colorStop: '50%' }; stopColor[1] = { color: endColor, colorStop: '100%' }; const linearGradient: Element = progress.renderer.drawGradient('linearGradient', option, stopColor); linearGradient.firstElementChild.setAttribute('gradientUnits', 'userSpaceOnUse'); return linearGradient; } private setCircularGradientColor( id: number, startColor: string, endColor: string, start: number, end: number, rad: number, x: number, y: number, progress: ProgressBar ): Element { const stopColor: GradientColor[] = []; const pos1: Pos = degreeToLocation(x, y, rad, start); const pos2: Pos = degreeToLocation(x, y, rad, end); const option: LinearGradient = { id: 'circleRangeGrad_' + id + '', x1: pos1.x.toString(), x2: pos2.x.toString(), y1: pos1.y.toString(), y2: pos2.y.toString() }; stopColor[0] = { color: startColor, colorStop: '50%' }; stopColor[1] = { color: endColor, colorStop: '100%' }; const linearGradient: Element = progress.renderer.drawGradient('linearGradient', option, stopColor); linearGradient.firstElementChild.setAttribute('gradientUnits', 'userSpaceOnUse'); return linearGradient; } }