UNPKG

@nativescript-community/ui-chart

Version:

A powerful chart / graph plugin, supporting line, bar, pie, radar, bubble, and candlestick charts as well as scaling, panning and animations.

379 lines 15.4 kB
import { Align } from '@nativescript-community/ui-canvas'; import { LimitLabelPosition } from '../components/LimitLine'; import { AxisDependency, YAxisLabelPosition } from '../components/YAxis'; import { Utils } from '../utils/Utils'; import { AxisRenderer } from './AxisRenderer'; export class YAxisRenderer extends AxisRenderer { constructor(viewPortHandler, yAxis, trans) { super(viewPortHandler, trans, yAxis); this.mYAxis = yAxis; } get zeroLinePaint() { if (!this.mZeroLinePaint) { this.mZeroLinePaint = Utils.getTemplatePaint('gray-stroke'); } return this.mZeroLinePaint; } /** * draws the y-axis labels to the screen */ renderAxisLabels(c) { const axis = this.mYAxis; if (!axis.enabled || !axis.drawLabels) return; const positions = this.getTransformedPositions(); const paint = this.axisLabelsPaint; paint.setFont(axis.typeface); paint.setColor(axis.textColor); const xoffset = axis.xOffset; const yoffset = Utils.calcTextHeight(paint, 'A') / 2.5 + axis.yOffset; const dependency = axis.axisDependency; const labelPosition = axis.position; let xPos = 0; let offsetLeft = 0; let rect; if (this.mAxis.ignoreOffsets) { rect = this.mViewPortHandler.chartRect; } else { rect = this.mViewPortHandler.contentRect; offsetLeft = this.mViewPortHandler.offsetLeft; } if (dependency === AxisDependency.LEFT) { if (labelPosition === YAxisLabelPosition.OUTSIDE_CHART) { paint.setTextAlign(Align.RIGHT); xPos = offsetLeft - xoffset; } else { paint.setTextAlign(Align.LEFT); xPos = offsetLeft + xoffset; } } else { if (labelPosition === YAxisLabelPosition.OUTSIDE_CHART) { paint.setTextAlign(Align.LEFT); xPos = rect.right + xoffset; } else { paint.setTextAlign(Align.RIGHT); xPos = rect.right - xoffset; } } this.drawYLabels(c, xPos, positions, yoffset); if (dependency === AxisDependency.LEFT) { this.drawMarkTick(c, rect.left, positions, -xoffset / 2); } else { this.drawMarkTick(c, rect.right, positions, +xoffset / 2); } } renderAxisLine(c) { const axis = this.mYAxis; if (!axis.enabled || !axis.drawAxisLine || axis.axisLineWidth === 0 || axis.mEntryCount === 0) return; const paint = this.axisLinePaint; paint.setColor(axis.axisLineColor); paint.setStrokeWidth(axis.axisLineWidth); const rect = this.mAxis.ignoreOffsets ? this.mViewPortHandler.chartRect : this.mViewPortHandler.contentRect; if (axis.axisDependency === AxisDependency.LEFT) { c.drawLine(rect.left, rect.top, rect.left, rect.bottom, paint); } else { c.drawLine(rect.right, rect.top, rect.right, rect.bottom, paint); } } /** * draws the y-labels on the specified x-position * * @param fixedPosition * @param positions */ drawYLabels(c, fixedPosition, positions, offset) { const axis = this.mYAxis; const customRender = axis.customRenderer; const customRenderFunction = customRender && customRender.drawLabel; const from = axis.drawBottomYLabelEntry ? 0 : 1; const to = axis.drawTopYLabelEntry ? axis.mEntryCount : axis.mEntryCount - 1; // draw const paint = this.axisLabelsPaint; const x = fixedPosition; for (let i = from; i < to; i++) { const text = axis.getFormattedLabel(i); if (text) { const y = positions[i * 2 + 1] + offset; if (customRenderFunction) { customRenderFunction(c, axis, text, x, y, paint); } else { // Utils.drawXAxisValue(c, text, x, y, paint, anchor, angleDegrees); c.drawText(text, x, y, paint); } } } } /** * Draw mark tickets * @param c * @param fixedPosition * @param positions * @param length */ drawMarkTick(c, fixedPosition, positions, ticklength) { const mYAxis = this.mYAxis; if (!mYAxis.drawMarkTicks) return; const from = mYAxis.drawBottomYLabelEntry ? 0 : 1; const to = mYAxis.drawTopYLabelEntry ? mYAxis.mEntryCount : mYAxis.mEntryCount - 1; // draw const paint = this.axisLinePaint; for (let i = from; i < to; i++) { c.drawLine(fixedPosition, positions[i * 2 + 1], fixedPosition + ticklength, positions[i * 2 + 1], paint); } } // protected mRenderGridLinesPath = new Path(); /** * Draws the grid line at the specified position using the provided path. * * @param c * @param rect * @param x * @param y * @param axisValue */ drawGridLine(c, rect, x, y, axisValue, paint, customRendererFunc) { if (customRendererFunc) { customRendererFunc(c, this.mAxis, rect, x, y, axisValue, paint); } else { c.drawLine(rect.left, y, rect.right, y, paint); } } renderGridLines(c) { const axis = this.mYAxis; if (!axis.enabled || !axis.drawGridLinesBehindData) return; if (axis.drawGridLines) { const clipRestoreCount = c.save(); c.clipRect(this.getGridClippingRect()); const positions = this.getTransformedPositions(); const paint = this.gridPaint; paint.setColor(axis.gridColor); paint.setStrokeWidth(axis.gridLineWidth); paint.setPathEffect(axis.gridDashPathEffect); const rect = this.mAxis.ignoreOffsets ? this.mViewPortHandler.chartRect : this.mViewPortHandler.contentRect; const customRender = axis.customRenderer; const customRenderFunction = customRender && customRender.drawGridLine; for (let i = 0; i < positions.length; i += 2) { this.drawGridLine(c, rect, positions[i], positions[i + 1], axis.mEntries[i / 2], paint, customRenderFunction); } c.restoreToCount(clipRestoreCount); } if (axis.drawZeroLine) { this.drawZeroLine(c); } } getGridClippingRect() { const rect = this.mAxis.ignoreOffsets ? this.mViewPortHandler.chartRect : this.mViewPortHandler.contentRect; const gridClippingRect = Utils.getTempRectF(); gridClippingRect.set(rect); gridClippingRect.inset(0, -this.mAxis.gridLineWidth); return gridClippingRect; } /** * Transforms the values contained in the axis entries to screen pixels and returns them in form of a let array * of x- and y-coordinates. */ getTransformedPositions() { const axis = this.mYAxis; const length = axis.mEntryCount * 2; if (!this.mGetTransformedPositionsBuffer || this.mGetTransformedPositionsBuffer.length !== length) { this.mGetTransformedPositionsBuffer = Utils.createArrayBuffer(length); } const positions = this.mGetTransformedPositionsBuffer; for (let i = 0; i < length; i += 2) { // only fill y values, x values are not needed for y-labels positions[i] = 0; positions[i + 1] = axis.mEntries[i / 2]; } const result = Utils.pointsFromBuffer(positions); this.transformer.pointValuesToPixel(result); return Utils.nativeArrayToArray(result); } /** * Draws the zero line. */ drawZeroLine(c) { const axis = this.mYAxis; const customRender = axis.customRenderer; const customRendererFunc = customRender && customRender.drawZeroLine; const clipRestoreCount = c.save(); const rect = this.mAxis.ignoreOffsets ? this.mViewPortHandler.chartRect : this.mViewPortHandler.contentRect; const zeroLineClippingRectl = Utils.getTempRectF(); zeroLineClippingRectl.set(rect); zeroLineClippingRectl.inset(0, -axis.zeroLineWidth); c.clipRect(zeroLineClippingRectl); // draw zero line const pos = this.transformer.getPixelForValues(0, 0); const paint = this.zeroLinePaint; paint.setColor(axis.zeroLineColor); paint.setStrokeWidth(axis.zeroLineWidth); const zeroLinePath = Utils.getTempPath(); zeroLinePath.reset(); zeroLinePath.moveTo(rect.left, pos.y); zeroLinePath.lineTo(rect.right, pos.y); // draw a path because lines don't support dashing on lower android versions if (customRendererFunc) { customRendererFunc(c, axis, pos, zeroLinePath, paint); } else { c.drawPath(zeroLinePath, paint); } c.restoreToCount(clipRestoreCount); } /** * Draws the LimitLines associated with this axis to the screen. * * @param c */ renderLimitLines(c) { const axis = this.mYAxis; if (!axis.drawLimitLines || !axis.enabled) { return; } const limitLines = axis.limitLines; if (!limitLines || limitLines.length <= 0) return; const pts = Utils.getTempArray(2); pts[0] = 0; pts[1] = 0; let offsetLeft = 0; let rect; if (axis.ignoreOffsets) { rect = this.mViewPortHandler.chartRect; } else { rect = this.mViewPortHandler.contentRect; offsetLeft = this.mViewPortHandler.offsetLeft; } const customRender = axis.customRenderer; const customRendererFunc = customRender && customRender.drawLimitLine; const clipToContent = axis.clipLimitLinesToContent; for (let i = 0; i < limitLines.length; i++) { const l = limitLines[i]; if (!l.enabled) continue; const lineWidth = l.lineWidth; if (clipToContent) { c.save(); const clipRect = Utils.getTempRectF(); clipRect.set(rect); clipRect.inset(0, -lineWidth); c.clipRect(clipRect); } const paint = this.limitLinePaint; paint.setColor(l.lineColor); paint.setStrokeWidth(lineWidth); paint.setPathEffect(l.dashPathEffect); pts[1] = l.limit; this.transformer.pointValuesToPixel(pts); if (lineWidth > 0) { if (customRendererFunc) { customRendererFunc(c, axis, l, rect, pts[1], paint); } else { c.drawLine(rect.left, pts[1], rect.right, pts[1], paint); } } const label = l.label; // if drawing the limit-value label is enabled if (label && label !== '') { paint.setStyle(l.textStyle); paint.setPathEffect(null); paint.setColor(l.textColor); paint.setFont(l.typeface); paint.setStrokeWidth(0.5); let size = Utils.calcTextSize(paint, label); const xOffset = l.xOffset; const yOffset = l.lineWidth + l.yOffset; const position = l.labelPosition; const needsSize = l.ensureVisible; if (needsSize) { size = Utils.calcTextSize(paint, label); } switch (position) { case LimitLabelPosition.RIGHT_TOP: { paint.setTextAlign(Align.RIGHT); const x = rect.right - xOffset; let y = pts[1] - yOffset; if (l.ensureVisible && y < size.height) { y -= size.height; } c.drawText(label, x, y, paint); break; } case LimitLabelPosition.RIGHT_BOTTOM: { paint.setTextAlign(Align.RIGHT); const x = rect.right - xOffset; let y = pts[1] + yOffset + size.height; if (l.ensureVisible && y > rect.bottom - size.height) { y += size.height; } c.drawText(label, x, y, paint); break; } case LimitLabelPosition.CENTER_TOP: { const x = rect.right - xOffset; let y = pts[1] - yOffset; if (l.ensureVisible && y < size.height) { y -= size.height; } c.drawText(label, x, y, paint); c.drawText(label, rect.right, pts[1] - yOffset + size.height, paint); break; } case LimitLabelPosition.CENTER_BOTTOM: { paint.setTextAlign(Align.CENTER); const x = rect.right - xOffset; let y = pts[1] + yOffset + size.height; if (l.ensureVisible && y > rect.bottom - size.height) { y += size.height; } c.drawText(label, x, y, paint); c.drawText(label, rect.right, pts[1] + yOffset, paint); break; } case LimitLabelPosition.LEFT_TOP: { const x = offsetLeft + xOffset; let y = pts[1] - yOffset; if (l.ensureVisible && x > rect.right - size.width) { paint.setTextAlign(Align.RIGHT); } if (l.ensureVisible && y < size.height) { y -= size.height; } c.drawText(label, x, y, paint); break; } case LimitLabelPosition.LEFT_BOTTOM: { paint.setTextAlign(Align.LEFT); const x = offsetLeft + xOffset; let y = pts[1] + yOffset + size.height; if (l.ensureVisible && x > rect.right - size.width) { paint.setTextAlign(Align.RIGHT); } if (l.ensureVisible && y > rect.bottom - size.height) { y += size.height; } c.drawText(label, x, y, paint); c.drawText(label, x, y, paint); break; } } } if (clipToContent) { c.restore(); } } } } //# sourceMappingURL=YAxisRenderer.js.map