refactor: rewrite canvas helpers to ts (#11100)

* refactor: rewrite canvas helpers to ts

* refactor: review fixes

* refactor: rm src/helpers/types.ts temporary entry point
This commit is contained in:
Dan Onoshko 2023-02-15 18:26:49 +04:00 committed by GitHub
parent 22c6906bbe
commit 1324672637
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 388 additions and 376 deletions

View File

@ -11,7 +11,7 @@ const offsetFromEdge = (scale, edge, offset) => edge === 'top' || edge === 'left
const getTicksLimit = (ticksLength, maxTicksLimit) => Math.min(maxTicksLimit || ticksLength, ticksLength);
/**
* @typedef { import('./core.controller.js').default } Chart
* @typedef { import('../types/index.js').Chart } Chart
* @typedef {{value:number | string, label?:string, major?:boolean, $context?:any}} Tick
*/
@ -120,6 +120,7 @@ function createTickContext(parent, index, tick) {
}
function titleAlign(align, position, reverse) {
/** @type {CanvasTextAlign} */
let ret = _toLeftRightCenter(align);
if ((reverse && position !== 'right') || (!reverse && position === 'right')) {
ret = reverseAlign(ret);
@ -839,7 +840,7 @@ export default class Scale extends Element {
} else if (isArray(label)) {
// if it is an array let's measure each element
for (j = 0, jlen = label.length; j < jlen; ++j) {
nestedLabel = label[j];
nestedLabel = /** @type {string} */ (label[j]);
// Undefined labels and arrays should not be measured
if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) {
width = _measureText(ctx, cache.data, cache.gc, width, nestedLabel);

View File

@ -22,6 +22,9 @@ function lineTo(ctx, previous, target) {
ctx.lineTo(target.x, target.y);
}
/**
* @returns {any}
*/
function getLineMethod(options) {
if (options.stepped) {
return _steppedLineTo;

View File

@ -1,25 +1,28 @@
import type {
Chart,
Point,
FontSpec,
CanvasFontSpec,
PointStyle,
RenderTextOpts,
BackdropOptions
} from '../types/index.js';
import type {
TRBL,
SplinePoint,
RoundedRect,
TRBLCorners
} from '../types/geometric.js';
import {isArray, isNullOrUndef} from './helpers.core.js';
import {PI, TAU, HALF_PI, QUARTER_PI, TWO_THIRDS_PI, RAD_PER_DEG} from './helpers.math.js';
/**
* Note: typedefs are auto-exported, so use a made-up `canvas` namespace where
* necessary to avoid duplicates with `export * from './helpers`; see
* https://github.com/microsoft/TypeScript/issues/46011
* @typedef { import('../core/core.controller.js').default } canvas.Chart
* @typedef { import('../types/index.js').Point } Point
*/
/**
* @namespace Chart.helpers.canvas
*/
/**
* Converts the given font object into a CSS font string.
* @param {object} font - A font object.
* @return {string|null} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
* @param font - A font object.
* @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
* @private
*/
export function toFontString(font) {
export function toFontString(font: FontSpec) {
if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {
return null;
}
@ -33,7 +36,13 @@ export function toFontString(font) {
/**
* @private
*/
export function _measureText(ctx, data, gc, longest, string) {
export function _measureText(
ctx: CanvasRenderingContext2D,
data: Record<string, number>,
gc: string[],
longest: number,
string: string
) {
let textWidth = data[string];
if (!textWidth) {
textWidth = data[string] = ctx.measureText(string).width;
@ -45,10 +54,19 @@ export function _measureText(ctx, data, gc, longest, string) {
return longest;
}
type Thing = string | undefined | null
type Things = (Thing | Thing[])[]
/**
* @private
*/
export function _longestText(ctx, font, arrayOfThings, cache) {
// eslint-disable-next-line complexity
export function _longestText(
ctx: CanvasRenderingContext2D,
font: string,
arrayOfThings: Things,
cache?: {data?: Record<string, number>, garbageCollect?: string[], font?: string}
) {
cache = cache || {};
let data = cache.data = cache.data || {};
let gc = cache.garbageCollect = cache.garbageCollect || [];
@ -64,12 +82,12 @@ export function _longestText(ctx, font, arrayOfThings, cache) {
ctx.font = font;
let longest = 0;
const ilen = arrayOfThings.length;
let i, j, jlen, thing, nestedThing;
let i: number, j: number, jlen: number, thing: Thing | Thing[], nestedThing: Thing | Thing[];
for (i = 0; i < ilen; i++) {
thing = arrayOfThings[i];
// Undefined strings and arrays should not be measured
if (thing !== undefined && thing !== null && isArray(thing) !== true) {
if (thing !== undefined && thing !== null && !isArray(thing)) {
longest = _measureText(ctx, data, gc, longest, thing);
} else if (isArray(thing)) {
// if it is an array lets measure each element
@ -98,13 +116,13 @@ export function _longestText(ctx, font, arrayOfThings, cache) {
/**
* Returns the aligned pixel value to avoid anti-aliasing blur
* @param {canvas.Chart} chart - The chart instance.
* @param {number} pixel - A pixel value.
* @param {number} width - The width of the element.
* @returns {number} The aligned pixel value.
* @param chart - The chart instance.
* @param pixel - A pixel value.
* @param width - The width of the element.
* @returns The aligned pixel value.
* @private
*/
export function _alignPixel(chart, pixel, width) {
export function _alignPixel(chart: Chart, pixel: number, width: number) {
const devicePixelRatio = chart.currentDevicePixelRatio;
const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;
return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
@ -112,10 +130,8 @@ export function _alignPixel(chart, pixel, width) {
/**
* Clears the entire canvas.
* @param {HTMLCanvasElement} canvas
* @param {CanvasRenderingContext2D} [ctx]
*/
export function clearCanvas(canvas, ctx) {
export function clearCanvas(canvas: HTMLCanvasElement, ctx?: CanvasRenderingContext2D) {
ctx = ctx || canvas.getContext('2d');
ctx.save();
@ -126,12 +142,32 @@ export function clearCanvas(canvas, ctx) {
ctx.restore();
}
export function drawPoint(ctx, options, x, y) {
export interface DrawPointOptions {
pointStyle: PointStyle;
rotation?: number;
radius: number;
borderWidth: number;
}
export function drawPoint(
ctx: CanvasRenderingContext2D,
options: DrawPointOptions,
x: number,
y: number
) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
drawPointLegend(ctx, options, x, y, null);
}
export function drawPointLegend(ctx, options, x, y, w) {
let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW;
// eslint-disable-next-line complexity
export function drawPointLegend(
ctx: CanvasRenderingContext2D,
options: DrawPointOptions,
x: number,
y: number,
w: number
) {
let type: string, xOffset: number, yOffset: number, size: number, cornerRadius: number, width: number, xOffsetW: number, yOffsetW: number;
const style = options.pointStyle;
const rotation = options.rotation;
const radius = options.radius;
@ -157,24 +193,24 @@ export function drawPointLegend(ctx, options, x, y, w) {
switch (style) {
// Default includes circle
default:
if (w) {
ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);
} else {
ctx.arc(x, y, radius, 0, TAU);
}
ctx.closePath();
break;
case 'triangle':
width = w ? w / 2 : radius;
ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
rad += TWO_THIRDS_PI;
ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
rad += TWO_THIRDS_PI;
ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
ctx.closePath();
break;
case 'rectRounded':
default:
if (w) {
ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);
} else {
ctx.arc(x, y, radius, 0, TAU);
}
ctx.closePath();
break;
case 'triangle':
width = w ? w / 2 : radius;
ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
rad += TWO_THIRDS_PI;
ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
rad += TWO_THIRDS_PI;
ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
ctx.closePath();
break;
case 'rectRounded':
// NOTE: the rounded rect implementation changed to use `arc` instead of
// `quadraticCurveTo` since it generates better results when rect is
// almost a circle. 0.516 (instead of 0.5) produces results with visually
@ -182,83 +218,83 @@ export function drawPointLegend(ctx, options, x, y, w) {
// circle with `radius`. For more details, see the following PRs:
// https://github.com/chartjs/Chart.js/issues/5597
// https://github.com/chartjs/Chart.js/issues/5858
cornerRadius = radius * 0.516;
size = radius - cornerRadius;
xOffset = Math.cos(rad + QUARTER_PI) * size;
xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
yOffset = Math.sin(rad + QUARTER_PI) * size;
yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);
ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);
ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
ctx.closePath();
break;
case 'rect':
if (!rotation) {
size = Math.SQRT1_2 * radius;
width = w ? w / 2 : size;
ctx.rect(x - width, y - size, 2 * width, 2 * size);
cornerRadius = radius * 0.516;
size = radius - cornerRadius;
xOffset = Math.cos(rad + QUARTER_PI) * size;
xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
yOffset = Math.sin(rad + QUARTER_PI) * size;
yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);
ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);
ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
ctx.closePath();
break;
}
rad += QUARTER_PI;
case 'rect':
if (!rotation) {
size = Math.SQRT1_2 * radius;
width = w ? w / 2 : size;
ctx.rect(x - width, y - size, 2 * width, 2 * size);
break;
}
rad += QUARTER_PI;
/* falls through */
case 'rectRot':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
ctx.closePath();
break;
case 'crossRot':
rad += QUARTER_PI;
case 'rectRot':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
ctx.closePath();
break;
case 'crossRot':
rad += QUARTER_PI;
/* falls through */
case 'cross':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
break;
case 'star':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
rad += QUARTER_PI;
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
break;
case 'line':
xOffset = w ? w / 2 : Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
ctx.moveTo(x - xOffset, y - yOffset);
ctx.lineTo(x + xOffset, y + yOffset);
break;
case 'dash':
ctx.moveTo(x, y);
ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);
break;
case false:
ctx.closePath();
break;
case 'cross':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
break;
case 'star':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
rad += QUARTER_PI;
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
break;
case 'line':
xOffset = w ? w / 2 : Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
ctx.moveTo(x - xOffset, y - yOffset);
ctx.lineTo(x + xOffset, y + yOffset);
break;
case 'dash':
ctx.moveTo(x, y);
ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);
break;
case false:
ctx.closePath();
break;
}
ctx.fill();
@ -269,34 +305,43 @@ export function drawPointLegend(ctx, options, x, y, w) {
/**
* Returns true if the point is inside the rectangle
* @param {Point} point - The point to test
* @param {object} area - The rectangle
* @param {number} [margin] - allowed margin
* @returns {boolean}
* @param point - The point to test
* @param area - The rectangle
* @param margin - allowed margin
* @private
*/
export function _isPointInArea(point, area, margin) {
export function _isPointInArea(
point: Point,
area: TRBL,
margin?: number
) {
margin = margin || 0.5; // margin - default is to match rounded decimals
return !area || (point && point.x > area.left - margin && point.x < area.right + margin &&
point.y > area.top - margin && point.y < area.bottom + margin);
}
export function clipArea(ctx, area) {
export function clipArea(ctx: CanvasRenderingContext2D, area: TRBL) {
ctx.save();
ctx.beginPath();
ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
ctx.clip();
}
export function unclipArea(ctx) {
export function unclipArea(ctx: CanvasRenderingContext2D) {
ctx.restore();
}
/**
* @private
*/
export function _steppedLineTo(ctx, previous, target, flip, mode) {
export function _steppedLineTo(
ctx: CanvasRenderingContext2D,
previous: Point,
target: Point,
flip?: boolean,
mode?: string
) {
if (!previous) {
return ctx.lineTo(target.x, target.y);
}
@ -315,7 +360,12 @@ export function _steppedLineTo(ctx, previous, target, flip, mode) {
/**
* @private
*/
export function _bezierCurveTo(ctx, previous, target, flip) {
export function _bezierCurveTo(
ctx: CanvasRenderingContext2D,
previous: SplinePoint,
target: SplinePoint,
flip?: boolean
) {
if (!previous) {
return ctx.lineTo(target.x, target.y);
}
@ -328,13 +378,81 @@ export function _bezierCurveTo(ctx, previous, target, flip) {
target.y);
}
function setRenderOpts(ctx: CanvasRenderingContext2D, opts: RenderTextOpts) {
if (opts.translation) {
ctx.translate(opts.translation[0], opts.translation[1]);
}
if (!isNullOrUndef(opts.rotation)) {
ctx.rotate(opts.rotation);
}
if (opts.color) {
ctx.fillStyle = opts.color;
}
if (opts.textAlign) {
ctx.textAlign = opts.textAlign;
}
if (opts.textBaseline) {
ctx.textBaseline = opts.textBaseline;
}
}
function decorateText(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
line: string,
opts: RenderTextOpts
) {
if (opts.strikethrough || opts.underline) {
/**
* Now that IE11 support has been dropped, we can use more
* of the TextMetrics object. The actual bounding boxes
* are unflagged in Chrome, Firefox, Edge, and Safari so they
* can be safely used.
* See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility
*/
const metrics = ctx.measureText(line);
const left = x - metrics.actualBoundingBoxLeft;
const right = x + metrics.actualBoundingBoxRight;
const top = y - metrics.actualBoundingBoxAscent;
const bottom = y + metrics.actualBoundingBoxDescent;
const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;
ctx.strokeStyle = ctx.fillStyle;
ctx.beginPath();
ctx.lineWidth = opts.decorationWidth || 2;
ctx.moveTo(left, yDecoration);
ctx.lineTo(right, yDecoration);
ctx.stroke();
}
}
function drawBackdrop(ctx: CanvasRenderingContext2D, opts: BackdropOptions) {
const oldColor = ctx.fillStyle;
ctx.fillStyle = opts.color as string;
ctx.fillRect(opts.left, opts.top, opts.width, opts.height);
ctx.fillStyle = oldColor;
}
/**
* Render text onto the canvas
*/
export function renderText(ctx, text, x, y, font, opts = {}) {
export function renderText(
ctx: CanvasRenderingContext2D,
text: string | string[],
x: number,
y: number,
font: CanvasFontSpec,
opts: RenderTextOpts = {}
) {
const lines = isArray(text) ? text : [text];
const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';
let i, line;
let i: number, line: string;
ctx.save();
ctx.font = font.string;
@ -362,73 +480,21 @@ export function renderText(ctx, text, x, y, font, opts = {}) {
ctx.fillText(line, x, y, opts.maxWidth);
decorateText(ctx, x, y, line, opts);
y += font.lineHeight;
y += Number(font.lineHeight);
}
ctx.restore();
}
function setRenderOpts(ctx, opts) {
if (opts.translation) {
ctx.translate(opts.translation[0], opts.translation[1]);
}
if (!isNullOrUndef(opts.rotation)) {
ctx.rotate(opts.rotation);
}
if (opts.color) {
ctx.fillStyle = opts.color;
}
if (opts.textAlign) {
ctx.textAlign = opts.textAlign;
}
if (opts.textBaseline) {
ctx.textBaseline = opts.textBaseline;
}
}
function decorateText(ctx, x, y, line, opts) {
if (opts.strikethrough || opts.underline) {
/**
* Now that IE11 support has been dropped, we can use more
* of the TextMetrics object. The actual bounding boxes
* are unflagged in Chrome, Firefox, Edge, and Safari so they
* can be safely used.
* See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility
*/
const metrics = ctx.measureText(line);
const left = x - metrics.actualBoundingBoxLeft;
const right = x + metrics.actualBoundingBoxRight;
const top = y - metrics.actualBoundingBoxAscent;
const bottom = y + metrics.actualBoundingBoxDescent;
const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;
ctx.strokeStyle = ctx.fillStyle;
ctx.beginPath();
ctx.lineWidth = opts.decorationWidth || 2;
ctx.moveTo(left, yDecoration);
ctx.lineTo(right, yDecoration);
ctx.stroke();
}
}
function drawBackdrop(ctx, opts) {
const oldColor = ctx.fillStyle;
ctx.fillStyle = opts.color;
ctx.fillRect(opts.left, opts.top, opts.width, opts.height);
ctx.fillStyle = oldColor;
}
/**
* Add a path of a rectangle with rounded corners to the current sub-path
* @param {CanvasRenderingContext2D} ctx Context
* @param {*} rect Bounding rect
* @param ctx - Context
* @param rect - Bounding rect
*/
export function addRoundedRectPath(ctx, rect) {
export function addRoundedRectPath(
ctx: CanvasRenderingContext2D,
rect: RoundedRect & { radius: TRBLCorners }
) {
const {x, y, w, h, radius} = rect;
// top left arc

View File

@ -1,19 +1,7 @@
import {almostEquals, distanceBetweenPoints, sign} from './helpers.math.js';
import {_isPointInArea} from './helpers.canvas.js';
import type {ChartArea} from '../types/index.js';
export interface SplinePoint {
x: number;
y: number;
skip?: boolean;
// Both Bezier and monotone interpolations have these fields
// but they are added in different spots
cp1x?: number;
cp1y?: number;
cp2x?: number;
cp2y?: number;
}
import type {SplinePoint} from '../types/geometric.js';
const EPSILON = Number.EPSILON || 1e-14;

View File

@ -1,5 +1,4 @@
import type {Point} from '../types/geometric.js';
import type {SplinePoint} from './helpers.curve.js';
import type {Point, SplinePoint} from '../types/geometric.js';
/**
* @private

View File

@ -1,7 +1,7 @@
import defaults from '../core/core.defaults.js';
import {isArray, isObject, toDimension, valueOrDefault} from './helpers.core.js';
import {Point, toFontString} from './helpers.canvas.js';
import type {ChartArea, FontSpec} from '../types/index.js';
import {toFontString} from './helpers.canvas.js';
import type {ChartArea, FontSpec, Point} from '../types/index.js';
import type {TRBL, TRBLCorners} from '../types/geometric.js';
const LINE_HEIGHT = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/;
@ -104,10 +104,6 @@ export function toPadding(value?: number | TRBL): ChartArea {
return obj;
}
export interface CanvasFontSpec extends FontSpec {
string: string;
}
/**
* Parses font options and returns the font object.
* @param options - A object that contains font options to be parsed.

View File

@ -1,19 +0,0 @@
/**
* Temporary entry point of the types at the time of the transition.
* After transition done need to remove it in favor of index.ts
*/
// export * from '..js';
export * from './helpers.color.js';
export * from './helpers.collection.js';
export * from './helpers.core.js';
export * from './helpers.curve.js';
export * from './helpers.dom.js';
export * from './helpers.easing.js';
export * from './helpers.extras.js';
export * from './helpers.interpolation.js';
export * from './helpers.intl.js';
export * from './helpers.math.js';
export * from './helpers.options.js';
export * from './helpers.rtl.js';
export * from '../types/helpers/index.js';

View File

@ -37,3 +37,16 @@ export type RoundedRect = {
}
export type Padding = Partial<TRBL> | number | Point;
export interface SplinePoint {
x: number;
y: number;
skip?: boolean;
// Both Bezier and monotone interpolations have these fields
// but they are added in different spots
cp1x?: number;
cp1y?: number;
cp2x?: number;
cp2y?: number;
}

View File

@ -1,135 +0,0 @@
import {PointStyle, Scriptable, ScriptableScaleContext} from '../index.js';
import {Color} from '../color.js';
import {ChartArea, RoundedRect} from '../geometric.js';
import {CanvasFontSpec} from '../../helpers/helpers.options.js';
export function clearCanvas(canvas: HTMLCanvasElement, ctx?: CanvasRenderingContext2D): void;
export function clipArea(ctx: CanvasRenderingContext2D, area: ChartArea): void;
export function unclipArea(ctx: CanvasRenderingContext2D): void;
export interface DrawPointOptions {
pointStyle: PointStyle;
rotation?: number;
radius: number;
borderWidth: number;
}
export function drawPoint(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number): void;
export function drawPointLegend(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number, w: number): void;
/**
* Converts the given font object into a CSS font string.
* @param font a font object
* @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
*/
export function toFontString(font: { size: number; family: string; style?: string; weight?: string }): string | null;
export interface RenderTextOpts {
/**
* The fill color of the text. If unset, the existing
* fillStyle property of the canvas is unchanged.
*/
color?: Color;
/**
* The width of the strikethrough / underline
* @default 2
*/
decorationWidth?: number;
/**
* The max width of the text in pixels
*/
maxWidth?: number;
/**
* A rotation to be applied to the canvas
* This is applied after the translation is applied
*/
rotation?: number;
/**
* Apply a strikethrough effect to the text
*/
strikethrough?: boolean;
/**
* The color of the text stroke. If unset, the existing
* strokeStyle property of the context is unchanged
*/
strokeColor?: Color;
/**
* The text stroke width. If unset, the existing
* lineWidth property of the context is unchanged
*/
strokeWidth?: number;
/**
* The text alignment to use. If unset, the existing
* textAlign property of the context is unchanged
*/
textAlign?: CanvasTextAlign;
/**
* The text baseline to use. If unset, the existing
* textBaseline property of the context is unchanged
*/
textBaseline?: CanvasTextBaseline;
/**
* If specified, a translation to apply to the context
*/
translation?: [number, number];
/**
* Underline the text
*/
underline?: boolean;
/**
* Dimensions for drawing the label backdrop
*/
backdrop?: BackdropOptions;
}
export interface BackdropOptions {
/**
* Left position of backdrop as pixel
*/
left: number;
/**
* Top position of backdrop as pixel
*/
top: number;
/**
* Width of backdrop in pixels
*/
width: number;
/**
* Height of backdrop in pixels
*/
height: number;
/**
* Color of label backdrops.
*/
color: Scriptable<Color, ScriptableScaleContext>;
}
export function renderText(
ctx: CanvasRenderingContext2D,
text: string | string[],
x: number,
y: number,
font: CanvasFontSpec,
opts?: RenderTextOpts
): void;
export function addRoundedRectPath(ctx: CanvasRenderingContext2D, rect: RoundedRect): void;

View File

@ -1 +0,0 @@
export {};

View File

@ -1,3 +0,0 @@
export * from './helpers.canvas.js';
export * from './helpers.canvas.js';
export * from './helpers.segment.js';

110
src/types/index.d.ts vendored
View File

@ -10,9 +10,7 @@ import {Color} from './color.js';
import Element from '../core/core.element.js';
import {ChartArea, Padding, Point} from './geometric.js';
import {LayoutItem, LayoutPosition} from './layout.js';
import {RenderTextOpts} from './helpers/helpers.canvas.js';
import {CanvasFontSpec} from '../helpers/helpers.options.js';
import type {ColorsPluginOptions} from '../plugins/plugin.colors.js';
import {ColorsPluginOptions} from '../plugins/plugin.colors.js';
export {EasingFunction} from '../helpers/helpers.easing.js';
export {default as ArcElement, ArcProps} from '../elements/element.arc.js';
@ -548,6 +546,8 @@ export declare class Chart<
isPluginEnabled(pluginId: string): boolean;
getContext(): { chart: Chart, type: string };
static readonly defaults: Defaults;
static readonly overrides: Overrides;
static readonly version: string;
@ -1359,6 +1359,102 @@ export interface ScriptableScalePointLabelContext {
type: string;
}
export interface RenderTextOpts {
/**
* The fill color of the text. If unset, the existing
* fillStyle property of the canvas is unchanged.
*/
color?: Color;
/**
* The width of the strikethrough / underline
* @default 2
*/
decorationWidth?: number;
/**
* The max width of the text in pixels
*/
maxWidth?: number;
/**
* A rotation to be applied to the canvas
* This is applied after the translation is applied
*/
rotation?: number;
/**
* Apply a strikethrough effect to the text
*/
strikethrough?: boolean;
/**
* The color of the text stroke. If unset, the existing
* strokeStyle property of the context is unchanged
*/
strokeColor?: Color;
/**
* The text stroke width. If unset, the existing
* lineWidth property of the context is unchanged
*/
strokeWidth?: number;
/**
* The text alignment to use. If unset, the existing
* textAlign property of the context is unchanged
*/
textAlign?: CanvasTextAlign;
/**
* The text baseline to use. If unset, the existing
* textBaseline property of the context is unchanged
*/
textBaseline?: CanvasTextBaseline;
/**
* If specified, a translation to apply to the context
*/
translation?: [number, number];
/**
* Underline the text
*/
underline?: boolean;
/**
* Dimensions for drawing the label backdrop
*/
backdrop?: BackdropOptions;
}
export interface BackdropOptions {
/**
* Left position of backdrop as pixel
*/
left: number;
/**
* Top position of backdrop as pixel
*/
top: number;
/**
* Width of backdrop in pixels
*/
width: number;
/**
* Height of backdrop in pixels
*/
height: number;
/**
* Color of label backdrops.
*/
color: Scriptable<Color, ScriptableScaleContext>;
}
export interface LabelItem {
label: string | string[];
font: CanvasFontSpec;
@ -1658,6 +1754,10 @@ export interface FontSpec {
lineHeight: number | string;
}
export interface CanvasFontSpec extends FontSpec {
string: string;
}
export type TextAlign = 'left' | 'center' | 'right';
export type Align = 'start' | 'center' | 'end';
@ -3655,6 +3755,8 @@ export interface ChartData<
TLabel = unknown
> {
labels?: TLabel[];
xLabels?: TLabel[];
yLabels?: TLabel[];
datasets: ChartDataset<TType, TData>[];
}
@ -3664,6 +3766,8 @@ export interface ChartDataCustomTypesPerDataset<
TLabel = unknown
> {
labels?: TLabel[];
xLabels?: TLabel[];
yLabels?: TLabel[];
datasets: ChartDatasetCustomTypesPerDataset<TType, TData>[];
}