UNPKG

@toast-ui/chart

Version:

TOAST UI Application: Chart

154 lines (153 loc) 5.85 kB
import { range, isInteger, isString, isNumber, isNull } from "./utils"; import { DEFAULT_LABEL_TEXT } from "../brushes/label"; import { TICK_SIZE } from "../brushes/axis"; const LINE_HEIGHT_NORMAL = 1.2; const ctx = document.createElement('canvas').getContext('2d'); export function getTextWidth(text, font = DEFAULT_LABEL_TEXT) { ctx.font = font; return Math.ceil(ctx.measureText(text).width); } /* * Calculate height of canvas text * https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics * */ export function getTextHeight(text, font = DEFAULT_LABEL_TEXT) { ctx.font = font; const { actualBoundingBoxAscent, actualBoundingBoxDescent } = ctx.measureText(text); const validActualBoundingBox = isNumber(actualBoundingBoxAscent) && isNumber(actualBoundingBoxDescent); return validActualBoundingBox ? Math.ceil(Math.abs(actualBoundingBoxAscent) + Math.abs(actualBoundingBoxDescent)) + 1 : getFontHeight(font); } export function getFontHeight(font = DEFAULT_LABEL_TEXT) { const fontSize = font.match(/\d+(?=px)/); return parseInt(String(Number(fontSize) * LINE_HEIGHT_NORMAL), 10); } export function getAxisLabelAnchorPoint(labelHeight) { return crispPixel(TICK_SIZE * 2 + labelHeight / 2); } function getDecimalLength(value) { var _a, _b; return _b = (_a = String(value).split('.')[1]) === null || _a === void 0 ? void 0 : _a.length, (_b !== null && _b !== void 0 ? _b : 0); } function findMultipleNum(...args) { const underPointLens = args.map((value) => getDecimalLength(value)); const underPointLen = Math.max(...underPointLens); return Math.pow(10, underPointLen); } export function add(a, b) { const multipleNum = findMultipleNum(a, b); return (a * multipleNum + b * multipleNum) / multipleNum; } export function multiply(a, b) { const multipleNum = findMultipleNum(a, b); return (a * multipleNum * (b * multipleNum)) / (multipleNum * multipleNum); } export function divide(a, b) { const multipleNum = findMultipleNum(a, b); return (a * multipleNum) / (b * multipleNum); } export function sum(values) { const copyArr = values.slice(); copyArr.unshift(0); return copyArr.reduce((base, value) => add(parseFloat(String(base)), parseFloat(String(value)))); } export function divisors(value) { const result = []; for (let a = 2, b; a * a <= value; a += 1) { if (value % a === 0) { b = value / a; result.push(a); if (b !== a) { result.push(b); } } } return result.sort((prev, next) => prev - next); } export function makeLabelsFromLimit(limit, stepSize, isDateType) { const multipleNum = findMultipleNum(stepSize); const min = Math.round(limit.min * multipleNum); const max = Math.round(limit.max * multipleNum); const labels = range(min, max + 1, stepSize * multipleNum); return labels.map((label) => { return String(isDateType ? new Date(label) : label / multipleNum); }); } export function makeTickPixelPositions(size, count, additionalPosition = 0, remainLastBlockIntervalPosition = 0) { let positions = []; if (count > 0) { positions = range(0, count).map((index) => { const ratio = index === 0 ? 0 : index / (count - 1); return ratio * size + additionalPosition; }); } if (remainLastBlockIntervalPosition) { positions.push(remainLastBlockIntervalPosition); } return positions; } export function crispPixel(pixel, thickness = 1) { const halfThickness = thickness / 2; return thickness % 2 ? (isInteger(pixel) ? pixel : Math.round(pixel - halfThickness)) + halfThickness : Math.round(pixel); } function getControlPoints(prev, cur, next) { // http://scaledinnovation.com/analytics/splines/aboutSplines.html const TENSION = 0.333; const { x: x0, y: y0 } = prev; const { x: x1, y: y1 } = cur; const { x: x2, y: y2 } = next; const d12 = getDistance(next, cur); const d01 = getDistance(cur, prev); const fa = (TENSION * d01) / (d01 + d12) || 0; // scaling factor for triangle Ta const fb = (TENSION * d12) / (d01 + d12) || 0; // ditto for Tb, simplifies to fb=t-fa return { prev: { x: x1 - fa * (x2 - x0), y: y1 - fa * (y2 - y0), }, next: { x: x1 + fb * (x2 - x0), y: y1 + fb * (y2 - y0) }, }; } export function setSplineControlPoint(points) { for (let i = 0, pointsSize = points.length, prev = points[0]; i < pointsSize; i += 1) { const point = points[i]; if (isNull(point)) { prev = points[i + 1]; continue; } const next = points[Math.min(i + 1, pointsSize - 1) % pointsSize]; if (prev && next) { point.controlPoint = getControlPoints(prev, point, next); } prev = point; } } export function getValueRatio(value, { min, max }) { if (max === min) { return 0; } return (value - min) / (max - min); } export function getDistance(point1, point2) { return Math.sqrt(Math.pow((point2.x - point1.x), 2) + Math.pow((point2.y - point1.y), 2)); } export function getMaxLengthLabelWidth(labels) { const maxLengthLabel = labels.reduce((acc, cur) => (acc.length > cur.length ? acc : cur), ''); return getTextWidth(maxLengthLabel); } export function getXPosition(axisData, offsetSize, value, dataIndex) { const { pointOnColumn, tickDistance, labelRange } = axisData; let x; if (labelRange) { const xValue = isString(value) ? Number(new Date(value)) : Number(value); const xValueRatio = getValueRatio(xValue, labelRange); x = xValueRatio * offsetSize; } else { x = tickDistance * dataIndex + (pointOnColumn ? tickDistance / 2 : 0); } return x; }