Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable different fonts and colors configuration on multiline labels #801

Merged
merged 48 commits into from
Feb 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
962ee4b
Add element diagrams to the annotation types guide
stockiNail Jun 6, 2022
05e619c
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Jun 8, 2022
22a3aa3
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Jun 8, 2022
f5186fe
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Jun 8, 2022
6d87201
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Jul 21, 2022
84366b3
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Jul 22, 2022
ec3a554
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Jul 26, 2022
1324e43
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Aug 4, 2022
693e84d
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Aug 19, 2022
30b5941
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Aug 19, 2022
b30c95e
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Sep 28, 2022
ff2a323
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Sep 28, 2022
1c5cbf2
Enable different fonts and colors configuration on multiline labels
stockiNail Sep 29, 2022
340a3e8
updates type definitions
stockiNail Sep 29, 2022
b2a5edc
fixes CC
stockiNail Sep 29, 2022
30d56bf
documentation
stockiNail Sep 29, 2022
ea14c2f
fixes check for font as array
stockiNail Sep 30, 2022
d2ff540
adds sample
stockiNail Sep 30, 2022
a522271
fixes CC
stockiNail Sep 30, 2022
b686c7a
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Oct 28, 2022
30446eb
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Nov 15, 2022
63e01f8
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Nov 15, 2022
235712c
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Nov 17, 2022
db39263
changes font and color as indexable
stockiNail Jan 9, 2023
5114654
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Jan 9, 2023
6f32004
Merge remote-tracking branch 'origin/master' into fontsColors
stockiNail Jan 9, 2023
e43a62e
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Jan 27, 2023
5069380
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Jan 27, 2023
d7723e6
Merge branch 'master' of
stockiNail Jan 27, 2023
f9835e1
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Feb 4, 2023
f8cc9c6
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Feb 6, 2023
ba80172
Merge remote-tracking branch 'origin/master' into fontsColors
stockiNail Feb 6, 2023
65c55ec
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Feb 24, 2023
358b44c
Merge branch 'master' into fontsColors
stockiNail Feb 24, 2023
fad03a4
add tolerance
stockiNail Feb 24, 2023
7a940ab
add tolerance 2
stockiNail Feb 24, 2023
2996473
add tolerance 3
stockiNail Feb 24, 2023
8b3857c
add tolerance 4
stockiNail Feb 24, 2023
5a80feb
add tolerance 4
stockiNail Feb 24, 2023
127e093
fallback tolerance changes
stockiNail Feb 24, 2023
006a3bb
Test with new registered fixtures
stockiNail Feb 25, 2023
05b50ec
Another test if adjust is passed
stockiNail Feb 25, 2023
bf0f8ad
additional test with a label fixture
stockiNail Feb 25, 2023
e04e475
add new fixture for polygon to test if works
stockiNail Feb 25, 2023
f634ce8
add new fixtures for ellipse from FF
stockiNail Feb 25, 2023
af0d85d
fallback tests on fixtures
stockiNail Feb 25, 2023
c552b5a
Merge branch 'master' of https://github.com/chartjs/chartjs-plugin-an…
stockiNail Feb 25, 2023
7ad86ca
Merge remote-tracking branch 'origin/master' into fontsColors
stockiNail Feb 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ module.exports = {
'label/image',
'label/innerChart',
'label/lowerUpper',
'label/fontsColors',
'label/autoscaling'
]
},
Expand Down
10 changes: 7 additions & 3 deletions docs/guide/types/_commonInnerLabel.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ All of these options can be [Scriptable](../options.md#scriptable-options)

| Name | Type | Default | Notes
| ---- | ---- | :----: | ----
| `color` | [`Color`](../options.md#color) | `'black'` | Text color.
| [`color`](#fonts-and-colors) | [`Color`\|`Color[]`](../options#color) | `'black'` | Text color.
| `content` | `string`\|`string[]`\|[`Image`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image)\|[`HTMLCanvasElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) | `null` | The content to show in the label.
| `display` | `boolean` | `false` | Whether or not the label is shown.
| `drawTime` | `string` | `options.drawTime` | See [drawTime](../options.md#draw-time). Defaults to the annotation draw time if unset
| `font` | [`Font`](../options.md#font) | `{ weight: 'bold' }` | Label font
| `drawTime` | `string` | `options.drawTime` | See [drawTime](../options#draw-time). Defaults to the annotation draw time if unset
| [`font`](#fonts-and-colors) | [`Font`\|`Font[]`](../options#font) | `{ weight: 'bold' }` | Label font
| `height` | `number`\|`string` | `undefined` | Overrides the height of the image or canvas element. Could be set in pixel by a number, or in percentage of current height of image or canvas element by a string. If undefined, uses the height of the image or canvas element. It is used only when the content is an image or canvas element.
| `opacity` | `number` | `undefined` | Overrides the opacity of the image or canvas element. Could be set a number in the range 0.0 to 1.0, inclusive. If undefined, uses the opacity of the image or canvas element. It is used only when the content is an image or canvas element.
| `padding` | [`Padding`](../options.md#padding) | `6` | The padding to add around the text label.
Expand All @@ -34,3 +34,7 @@ A position can be set in 2 different values types:
If this value is a string (possible options are `'start'`, `'center'`, `'end'` or a string in percentage format), it is applied to vertical and horizontal position in the annotation.

If this value is an object, the `x` property defines the horizontal alignment in the annotation. Similarly, the `y` property defines the vertical alignment in the annotation. Possible options for both properties are `'start'`, `'center'`, `'end'`, a string in percentage format. Omitted property have value of the default, `'center'`.

### Fonts and colors

When the label to draw has multiple lines, you can use different font and color for each line of the label. This is enabled configuring an array of fonts or colors for those options. When the lines are more than the configured fonts of colors, the last configuration of those options is used for all remaining lines.
8 changes: 4 additions & 4 deletions docs/guide/types/label.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ The following options are available for label annotations.
| [`borderRadius`](#borderradius) | `number` \| `object` | Yes | `0`
| [`borderWidth`](#styling) | `number`| Yes | `0`
| [`callout`](#callout) | `object` | Yes |
| [`color`](#styling) | [`Color`](../options.md#color) | Yes | `'black'`
| [`color`](#styling) | [`Color`\|`Color[]`](../options#color) | Yes | `'black'`
| [`content`](#general) | `string`\|`string[]`\|[`Image`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image)\|[`HTMLCanvasElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) | Yes | `null`
| [`font`](#styling) | [`Font`](../options.md#font) | Yes | `{}`
| [`font`](#styling) | [`Font`\|`Font[]`](../options#font) | Yes | `{}`
| [`height`](#general) | `number`\|`string` | Yes | `undefined`
| [`opacity`](#styling) | `number` | Yes | `undefined`
| [`padding`](#general) | [`Padding`](../options.md#padding) | Yes | `6`
Expand Down Expand Up @@ -122,8 +122,8 @@ The 4 coordinates, xMin, xMax, yMin, yMax are optional. If not specified, the bo
| `borderJoinStyle` | Border line join style. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin).
| `borderShadowColor` | The color of the border shadow. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowColor).
| `borderWidth` | Stroke width (in pixels).
| `color` | Text color.
| `font` | Text font.
| `color` | Text color. When the label to draw has multiple lines, you can use different color for each line of the label. This is enabled configuring an array of colors. When the lines are more than the configured colors, the last configuration of this option is used for all remaining lines.
| `font` | Text font. When the label to draw has multiple lines, you can use different font for each line of the label. This is enabled configuring an array of fonts. When the lines are more than the configured fonts, the last configuration of this option is used for all remaining lines.
| `opacity` | Overrides the opacity of the image or canvas element. Could be set a number in the range 0.0 to 1.0, inclusive. If undefined, uses the opacity of the image or canvas element. It is used only when the content is an image or canvas element.
| `shadowBlur` | The amount of blur applied to shadow of the box where the label is located. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowBlur).
| `shadowOffsetX` | The distance that shadow, of the box where the label is located, will be offset horizontally. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowOffsetX).
Expand Down
10 changes: 7 additions & 3 deletions docs/guide/types/line.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,11 @@ All of these options can be [Scriptable](../options.md#scriptable-options)
| `borderShadowColor` | [`Color`](../options.md#color) | `'transparent'` | The color of border shadow of the box where the label is located. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowColor).
| `borderWidth` | `number` | `0` | The border line width (in pixels).
| [`callout`](#callout) | `object` | | Can connect the label to the line. See [callout](#callout).
| `color` | [`Color`](../options.md#color) | `'#fff'` | Text color.
| [`color`](#fonts-and-colors) | [`Color`\|`Color[]`](../options#color) | `'#fff'` | Text color.
| `content` | `string`\|`string[]`\|[`Image`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image)\|[`HTMLCanvasElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) | `null` | The content to show in the label.
| `display` | `boolean` | `false` | Whether or not the label is shown.
| `drawTime` | `string` | `options.drawTime` | See [drawTime](../options.md#draw-time). Defaults to the line annotation draw time if unset.
| `font` | [`Font`](../options.md#font) | `{ weight: 'bold' }` | Label font.
| `drawTime` | `string` | `options.drawTime` | See [drawTime](../options#draw-time). Defaults to the line annotation draw time if unset.
| [`font`](#fonts-and-colors) | [`Font`\|`Font[]`](../options#font) | `{ weight: 'bold' }` | Label font.
| `height` | `number`\|`string` | `undefined` | Overrides the height of the image or canvas element. Could be set in pixel by a number, or in percentage of current height of image or canvas element by a string. If undefined, uses the height of the image or canvas element. It is used only when the content is an image or canvas element.
| `opacity` | `number` | `undefined` | Overrides the opacity of the image or canvas element. Could be set a number in the range 0.0 to 1.0, inclusive. If undefined, uses the opacity of the image or canvas element. It is used only when the content is an image or canvas element.
| `padding` | [`Padding`](../options.md#padding) | `6` | The padding to add around the text label.
Expand All @@ -156,6 +156,10 @@ All of these options can be [Scriptable](../options.md#scriptable-options)

If this value is a number, it is applied to all corners of the rectangle (topLeft, topRight, bottomLeft, bottomRight). If this value is an object, the `topLeft` property defines the top-left corners border radius. Similarly, the `topRight`, `bottomLeft`, and `bottomRight` properties can also be specified. Omitted corners have radius of 0.

### Fonts and colors

When the label to draw has multiple lines, you can use different font and color for each line of the label. This is enabled configuring an array of fonts or colors for those options. When the lines are more than the configured fonts of colors, the last configuration of those options is used for all remaining lines.

### Callout

A callout can connect the label to the line when the label is arbitrarily (by `xAdjust` and `yAdjust` options) moved from its original position.
Expand Down
152 changes: 152 additions & 0 deletions docs/samples/label/fontsColors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Fonts and colors

```js chart-editor
// <block:setup:5>
const DATA_COUNT = 12;
const MIN = 0;
const MAX = 100;

const numberCfg = {count: DATA_COUNT, min: MIN, max: MAX};

const data = {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
datasets: [{
data: Utils.numbers(numberCfg)
}]
};
// </block:setup>

// <block:annotation1:1>
const annotation1 = {
type: 'label',
backgroundColor: 'rgba(0,0,0,0.2)',
borderRadius: 6,
borderWidth: 0,
callout: {
display: true
},
color: ['black,', 'black', 'green'],
content: ['March', 'is', 'annotated'],
font: [{size: 16, weight: 'bold'}, {family: 'courier'}],
position: {
x: 'center',
y: 'end'
},
xValue: 'March',
yAdjust: (ctx) => yOffset(ctx, 'March'),
yValue: (ctx) => yValue(ctx, 'March')
};
// </block:annotation1>

// <block:annotation2:2>
const annotation2 = {
type: 'label',
backgroundColor: 'rgba(0,0,0,0.2)',
borderRadius: 6,
borderWidth: 0,
callout: {
display: true
},
color: ['black,', 'black', 'green'],
content: ['June', 'is', 'annotated'],
font: [{size: 16, weight: 'bold'}, {family: 'courier'}],
position: {
x: 'center',
y: 'end'
},
xValue: 'June',
yAdjust: (ctx) => yOffset(ctx, 'June'),
yValue: (ctx) => yValue(ctx, 'June')
};
// </block:annotation2>

// <block:annotation3:3>
const annotation3 = {
type: 'label',
backgroundColor: 'rgba(0,0,0,0.2)',
borderRadius: 6,
borderWidth: 0,
callout: {
display: true
},
color: ['black,', 'black', 'green'],
content: ['October', 'is', 'annotated'],
font: [{size: 16, weight: 'bold'}, {family: 'courier'}],
position: {
x: 'center',
y: 'end'
},
xValue: 'October',
yAdjust: (ctx) => yOffset(ctx, 'October'),
yValue: (ctx) => yValue(ctx, 'October')
};
// </block:annotation3>

// <block:utils:4>
function yValue(ctx, label) {
const chart = ctx.chart;
const dataset = chart.data.datasets[0];
return dataset.data[chart.data.labels.indexOf(label)];
}

function yOffset(ctx, label) {
const value = yValue(ctx, label);
const chart = ctx.chart;
const scale = chart.scales.y;
const y = scale.getPixelForValue(value);
const lblPos = scale.getPixelForValue(100);
return lblPos - y - 5;
}

// </block:utils>

/* <block:config:0> */
const config = {
type: 'bar',
data,
options: {
scales: {
y: {
beginAtZero: true,
max: 130,
min: 0,
grid: {
color: (ctx)=> ctx.tick.value <= 100 ?
ctx.chart.scales.x.options.grid.color :
undefined
},
ticks: {
callback: (value) => value > 100 ? '' : value
}
}
},
plugins: {
annotation: {
annotations: {
annotation1,
annotation2,
annotation3
}
}
}
}
};
/* </block:config> */

const actions = [
{
name: 'Randomize',
handler: function(chart) {
chart.data.datasets.forEach(function(dataset, i) {
dataset.data = dataset.data.map(() => Utils.rand(MIN, MAX));
});
chart.update();
}
}
];

module.exports = {
actions: actions,
config: config,
};
```
6 changes: 4 additions & 2 deletions src/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {clipArea, unclipArea, isObject, isArray} from 'chart.js/helpers';
import {handleEvent, eventHooks, updateListeners} from './events';
import {invokeHook, elementHooks, updateHooks} from './hooks';
import {adjustScaleRange, verifyScaleOptions} from './scale';
import {updateElements, resolveType} from './elements';
import {updateElements, resolveType, isIndexable} from './elements';
import {annotationTypes} from './types';
import {requireVersion} from './helpers';
import {version} from '../package.json';
Expand Down Expand Up @@ -137,8 +137,10 @@ export default {
},
common: {
label: {
_indexable: isIndexable,
_fallback: true
}
},
_indexable: isIndexable
}
},

Expand Down
8 changes: 7 additions & 1 deletion src/elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ const hooks = eventHooks.concat(elementHooks);
* @typedef { import('../../types/options').AnnotationPluginOptions } AnnotationPluginOptions
*/

/**
* @param {string} prop
* @returns {boolean}
*/
export const isIndexable = (prop) => prop === 'color' || prop === 'font';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be in the plugin descriptors, unless it does not work there?

For example:
https://github.com/chartjs/Chart.js/blob/cfe8e0311bca46e43d5630e91bf82f470df3f56d/src/plugins/plugin.tooltip.js#L1322-L1335

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kurkle it is in element because is also used in element code: https://github.com/chartjs/chartjs-plugin-annotation/pull/801/files#diff-4e8835b7cdbc98ff051ec4984253e2a838ff262a5ec9e3ecf469aa0569cd8429R132

Putting it in annotation and exported to element, we have a cycle in the "import"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kurkle what do you think? Is it acceptable or do you think I must move in annotation.js ?


/**
* Resolve the annotation type, checking if is supported.
* @param {string} [type=line] - annotation type
Expand Down Expand Up @@ -126,7 +132,7 @@ function resolveObj(resolver, defs) {
for (const prop of Object.keys(defs)) {
const optDefs = defs[prop];
const value = resolver[prop];
result[prop] = isObject(optDefs) ? resolveObj(value, optDefs) : value;
result[prop] = isObject(optDefs) && !isIndexable(prop) ? resolveObj(value, optDefs) : value;
}
return result;
}
Expand Down
79 changes: 59 additions & 20 deletions src/helpers/helpers.canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import {clampAll, clamp} from './helpers.core';
import {calculateTextAlignment, getSize} from './helpers.options';

const widthCache = new Map();
const fontsKey = (fonts) => fonts.reduce(function(prev, item) {
prev += item.string;
return prev;
}, '');

/**
* @typedef { import('chart.js').Point } Point
Expand Down Expand Up @@ -77,22 +81,13 @@ export function measureLabelSize(ctx, options) {
height: getSize(content.height, options.height)
};
}
const font = toFont(options.font);
const optFont = options.font;
const fonts = isArray(optFont) ? optFont.map(f => toFont(f)) : [toFont(optFont)];
const strokeWidth = options.textStrokeWidth;
const lines = isArray(content) ? content : [content];
const mapKey = lines.join() + font.string + strokeWidth + (ctx._measureText ? '-spriting' : '');
const mapKey = lines.join() + fontsKey(fonts) + strokeWidth + (ctx._measureText ? '-spriting' : '');
if (!widthCache.has(mapKey)) {
ctx.save();
ctx.font = font.string;
const count = lines.length;
let width = 0;
for (let i = 0; i < count; i++) {
const text = lines[i];
width = Math.max(width, ctx.measureText(text).width + strokeWidth);
}
ctx.restore();
const height = count * font.lineHeight + strokeWidth;
widthCache.set(mapKey, {width, height});
widthCache.set(mapKey, calculateLabelSize(ctx, lines, fonts, strokeWidth));
}
return widthCache.get(mapKey);
}
Expand Down Expand Up @@ -137,19 +132,19 @@ export function drawLabel(ctx, rect, options) {
return;
}
const labels = isArray(content) ? content : [content];
const font = toFont(options.font);
const lh = font.lineHeight;
const optFont = options.font;
const fonts = isArray(optFont) ? optFont.map(f => toFont(f)) : [toFont(optFont)];
const optColor = options.color;
const colors = isArray(optColor) ? optColor : [optColor];
const x = calculateTextAlignment(rect, options);
const y = rect.y + (lh / 2) + options.textStrokeWidth / 2;
const y = rect.y + options.textStrokeWidth / 2;
ctx.save();
ctx.font = font.string;
ctx.textBaseline = 'middle';
ctx.textAlign = options.textAlign;
if (setTextStrokeStyle(ctx, options)) {
labels.forEach((l, i) => ctx.strokeText(l, x, y + (i * lh)));
applyLabelDecoration(ctx, {x, y}, labels, fonts);
}
ctx.fillStyle = options.color;
labels.forEach((l, i) => ctx.fillText(l, x, y + (i * lh)));
applyLabelContent(ctx, {x, y}, labels, {fonts, colors});
ctx.restore();
}

Expand All @@ -164,6 +159,50 @@ function setTextStrokeStyle(ctx, options) {
}
}

function calculateLabelSize(ctx, lines, fonts, strokeWidth) {
ctx.save();
const count = lines.length;
let width = 0;
let height = strokeWidth;
for (let i = 0; i < count; i++) {
const font = fonts[Math.min(i, fonts.length - 1)];
ctx.font = font.string;
const text = lines[i];
width = Math.max(width, ctx.measureText(text).width + strokeWidth);
height += font.lineHeight;
}
ctx.restore();
return {width, height};
}

function applyLabelDecoration(ctx, {x, y}, labels, fonts) {
ctx.beginPath();
let lhs = 0;
labels.forEach(function(l, i) {
const f = fonts[Math.min(i, fonts.length - 1)];
const lh = f.lineHeight;
ctx.font = f.string;
ctx.strokeText(l, x, y + lh / 2 + lhs);
lhs += lh;
});
ctx.stroke();
}

function applyLabelContent(ctx, {x, y}, labels, {fonts, colors}) {
let lhs = 0;
labels.forEach(function(l, i) {
const c = colors[Math.min(i, colors.length - 1)];
const f = fonts[Math.min(i, fonts.length - 1)];
const lh = f.lineHeight;
ctx.beginPath();
ctx.font = f.string;
ctx.fillStyle = c;
ctx.fillText(l, x, y + lh / 2 + lhs);
lhs += lh;
ctx.fill();
});
}

function getOpacity(value, elementValue) {
const opacity = isNumber(value) ? value : elementValue;
return isNumber(opacity) ? clamp(opacity, 0, 1) : 1;
Expand Down
Loading