@devexperts/dxcharts-lite
Version:
179 lines (178 loc) • 10.3 kB
JavaScript
/*
* Copyright (C) 2019 - 2025 Devexperts Solutions IE Limited
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { getFontFromConfig } from '../../chart.config';
import { drawPriceLabel, drawRoundedRect } from '../../utils/canvas/canvas-drawing-functions.utils';
import { calculateSymbolHeight, calculateTextWidth } from '../../utils/canvas/canvas-font-measure-tool.utils';
import { getLabelTextColorByBackgroundColor } from '../../utils/canvas/canvas-text-functions.utils';
import { round } from '../../utils/math.utils';
export const DEFAULT_PRICE_LABEL_PADDING = 4;
/**
* Draws badge label on Y axis with provided parameters.
* @param ctx - canvas 2D context to draw on
* @param bounds - bounds of Y axis
* @param text - text to draw
* @param centralY - y
* @param config - label styles config
* @param yAxisState
* @param yAxisColors
* @param checkBoundaries
*/
export function drawBadgeLabel(ctx, bounds, text, centralY, config, yAxisState, yAxisColors, checkBoundaries = true) {
var _a, _b, _c, _d, _e, _f, _g;
const align = yAxisState.align;
const textFont = (_a = config.textFont) !== null && _a !== void 0 ? _a : getFontFromConfig(yAxisState);
const bgColor = config.bgColor;
const textColor = (_b = config.textColor) !== null && _b !== void 0 ? _b : getLabelTextColorByBackgroundColor(bgColor, yAxisColors.labelTextColor, yAxisColors.labelInvertedTextColor);
const paddingTop = (_c = config.paddingTop) !== null && _c !== void 0 ? _c : DEFAULT_PRICE_LABEL_PADDING;
const paddingBottom = (_d = config.paddingBottom) !== null && _d !== void 0 ? _d : DEFAULT_PRICE_LABEL_PADDING;
const paddingEnd = (_e = config.paddingEnd) !== null && _e !== void 0 ? _e : DEFAULT_PRICE_LABEL_PADDING;
const halfFontHeight = round(calculateSymbolHeight(textFont, ctx) / 2);
const labelBoxTopY = centralY - halfFontHeight - paddingTop;
const labelBoxBottomY = centralY + halfFontHeight + paddingBottom;
const labelBoxHeight = labelBoxBottomY - labelBoxTopY;
// do not draw, if label is out of bounds
if (checkBoundaries && !checkLabelInBoundaries(centralY, bounds, labelBoxHeight)) {
return;
}
ctx.save();
ctx.fillStyle = bgColor;
ctx.strokeStyle = bgColor;
const labelForeWidth = round(bounds.width / 6);
const x1 = align === 'right' ? bounds.x : bounds.x + bounds.width;
const x2 = align === 'right' ? x1 + labelForeWidth : x1 - labelForeWidth;
const xTextOffset = yAxisState.labelBoxMargin.end;
const marginEnd = xTextOffset - paddingEnd;
// main body label width, without triangle
const width = bounds.width - labelForeWidth - marginEnd;
drawPriceLabel(ctx, x2, labelBoxTopY, x1, labelBoxTopY + round(labelBoxHeight / 2), x2, labelBoxTopY + labelBoxHeight, width, (_g = (_f = yAxisState.typeConfig.badge) === null || _f === void 0 ? void 0 : _f.rounded) !== null && _g !== void 0 ? _g : false, align);
ctx.fillStyle = textColor;
ctx.font = textFont;
const textX = align === 'right'
? bounds.x + bounds.width - calculateTextWidth(text, ctx, textFont) - xTextOffset
: bounds.x + xTextOffset;
ctx.fillText(text, textX, centralY + halfFontHeight - 1); // -1 for font height adjustment
ctx.restore();
}
/**
* Draws rectangle label on Y axis with provided parameters.
* @param ctx - canvas 2D context to draw on
* @param bounds - bounds of Y axis
* @param text - text to draw
* @param centralY - y
* @param config - label styles config
* @param yAxisState
* @param yAxisColors
* @param checkBoundaries
*/
export function drawRectLabel(ctx, bounds, text, centralY, config, yAxisState, yAxisColors, checkBoundaries = true) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
const align = yAxisState.align;
const textFont = (_a = config.textFont) !== null && _a !== void 0 ? _a : getFontFromConfig(yAxisState);
const bgColor = config.bgColor;
const textColor = (_b = config.textColor) !== null && _b !== void 0 ? _b : getLabelTextColorByBackgroundColor(bgColor, yAxisColors.labelTextColor, yAxisColors.labelInvertedTextColor);
const paddings = (_c = yAxisState.typeConfig.rectangle) === null || _c === void 0 ? void 0 : _c.paddings;
const paddingTop = (_e = (_d = config.paddingTop) !== null && _d !== void 0 ? _d : paddings === null || paddings === void 0 ? void 0 : paddings.top) !== null && _e !== void 0 ? _e : DEFAULT_PRICE_LABEL_PADDING;
const paddingBottom = (_g = (_f = config.paddingBottom) !== null && _f !== void 0 ? _f : paddings === null || paddings === void 0 ? void 0 : paddings.bottom) !== null && _g !== void 0 ? _g : DEFAULT_PRICE_LABEL_PADDING;
const paddingEnd = (_j = (_h = config.paddingEnd) !== null && _h !== void 0 ? _h : paddings === null || paddings === void 0 ? void 0 : paddings.end) !== null && _j !== void 0 ? _j : DEFAULT_PRICE_LABEL_PADDING;
const paddingStart = config.paddingStart;
const fontHeight = calculateSymbolHeight(textFont, ctx);
const labelBoxTopY = centralY - fontHeight / 2 - paddingTop;
const labelBoxBottomY = centralY + fontHeight / 2 + paddingBottom;
const labelBoxHeight = labelBoxBottomY - labelBoxTopY;
const rounded = (_k = config.rounded) !== null && _k !== void 0 ? _k : (_l = yAxisState.typeConfig.rectangle) === null || _l === void 0 ? void 0 : _l.rounded;
// do not draw, if label is out of bounds
if (checkBoundaries && !checkLabelInBoundaries(centralY, bounds, labelBoxHeight)) {
return;
}
ctx.save();
ctx.fillStyle = bgColor;
ctx.strokeStyle = bgColor;
const xTextOffset = yAxisState.labelBoxMargin.end;
ctx.font = textFont;
const textWidth = calculateTextWidth(text, ctx, textFont);
const marginEnd = xTextOffset - paddingEnd;
const width = paddingStart !== undefined ? textWidth + paddingStart + paddingEnd : bounds.width - marginEnd;
const x = align === 'right' ? bounds.x + bounds.width - marginEnd - width : bounds.x + marginEnd;
const textX = align === 'right' ? bounds.x + bounds.width - textWidth - xTextOffset : xTextOffset;
if (rounded) {
drawRoundedRect(ctx, x, labelBoxTopY, width, labelBoxHeight, 4, true);
}
else {
ctx.fillRect(x, labelBoxTopY, width, labelBoxHeight);
}
ctx.fillStyle = textColor;
ctx.fillText(text, textX, centralY + fontHeight / 2 - 1); // -1 for font height adjustment
ctx.restore();
}
/**
* Draws rectangle label on Y axis with provided parameters but with transparent background.
* @param ctx - canvas 2D context to draw on
* @param bounds - bounds of Y axis
* @param text - text to draw
* @param centralY - y
* @param config - label styles config
* @param yAxisState
* @param yAxisColors
* @param checkBoundaries
*/
export function drawPlainLabel(ctx, bounds, text, centralY, config, yAxisState, yAxisColors, checkBoundaries = true) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
const align = yAxisState.align;
const textFont = (_a = config.textFont) !== null && _a !== void 0 ? _a : getFontFromConfig(yAxisState);
const bgColor = yAxisColors.backgroundColor;
const textColor = (_b = config.textColor) !== null && _b !== void 0 ? _b : getLabelTextColorByBackgroundColor(bgColor, yAxisColors.labelTextColor, yAxisColors.labelInvertedTextColor);
const paddings = (_c = yAxisState.typeConfig.rectangle) === null || _c === void 0 ? void 0 : _c.paddings;
const paddingTop = (_e = (_d = config.paddingTop) !== null && _d !== void 0 ? _d : paddings === null || paddings === void 0 ? void 0 : paddings.top) !== null && _e !== void 0 ? _e : DEFAULT_PRICE_LABEL_PADDING;
const paddingBottom = (_g = (_f = config.paddingBottom) !== null && _f !== void 0 ? _f : paddings === null || paddings === void 0 ? void 0 : paddings.bottom) !== null && _g !== void 0 ? _g : DEFAULT_PRICE_LABEL_PADDING;
const paddingEnd = (_j = (_h = config.paddingEnd) !== null && _h !== void 0 ? _h : paddings === null || paddings === void 0 ? void 0 : paddings.end) !== null && _j !== void 0 ? _j : DEFAULT_PRICE_LABEL_PADDING;
const paddingStart = config.paddingStart;
const fontHeight = calculateSymbolHeight(textFont, ctx);
const labelBoxTopY = centralY - fontHeight / 2 - paddingTop;
const labelBoxBottomY = centralY + fontHeight / 2 + paddingBottom;
const labelBoxHeight = labelBoxBottomY - labelBoxTopY;
// do not draw, if label is out of bounds
if (checkBoundaries && !checkLabelInBoundaries(centralY, bounds, labelBoxHeight)) {
return;
}
ctx.save();
ctx.fillStyle = bgColor;
ctx.strokeStyle = bgColor;
const xTextOffset = yAxisState.labelBoxMargin.end;
ctx.font = textFont;
const textWidth = calculateTextWidth(text, ctx, textFont);
const marginEnd = xTextOffset - paddingEnd;
const width = paddingStart !== undefined ? textWidth + paddingStart + paddingEnd : bounds.width - marginEnd;
const x = align === 'right' ? bounds.x + bounds.width - marginEnd - width : bounds.x + marginEnd;
const textX = align === 'right' ? bounds.x + bounds.width - textWidth - xTextOffset : xTextOffset;
// label can overlap with regular price y-axis label, so we need to hide regular y-axis label
ctx.fillStyle = bgColor;
ctx.strokeStyle = bgColor;
ctx.fillRect(x, labelBoxTopY, width, labelBoxHeight);
ctx.fillStyle = textColor;
ctx.fillText(text, textX, centralY + fontHeight / 2 - 1); // -1 for font height adjustment
ctx.restore();
}
/**
* Offset from the center of label to the top/bottom.
*
* @param font Label font
* @param ctx Drawing context
* @param paddingTop - extra padding from top
*/
export function getLabelYOffset(font, ctx, paddingTop = DEFAULT_PRICE_LABEL_PADDING) {
const fontHeight = calculateSymbolHeight(font, ctx);
return fontHeight / 2 + paddingTop;
}
/**
* Checks if label fits in chart scale boundaries
* @param centralY
* @param bounds
* @param labelBoxHeight
* returns true if label fits
*/
export function checkLabelInBoundaries(centralY, bounds, labelBoxHeight) {
return !(centralY < bounds.y + labelBoxHeight / 2 || centralY > bounds.y + bounds.height - labelBoxHeight / 2);
}