From 0ea15b349a8eeebe5cbd6e845c623b541c4a4bff Mon Sep 17 00:00:00 2001 From: stockiNail Date: Sat, 21 May 2022 20:28:52 +0200 Subject: [PATCH 1/9] Add `beforeDraw` and `afterDraw` hooks to the annotations --- src/annotation.js | 16 +++++--- src/elements.js | 5 ++- src/events.js | 15 ++----- src/helpers/helpers.options.js | 19 +++++++++ src/hooks.js | 73 +++++++++++++++++++++++++++++++++ test/fixtures/line/hooks.js | 71 ++++++++++++++++++++++++++++++++ test/fixtures/line/hooks.png | Bin 0 -> 17008 bytes 7 files changed, 182 insertions(+), 17 deletions(-) create mode 100644 src/hooks.js create mode 100644 test/fixtures/line/hooks.js create mode 100644 test/fixtures/line/hooks.png diff --git a/src/annotation.js b/src/annotation.js index 070b3f044..b931f165c 100644 --- a/src/annotation.js +++ b/src/annotation.js @@ -1,6 +1,7 @@ import {Chart} from 'chart.js'; import {clipArea, unclipArea, isObject, isArray} from 'chart.js/helpers'; -import {handleEvent, hooks, updateListeners} from './events'; +import {handleEvent, eventHooks, updateListeners} from './events'; +import {drawElement, elementHooks, updateHooks, resetCounters} from './hooks'; import {adjustScaleRange, verifyScaleOptions} from './scale'; import {updateElements, resolveType} from './elements'; import {annotationTypes} from './types'; @@ -8,6 +9,7 @@ import {requireVersion} from './helpers'; import {version} from '../package.json'; const chartStates = new Map(); +const hooks = eventHooks.concat(elementHooks); export default { id: 'annotation', @@ -34,6 +36,8 @@ export default { listeners: {}, listened: false, moveListened: false, + hooks: {}, + hooked: false, hovered: [] }); }, @@ -67,6 +71,7 @@ export default { updateListeners(chart, state, options); updateElements(chart, state, options, args.mode); state.visibleElements = state.elements.filter(el => !el.skip && el.options.display); + updateHooks(chart, state, options); }, beforeDatasetsDraw(chart, _args, options) { @@ -78,6 +83,7 @@ export default { }, beforeDraw(chart, _args, options) { + resetCounters(chartStates.get(chart)); draw(chart, 'beforeDraw', options.clip); }, @@ -142,18 +148,18 @@ export default { function draw(chart, caller, clip) { const {ctx, canvas, chartArea} = chart; - const {visibleElements} = chartStates.get(chart); let area = {left: 0, top: 0, width: canvas.width, height: canvas.height}; + const state = chartStates.get(chart); if (clip) { clipArea(ctx, chartArea); area = chartArea; } - const drawableElements = getDrawableElements(visibleElements, caller, area).sort((a, b) => a.element.options.z - b.element.options.z); + const drawableElements = getDrawableElements(state.visibleElements, caller, area).sort((a, b) => a.element.options.z - b.element.options.z); for (const item of drawableElements) { - item.element.draw(chart.ctx, item.area); + drawElement(chart, state, item); } if (clip) { @@ -171,7 +177,7 @@ function getDrawableElements(elements, caller, area) { const box = 'getBoundingBox' in el ? el.getBoundingBox() : area; for (const sub of el.elements) { if (sub.options.display && sub.options.drawTime === caller) { - drawableElements.push({element: sub, area: box}); + drawableElements.push({element: sub, area: box, main: el}); } } } diff --git a/src/elements.js b/src/elements.js index 5d296f7ab..ba996cc53 100644 --- a/src/elements.js +++ b/src/elements.js @@ -1,12 +1,15 @@ import {Animations} from 'chart.js'; import {isObject, defined} from 'chart.js/helpers'; -import {hooks} from './events'; +import {eventHooks} from './events'; +import {elementHooks} from './hooks'; import {annotationTypes} from './types'; const directUpdater = { update: Object.assign }; +const hooks = eventHooks.concat(elementHooks); + /** * @typedef { import("chart.js").Chart } Chart * @typedef { import("chart.js").UpdateMode } UpdateMode diff --git a/src/events.js b/src/events.js index 57052c9f9..3bda88b5c 100644 --- a/src/events.js +++ b/src/events.js @@ -1,5 +1,6 @@ -import {defined, callback} from 'chart.js/helpers'; +import {callback} from 'chart.js/helpers'; import {getElements} from './interaction'; +import {loadHooks} from './helpers'; const moveHooks = ['enter', 'leave']; @@ -8,7 +9,7 @@ const moveHooks = ['enter', 'leave']; * @typedef { import('../../types/options').AnnotationPluginOptions } AnnotationPluginOptions */ -export const hooks = moveHooks.concat('click'); +export const eventHooks = moveHooks.concat('click'); /** * @param {Chart} chart @@ -16,18 +17,10 @@ export const hooks = moveHooks.concat('click'); * @param {AnnotationPluginOptions} options */ export function updateListeners(chart, state, options) { - state.listened = false; + state.listened = loadHooks(options, eventHooks, state.listeners); state.moveListened = false; state._getElements = getElements; // for testing - hooks.forEach(hook => { - if (typeof options[hook] === 'function') { - state.listened = true; - state.listeners[hook] = options[hook]; - } else if (defined(state.listeners[hook])) { - delete state.listeners[hook]; - } - }); moveHooks.forEach(hook => { if (typeof options[hook] === 'function') { state.moveListened = true; diff --git a/src/helpers/helpers.options.js b/src/helpers/helpers.options.js index b800d02e0..ab892e2a1 100644 --- a/src/helpers/helpers.options.js +++ b/src/helpers/helpers.options.js @@ -85,3 +85,22 @@ export function toPosition(value) { export function isBoundToPoint(options) { return options && (defined(options.xValue) || defined(options.yValue)); } + +/** + * @param {Object} options + * @param {Array} hooks + * @param {Object} hooksContainer + * @returns {boolean} + */ +export function loadHooks(options, hooks, hooksContainer) { + let activated = false; + hooks.forEach(hook => { + if (typeof options[hook] === 'function') { + activated = true; + hooksContainer[hook] = options[hook]; + } else if (defined(hooksContainer[hook])) { + delete hooksContainer[hook]; + } + }); + return activated; +} diff --git a/src/hooks.js b/src/hooks.js new file mode 100644 index 000000000..1f215b919 --- /dev/null +++ b/src/hooks.js @@ -0,0 +1,73 @@ +import {callback} from 'chart.js/helpers'; +import {loadHooks} from './helpers'; + +/** + * @typedef { import("chart.js").Chart } Chart + * @typedef { import('../../types/options').AnnotationPluginOptions } AnnotationPluginOptions + * @typedef { import('../../types/element').AnnotationElement } AnnotationElement + */ + +export const elementHooks = ['afterDraw', 'beforeDraw']; + +/** + * @param {Chart} chart + * @param {Object} state + * @param {AnnotationPluginOptions} options + */ +export function updateHooks(chart, state, options) { + const visibleElements = state.visibleElements; + state.hooked = loadHooks(options, elementHooks, state.hooks); + + if (!state.hooked) { + visibleElements.forEach(scope => { + if (!state.hooked) { + elementHooks.forEach(hook => { + if (typeof scope.options[hook] === 'function') { + state.hooked = true; + } + }); + } + }); + } +} + +/** + * @param {Chart} chart + * @param {Object} state + * @param {{element: AnnotationElement, area: {top: number, left: number, width: number, height: number}, main?: AnnotationElement}} options + */ +export function drawElement(chart, state, item) { + const {element, area, main} = item; + const el = main || element; + el._drawnElements += 1; + beforeDraw(state, el); + element.draw(chart.ctx, area); + afterDraw(state, el); +} + +/** + * @param {Object} state + */ +export function resetCounters(state) { + state.visibleElements.forEach(function(el) { + el._drawnElements = 0; + el._drawCount = (el.elements || []).length + 1; + }); +} + +function beforeDraw(state, el) { + if (state.hooked && el._drawnElements === 1) { + invokeHook(state, el, 'beforeDraw'); + } +} + +function afterDraw(state, el) { + if (state.hooked && el._drawnElements === el._drawCount) { + invokeHook(state, el, 'afterDraw'); + } +} + +function invokeHook(state, el, hook) { + const callbackHook = el.options[hook] || state.hooks[hook]; + callback(callbackHook, [el.$context]); +} diff --git a/test/fixtures/line/hooks.js b/test/fixtures/line/hooks.js new file mode 100644 index 000000000..cd105e671 --- /dev/null +++ b/test/fixtures/line/hooks.js @@ -0,0 +1,71 @@ +module.exports = { + config: { + type: 'scatter', + options: { + layout: { + padding: { + right: 50, + left: 50 + } + }, + scales: { + x: { + display: true, + min: 0, + max: 100, + ticks: { + display: false + } + }, + y: { + display: true, + min: 0, + max: 100, + ticks: { + display: false + } + } + }, + plugins: { + annotation: { + clip: false, + annotations: { + line1: { + type: 'line', + scaleID: 'y', + value: 50, + borderColor: 'black', + borderWidth: 5, + label: { + rotation: 'auto', + borderRadius: 10, + borderWidth: 3, + content: 'afterDraw hook', + display: true + }, + afterDraw(context) { + const ctx = context.chart.ctx; + const {x, y, x2, y2, options} = context.element; + ctx.save(); + ctx.lineWidth = options.borderWidth; + ctx.strokeStyle = options.borderColor; + ctx.setLineDash([6, 6]); + ctx.lineDashOffset = options.borderDashOffset; + ctx.beginPath(); + ctx.moveTo(x - 50, y); + ctx.lineTo(x, y); + ctx.moveTo(x2, y2); + ctx.lineTo(x2 + 50, y); + ctx.stroke(); + ctx.restore(); + } + }, + } + } + } + } + }, + options: { + spriteText: true + } +}; diff --git a/test/fixtures/line/hooks.png b/test/fixtures/line/hooks.png new file mode 100644 index 0000000000000000000000000000000000000000..bb2bea33b239be50b801ee4f104119ad05867d89 GIT binary patch literal 17008 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 ae{NJHyAE!{fmcWXp=-CSxf2v~^uGWJ6bYvQ literal 0 HcmV?d00001 From 6d86eecbf03ffb8e1316c90d1ce5652c2366b7de Mon Sep 17 00:00:00 2001 From: stockiNail Date: Mon, 23 May 2022 11:48:37 +0200 Subject: [PATCH 2/9] adds test cases --- src/hooks.js | 3 +- test/fixtures/box/hooks.js | 124 +++++++++++++++++++++++++++++ test/fixtures/box/hooks.png | Bin 0 -> 18004 bytes test/fixtures/ellipse/hooks.js | 101 ++++++++++++++++++++++++ test/fixtures/ellipse/hooks.png | Bin 0 -> 37051 bytes test/fixtures/label/hooks.js | 134 ++++++++++++++++++++++++++++++++ test/fixtures/label/hooks.png | Bin 0 -> 27542 bytes test/fixtures/point/hooks.js | 104 +++++++++++++++++++++++++ test/fixtures/point/hooks.png | Bin 0 -> 26778 bytes test/fixtures/polygon/hooks.js | 104 +++++++++++++++++++++++++ test/fixtures/polygon/hooks.png | Bin 0 -> 28797 bytes test/index.js | 3 +- test/specs/hooks.spec.js | 58 ++++++++++++++ test/utils.js | 17 ++++ 14 files changed, 646 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/box/hooks.js create mode 100644 test/fixtures/box/hooks.png create mode 100644 test/fixtures/ellipse/hooks.js create mode 100644 test/fixtures/ellipse/hooks.png create mode 100644 test/fixtures/label/hooks.js create mode 100644 test/fixtures/label/hooks.png create mode 100644 test/fixtures/point/hooks.js create mode 100644 test/fixtures/point/hooks.png create mode 100644 test/fixtures/polygon/hooks.js create mode 100644 test/fixtures/polygon/hooks.png create mode 100644 test/specs/hooks.spec.js diff --git a/src/hooks.js b/src/hooks.js index 1f215b919..c406190fd 100644 --- a/src/hooks.js +++ b/src/hooks.js @@ -51,7 +51,8 @@ export function drawElement(chart, state, item) { export function resetCounters(state) { state.visibleElements.forEach(function(el) { el._drawnElements = 0; - el._drawCount = (el.elements || []).length + 1; + const subElements = (el.elements || []).filter(item => item.options.display); + el._drawCount = subElements.length + 1; }); } diff --git a/test/fixtures/box/hooks.js b/test/fixtures/box/hooks.js new file mode 100644 index 000000000..f72ba5dc9 --- /dev/null +++ b/test/fixtures/box/hooks.js @@ -0,0 +1,124 @@ +module.exports = { + config: { + type: 'scatter', + options: { + scales: { + x: { + display: true, + min: -10, + max: 10, + ticks: { + display: false + } + }, + y: { + display: true, + min: -10, + max: 10, + ticks: { + display: false + } + } + }, + plugins: { + legend: false, + annotation: { + drawTime: 'afterDraw', + annotations: { + box1: { + type: 'box', + xScaleID: 'x', + yScaleID: 'y', + xMin: -9, + yMin: 9, + xMax: -1, + yMax: 1, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x + 10, element.y + 10, 10, 5, 3); + } + }, + box2: { + type: 'box', + xScaleID: 'x', + yScaleID: 'y', + xMin: 1, + yMin: 9, + xMax: 9, + yMax: 1, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + label: { + enabled: true, + content: 'p: 25%,75%', + position: { + x: '25%', + y: '75%' + } + }, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x + element.width - 10, element.y + 10, 10, 5, 3); + } + }, + box3: { + type: 'box', + xScaleID: 'x', + yScaleID: 'y', + xMin: -9, + yMin: -1, + xMax: -1, + yMax: -9, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + label: { + enabled: true, + content: 'p: 50%,50%', + position: { + x: '50%', + y: '50%' + } + }, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x + 10, element.y + element.height - 10, 10, 5, 3); + } + }, + box4: { + type: 'box', + xScaleID: 'x', + yScaleID: 'y', + xMin: 1, + yMin: -1, + xMax: 9, + yMax: -9, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + label: { + enabled: true, + content: 'p: 100%,0%', + position: { + x: '100%', + y: '0%' + } + }, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x + element.width - 10, element.y + element.height - 10, 10, 5, 3); + } + } + } + } + } + } + }, + options: { + spriteText: true + } +}; diff --git a/test/fixtures/box/hooks.png b/test/fixtures/box/hooks.png new file mode 100644 index 0000000000000000000000000000000000000000..6a7d63f280acb51b5e23dfca515c69ce68aa5a4e GIT binary patch literal 18004 zcmeHvXH-<%mhP?~6i5<8kPK3Qf+WcZ6e^*}CQy-#kI zNK#^<K2L-}o9+%#Kd`a#YP9dF`1x4~@j02|) z(#1P7PMhhxv3!}G^#gPNwMcQa<2mwxyv6vJ)^)c}&mI>~By(HjHbAS72)FN$Ykjts zh8}-d5j;)&?fMe`gOw7qEOlox4(V~u;L;J4>FZ`W-ne8L`?$&Gd$O8=$@rz7K8ba+ z)|s1W6hY@!2_3FuPBGUqKB*fx9Zg>mcldJL_gVMD78Y>F*n!t9t<=1Vq87oR+i(0^vTUZ{IC@Q4=U_-h5^pMFKaj862c%rk3PeCm4O zD+z3<^U9x~LtWo^;??!^sBTOIy<$rNZ~AEu>{Eb>@A!Z%teXk@()FyQqT^HX{*a3v z%t(gTThg!PdD&r|$z~Q>M5avvwe@=~G>T{qz%@%?Fknbhu?~=7Q@79xJHqReypDWX zH2o|?V!YYVd{VGD*%FY})qY00a8e)^?krgO$ZT&AeoTf9CtJ{V;FGqo(H!03moBhl zI9e91;zTwDB6&VNfHX61emH*bIHpN^K!^B3WxOctb|s!oXVWNZ#le48E?S2(Q)i1kqP6g0a1c#Pp64ftp&zaOZkoS{H9tuzs3eR#0+G+X zp5PB7xkVgru|d9a*({M(c8rPjPCU3dH<{2a>GKB7=u=P!QPzIDi5)$ng|=S^{!1c# z*UYO!*mm41Gur!fpnvAY9_8k4z~#*L+xwj88>dM9ShpF0$4{h2PLY0-zC}(5rcB;$ zkI6~r$KwZGTs(+RO+RO5ObY67T{MoH=lIouHP(IS{DtPE##t3|DYiz$kqy1B{m$ZQ zRZ+x>9|w@MgzRPuqAZP~{UY)1-bb3nY+S@i6WIBJ;6b_}Z-eX;7qv6391$l;a-2!Y zG83`CUWq7%Gi$|+++v%sK)+M7b)_WZ1ChhewObA2Yur>F{I)u@{mf0Kf5`zIFII!? z^@3jD<8+m3-(s?x`qhck$ORdjp#p6`&8BIRjRMCx-mgt*xa4Tu6?mudsdsHn@10w^ z?YL_6h{b~285_~XJogBz^mCjYzrXG{?6{ar|C9r!5@U7W6~OG=b_GbX*jA>J;nY(k zYqV*;eDrYL^rDaZVyFl@ilVJt7pkqU*b!pPpyq#KQhh=G7swHJ3 z)FyJcnh|En@{SFtY*fp;5K+Nc~vb`~GTx%$z>w9GS4P=YsG4?t)Vj zHw@-)p**+wolgw%7Ll8Bw=bI77dKJnrPL@k-lYqpg4HzJX!6zRd37^C$QOLIdxs;&DE}n0kW&QN5-Zl@1EsJ)@QQcoQ z&n!hJyNdaLCX4{=`T=);B~er-1#9V^=OA z$opQ?VhsLS8P&06PQ_ZH^G71ye>uI9vqUJ-5_}^Ev-i;-sLecj z2{g1^=qZ)h<$HU*5DykyM>KX+IcHQ3>qOu{{ds>UFe zC1MVwuX<4zBZpg5yu4Y*L(J=l=2@wi9j(3MM$wiJ(Ag|n@QsrRzC7B?XwqKL!fsWy0lbMW@6DxcF0+nc8S@w=aFKmYt}^oboiknI=+5n`cB5kL z5=kvTx-{V7YOmwO_mMhiXSYFveiA~{qn#-J?XLY;aN1IT5 zFsSY{x>6`r%L!�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 literal 0 HcmV?d00001 diff --git a/test/fixtures/ellipse/hooks.js b/test/fixtures/ellipse/hooks.js new file mode 100644 index 000000000..fb94c491b --- /dev/null +++ b/test/fixtures/ellipse/hooks.js @@ -0,0 +1,101 @@ +module.exports = { + config: { + type: 'scatter', + options: { + scales: { + x: { + display: true, + min: -10, + max: 10, + ticks: { + display: false + } + }, + y: { + display: true, + min: -10, + max: 10, + ticks: { + display: false + } + } + }, + plugins: { + legend: false, + annotation: { + drawTime: 'afterDraw', + annotations: { + ellipse1: { + type: 'ellipse', + xScaleID: 'x', + yScaleID: 'y', + xMin: -9, + yMin: 7, + xMax: -2, + yMax: 3, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x + 10, element.y + 10, 10, 5, 3); + } + }, + ellipse2: { + type: 'ellipse', + xScaleID: 'x', + yScaleID: 'y', + xMin: 1, + yMin: 7, + xMax: 9, + yMax: 3, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x + element.width - 10, element.y + 10, 10, 5, 3); + } + }, + ellipse3: { + type: 'ellipse', + xScaleID: 'x', + yScaleID: 'y', + xMin: -9, + yMin: -7, + xMax: -1, + yMax: -3, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + rotation: 45, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x + 10, element.y + element.height - 10, 10, 5, 3); + } + }, + ellipse4: { + type: 'ellipse', + xScaleID: 'x', + yScaleID: 'y', + xMin: 1, + yMin: -1, + xMax: 9, + yMax: -9, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x + element.width - 10, element.y + element.height - 10, 10, 5, 3); + } + } + } + } + } + } + }, + options: { + spriteText: true + } +}; diff --git a/test/fixtures/ellipse/hooks.png b/test/fixtures/ellipse/hooks.png new file mode 100644 index 0000000000000000000000000000000000000000..038206f084c925d145ddb4989976cce676ff6384 GIT binary patch literal 37051 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

lTd~Atc7Qr5kihx}kzrOCpDYe`AV?&Hl~r z2ezzLxFWbreuy5J9?IymY8$THpg+guzChHa%Hf}cr)D)vhF$E0rGv;|d`I*2WGT>1 zWLVu+3g4qh2mPE*a&32brbSeydCs+_$m#y015s&X@QzIuKi8i5BMr*+bZvr8+Ytrt zF>8Dy=Bcd`Eu(B@aTZvs`%_D%`k$TAoq-)j4%l5Y@kb_eX!*Ujx>(2L^Drw~f)dGK z09kTx^w{y>y5=wQy&aH0)%`en<#68eYqF2~SQPH$Up$_0+Ka8fmT-ibTG(I%2Vm)JDDW%mkMduBkt)X;T>?NZzPLQsDxd zT6Cjsk+pnK^p8H>|0Z^)9!P6rJiIg)OWHbbG$|v0ylxUe(={mM_pTIDwZ`V*MrA@7 zEqB8(oPS!|)@-AO1ts+vP=uv8+Ry0gQPTrC)@YvdyOiyqa{1S0*pInPEF9>n^9078 zvwO?QL}JLi=yeXQPYv*-eUG&Y+hYP?n5TI9{qm{B9_S zHVy@TIWp)jr1Ib3;GsAlj2i#>pyZW4;M~zMKGct7!=WYgaWmgfpfF~-up!2p{or?I zR&KoN^N08)!4l5boezGEHeNbaBAQIgE33z%Pu^Lnus96CYoPkK!v z$~a34I0kka6(x$kf*?7nJ13>zCOt(HkiT(VeT3yezCdX0SKhdylR>GA#d1^8Y3)ou z{6FKXZm5DUFR_8=ii|&AJhHyPP-8+myIp0Y-z|`T4Tz&VXHPEfXAkg{ zPfn=Pyu$X2DtTWrx36;cEB_rxNkO6W-u|QAF%M%Rpcno6qDu1;*@Zsz748WQ2^W! z>9WnlJV-2FGsLgy>Un>Pr3Q{Fiw>jorsj0w>8aZoG3I5E^@FY)vJ*klHR7$315U=R zacUv={ZlIPvCZ}Ww(*tpJaY*XlYJ|S*6lVgUZE2VcaA!}mGRUnhyTr>l`olNdpQTC z{~fM;pd6DPqqLUhbd=$-i>cwfXuZoIOK&f;kGf0P2yb`w###L*7CAp-XI}rWQv*g{ z_UG!G(>c36jNR-sG*HIDYtb_v(>_2=Lq2uB>}>a^RH}U^p!|Ppnw>efC#9~gc43}g z!A-o((6G#$1J15#V3OXt8gsNZTuqbfuDk{cZM}1DDD9DoMr}cL^?6;wvZvh+aH_Vc z()I+T8wE-K@6T^tnhO0|oXZ$feu@6SRhkRM^OuufUoGq~{tK?E;XI(SE3|Nf+8-0$_ai$+7gsE#JE#NnT` zBLCZfw94%ST)Fj(03EzPbD3kxz5hlZuCDZE^93M+2c_+^-PQW=FNMEZ{qdeE{r~?1 z;1Cd>6r%t(mF=8bwl4n452||Rg21(78PCOaG4DeZf(-OYCfB&Xh3*5Y>tU3uhP7;_ o@t2BGWndM4@-~-J_iR4mEqL69F8;zB1Ofk)OV literal 0 HcmV?d00001 diff --git a/test/fixtures/label/hooks.js b/test/fixtures/label/hooks.js new file mode 100644 index 000000000..40efd1241 --- /dev/null +++ b/test/fixtures/label/hooks.js @@ -0,0 +1,134 @@ +module.exports = { + config: { + type: 'scatter', + options: { + scales: { + x: { + display: true, + ticks: { + display: false + }, + min: -10, + max: 10 + }, + y: { + display: true, + ticks: { + display: false + }, + min: -10, + max: 10 + } + }, + plugins: { + legend: false, + annotation: { + drawTime: 'afterDraw', + annotations: { + label1: { + type: 'label', + xScaleID: 'x', + yScaleID: 'y', + xMin: 1, + yMin: 1, + xMax: 8, + yMax: 8, + backgroundColor: 'rgba(33, 101, 171, 0.5)', + borderColor: 'rgb(33, 101, 171)', + borderWidth: 1, + content: ['This is my text', 'and this is the second row of my text'], + padding: { + x: 10, + y: 10 + }, + position: { + y: 'end' + }, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x + 10, element.y + 10, 5, 5, 3); + } + }, + point1: { + type: 'point', + xScaleID: 'x', + yScaleID: 'y', + xMin: 1, + yMin: 1, + xMax: 8, + yMax: 8, + radius: 10, + backgroundColor: 'red', + borderColor: 'black', + borderWidth: 1 + }, + box1: { + type: 'box', + xScaleID: 'x', + yScaleID: 'y', + xMin: 1, + yMin: 1, + xMax: 8, + yMax: 8, + backgroundColor: 'transparent', + borderColor: 'red', + borderWidth: 1, + }, + label2: { + type: 'label', + xScaleID: 'x', + yScaleID: 'y', + xMin: -8, + yMin: -8, + xMax: 1, + yMax: 1, + backgroundColor: 'rgba(33, 101, 171, 0.5)', + borderColor: 'rgb(33, 101, 171)', + borderWidth: 1, + content: ['This is my text', 'and this is the second row of my text'], + padding: { + x: 10, + y: 10 + }, + position: { + y: 'end' + }, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x + element.width - 10, element.y + 10, 5, 5, 3); + } + }, + point2: { + type: 'point', + xScaleID: 'x', + yScaleID: 'y', + xMin: -8, + yMin: -8, + xMax: 1, + yMax: 1, + radius: 10, + backgroundColor: 'red', + borderColor: 'black', + borderWidth: 1 + }, + box2: { + type: 'box', + xScaleID: 'x', + yScaleID: 'y', + xMin: -8, + yMin: -8, + xMax: 1, + yMax: 1, + backgroundColor: 'transparent', + borderColor: 'red', + borderWidth: 1, + } + } + } + } + } + }, + options: { + spriteText: true + } +}; diff --git a/test/fixtures/label/hooks.png b/test/fixtures/label/hooks.png new file mode 100644 index 0000000000000000000000000000000000000000..b875a52ccad62c141630d8188da0d29bd4f79639 GIT binary patch literal 27542 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 literal 0 HcmV?d00001 diff --git a/test/fixtures/point/hooks.js b/test/fixtures/point/hooks.js new file mode 100644 index 000000000..67660b259 --- /dev/null +++ b/test/fixtures/point/hooks.js @@ -0,0 +1,104 @@ +module.exports = { + config: { + type: 'scatter', + options: { + scales: { + x: { + display: true, + min: -10, + max: 10, + ticks: { + display: false + } + }, + y: { + display: true, + min: -10, + max: 10, + ticks: { + display: false + } + } + }, + plugins: { + legend: false, + annotation: { + drawTime: 'afterDraw', + annotations: { + point1: { + type: 'point', + xScaleID: 'x', + yScaleID: 'y', + xMin: -9, + yMin: 9, + xMax: -1, + yMax: 1, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + radius: 50, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.centerX - element.options.radius, element.y + element.options.radius, 10, 5, 3); + } + }, + point2: { + type: 'point', + xScaleID: 'x', + yScaleID: 'y', + xMin: 1, + yMin: 9, + xMax: 9, + yMax: 1, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + radius: 50, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.centerX + element.options.radius, element.y + element.options.radius, 10, 5, 3); + } + }, + point3: { + type: 'point', + xScaleID: 'x', + yScaleID: 'y', + xMin: -9, + yMin: -1, + xMax: -1, + yMax: -9, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + radius: 50, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x, element.y, 10, 5, 3); + } + }, + point4: { + type: 'point', + xScaleID: 'x', + yScaleID: 'y', + xMin: 1, + yMin: -1, + xMax: 9, + yMax: -9, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + radius: 50, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.centerX, element.centerY, 10, 5, 3); + } + } + } + } + } + } + }, + options: { + spriteText: true + } +}; diff --git a/test/fixtures/point/hooks.png b/test/fixtures/point/hooks.png new file mode 100644 index 0000000000000000000000000000000000000000..1484473fa1d716a0ff1216c3b14540f273cb41c0 GIT binary patch literal 26778 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 E155kk3IG5A literal 0 HcmV?d00001 diff --git a/test/fixtures/polygon/hooks.js b/test/fixtures/polygon/hooks.js new file mode 100644 index 000000000..aeb8bda27 --- /dev/null +++ b/test/fixtures/polygon/hooks.js @@ -0,0 +1,104 @@ +module.exports = { + config: { + type: 'scatter', + options: { + scales: { + x: { + display: true, + min: -10, + max: 10, + ticks: { + display: false + } + }, + y: { + display: true, + min: -10, + max: 10, + ticks: { + display: false + } + } + }, + plugins: { + legend: false, + annotation: { + drawTime: 'afterDraw', + annotations: { + polygon1: { + type: 'polygon', + xScaleID: 'x', + yScaleID: 'y', + xMin: -9, + yMin: 9, + xMax: -1, + yMax: 1, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + radius: 50, + sides: 5, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.centerX - element.options.radius, element.y + element.options.radius, 10, 5, 3); + } + }, + polygon2: { + type: 'polygon', + xScaleID: 'x', + yScaleID: 'y', + xMin: 1, + yMin: 9, + xMax: 9, + yMax: 1, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + radius: 50, + sides: 8, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.centerX + element.options.radius, element.y + element.options.radius, 10, 5, 3); + } + }, + polygon3: { + type: 'polygon', + xScaleID: 'x', + yScaleID: 'y', + xMin: -9, + yMin: -1, + xMax: -1, + yMax: -9, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + radius: 50, + sides: 10, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.x, element.y, 10, 5, 3); + } + }, + polygon4: { + type: 'polygon', + xScaleID: 'x', + yScaleID: 'y', + xMin: 1, + yMin: -1, + xMax: 9, + yMax: -9, + backgroundColor: 'white', + borderColor: 'red', + borderWidth: 2, + radius: 50, + afterDraw(ctx) { + const {chart, element} = ctx; + window.drawStar(chart.ctx, element.centerX, element.centerY, 10, 5, 3); + } + } + } + } + } + } + } +}; diff --git a/test/fixtures/polygon/hooks.png b/test/fixtures/polygon/hooks.png new file mode 100644 index 0000000000000000000000000000000000000000..b406650daa5be1bb37e1bf15f1abcab33ff3b786 GIT binary patch literal 28797 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%0Dy4{`%@JnH|ryxUV_83?TW6SrgeLRE^vd8pm~=1 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 literal 0 HcmV?d00001 diff --git a/test/index.js b/test/index.js index 47a0dcd4c..84bb74f40 100644 --- a/test/index.js +++ b/test/index.js @@ -1,6 +1,6 @@ import {acquireChart, addMatchers, releaseCharts, specsFromFixtures, triggerMouseEvent, afterEvent} from 'chartjs-test-utils'; import {testEvents, eventPoint0, getCenterPoint} from './events'; -import {createCanvas, getAnnotationElements, scatterChart, stringifyObject, interactionData} from './utils'; +import {createCanvas, getAnnotationElements, scatterChart, stringifyObject, interactionData, drawStar} from './utils'; import * as helpers from '../src/helpers'; window.helpers = helpers; @@ -12,6 +12,7 @@ window.testEvents = testEvents; window.eventPoint0 = eventPoint0; window.getCenterPoint = getCenterPoint; window.createCanvas = createCanvas; +window.drawStar = drawStar; window.getAnnotationElements = getAnnotationElements; window.scatterChart = scatterChart; window.stringifyObject = stringifyObject; diff --git a/test/specs/hooks.spec.js b/test/specs/hooks.spec.js new file mode 100644 index 000000000..6754c8c56 --- /dev/null +++ b/test/specs/hooks.spec.js @@ -0,0 +1,58 @@ +describe('Hooks', function() { + + const chartConfig = { + type: 'scatter', + options: { + animation: false, + scales: { + x: { + display: false, + min: 0, + max: 10 + }, + y: { + display: false, + min: 0, + max: 10 + } + }, + plugins: { + legend: false, + annotation: { + } + } + }, + }; + + ['box', 'ellipse', 'label', 'line', 'point', 'polygon'].forEach(function(type) { + const options = { + type, + xMin: 2, + yMin: 2, + xMax: 8, + yMax: 8, + label: { + display: true, + content: 'test' + } + }; + + const pluginOpts = chartConfig.options.plugins.annotation; + + [pluginOpts, options].forEach(function(targetOptions) { + + it(`should detect hooks invocations on ${type}`, function() { + targetOptions.beforeDraw = function({element}) { + element.invocations = (element.invocations || 0) + 1; + }; + targetOptions.afterDraw = function({element}) { + element.invocations = (element.invocations || 0) - 1; + }; + pluginOpts.annotations = [options]; + const chart = window.acquireChart(chartConfig); + const element = window.getAnnotationElements(chart)[0]; + expect(element.invocations).toBe(0); + }); + }); + }); +}); diff --git a/test/utils.js b/test/utils.js index bc1ac5e99..3baa7e7fc 100644 --- a/test/utils.js +++ b/test/utils.js @@ -15,6 +15,23 @@ export function createCanvas() { return canvas; } +// https://stackoverflow.com/questions/25837158/how-to-draw-a-star-by-using-canvas-html5 +export function drawStar(ctx, x, y, radius, spikes, inset) { + ctx.save(); + ctx.beginPath(); + ctx.translate(x, y); + ctx.moveTo(0, 0 - radius); + for (let i = 0; i < spikes; i++) { + ctx.rotate(Math.PI / spikes); + ctx.lineTo(0, 0 - (radius * inset)); + ctx.rotate(Math.PI / spikes); + ctx.lineTo(0, 0 - radius); + } + ctx.closePath(); + ctx.fill(); + ctx.restore(); +} + export function getAnnotationElements(chart) { return window['chartjs-plugin-annotation']._getState(chart).elements; } From 816b7e0b24a303dfacc715bcfff2a7c9e50b06ea Mon Sep 17 00:00:00 2001 From: stockiNail Date: Mon, 23 May 2022 11:56:10 +0200 Subject: [PATCH 3/9] adds types --- types/options.d.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/types/options.d.ts b/types/options.d.ts index fc55257d7..7958c7e45 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -1,5 +1,5 @@ import { Color, PointStyle, BorderRadius, CoreInteractionOptions } from 'chart.js'; -import { AnnotationEvents, PartialEventContext } from './events'; +import { AnnotationEvents, PartialEventContext, EventContext } from './events'; import { LabelOptions, BoxLabelOptions, LabelTypeOptions } from './label'; export type DrawTime = 'afterDraw' | 'afterDatasetsDraw' | 'beforeDraw' | 'beforeDatasetsDraw'; @@ -18,6 +18,11 @@ export type AnnotationType = keyof AnnotationTypeRegistry; export type AnnotationOptions = { [key in TYPE]: { type: key } & AnnotationTypeRegistry[key] }[TYPE] +interface AnnotationHooks { + beforeDraw?(context: EventContext): void, + afterDraw?(context: EventContext): void +} + interface ShadowOptions { backgroundShadowColor?: Scriptable, borderShadowColor?: Scriptable, @@ -26,7 +31,7 @@ interface ShadowOptions { shadowOffsetY?: Scriptable } -export interface CoreAnnotationOptions extends AnnotationEvents, ShadowOptions { +export interface CoreAnnotationOptions extends AnnotationEvents, ShadowOptions, AnnotationHooks { id?: string, display?: Scriptable, adjustScaleRange?: Scriptable, @@ -157,7 +162,7 @@ export interface AnnotationPluginCommonOptions { drawTime?: Scriptable } -export interface AnnotationPluginOptions extends AnnotationEvents { +export interface AnnotationPluginOptions extends AnnotationEvents, AnnotationHooks { animations?: Record, annotations: AnnotationOptions[] | Record, clip?: boolean, From 2a6ef517bfe28fefa0a8d30671dbad2e94cf05a8 Mon Sep 17 00:00:00 2001 From: stockiNail Date: Mon, 23 May 2022 12:11:25 +0200 Subject: [PATCH 4/9] adds doc --- docs/guide/configuration.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index 7ef439717..4f203637d 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -80,3 +80,14 @@ The following options are available for all annotation types. These options can ::: If the event callbacks explicitly returns `true`, the chart will re-render automatically after processing the event completely. This is important when there are the annotations that require re-draws (for instance, after a change of a rendering options). + +## Hooks + +The following hooks are available for all annotation types. These hooks can be specified per annotation, or at the top level which apply to all annotations. + +These hooks enable some user customizations on the annotations. + +| Name | Type | Notes +| ---- | ---- | ---- +| `beforeDraw` | `(context) => void ` | Called before that the annotation is being drawn. +| `afterDraw` | `(context) => void` | Called after the annotation has been drawn. From 69439f36cd1fe9aaacf8af1624efd38a3b437289 Mon Sep 17 00:00:00 2001 From: stockiNail Date: Mon, 23 May 2022 12:24:20 +0200 Subject: [PATCH 5/9] improves tests on hooks invocations --- test/specs/hooks.spec.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/specs/hooks.spec.js b/test/specs/hooks.spec.js index 6754c8c56..3173ceff0 100644 --- a/test/specs/hooks.spec.js +++ b/test/specs/hooks.spec.js @@ -44,14 +44,19 @@ describe('Hooks', function() { it(`should detect hooks invocations on ${type}`, function() { targetOptions.beforeDraw = function({element}) { element.invocations = (element.invocations || 0) + 1; + element.count = (element.count || 0) + 1; }; targetOptions.afterDraw = function({element}) { - element.invocations = (element.invocations || 0) - 1; + expect(element.invocations).toBe(1); + element.invocations--; + expect(element.count).toBe(1); + element.count++; }; pluginOpts.annotations = [options]; const chart = window.acquireChart(chartConfig); const element = window.getAnnotationElements(chart)[0]; expect(element.invocations).toBe(0); + expect(element.count).toBe(2); }); }); }); From b41615965b955984ecaa48faaead291b0d0bad36 Mon Sep 17 00:00:00 2001 From: stockiNail Date: Mon, 23 May 2022 13:08:48 +0200 Subject: [PATCH 6/9] adds sample --- docs/.vuepress/config.js | 1 + docs/samples/line/hook.md | 121 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 docs/samples/line/hook.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 6ab53be47..bb83d1850 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -146,6 +146,7 @@ module.exports = { 'line/image', 'line/datasetBars', 'line/animation', + 'line/hook', ] }, { diff --git a/docs/samples/line/hook.md b/docs/samples/line/hook.md new file mode 100644 index 000000000..fbed23a2c --- /dev/null +++ b/docs/samples/line/hook.md @@ -0,0 +1,121 @@ +# Outside of chart + +```js chart-editor +// +const DATA_COUNT = 8; +const MIN = 10; +const MAX = 100; + +Utils.srand(8); + +const labels = []; +for (let i = 0; i < DATA_COUNT; ++i) { + labels.push('' + i); +} + +const numberCfg = {count: DATA_COUNT, min: MIN, max: MAX}; + +const data = { + labels: labels, + datasets: [{ + data: Utils.numbers(numberCfg) + }] +}; +// + +// +const annotation = { + type: 'line', + borderColor: 'black', + borderWidth: 3, + scaleID: 'y', + value: 55, + beforeDraw: drawExtraLine +}; +// + +// +function drawExtraLine(context) { + const ctx = context.chart.ctx; + const width = context.chart.canvas.width; + const {x, y, x2, y2, options} = context.element; + ctx.save(); + ctx.lineWidth = options.borderWidth; + ctx.strokeStyle = options.borderColor; + ctx.setLineDash([6, 6]); + ctx.lineDashOffset = options.borderDashOffset; + ctx.beginPath(); + ctx.moveTo(0, y); + ctx.lineTo(x, y); + ctx.moveTo(x2, y2); + ctx.lineTo(width, y); + ctx.stroke(); + ctx.restore(); + return true; +} +// + +/* */ +const config = { + type: 'line', + data, + options: { + layout: { + padding: { + right: 50 + } + }, + scales: { + y: { + stacked: true + } + }, + plugins: { + annotation: { + clip: false, + annotations: { + annotation + } + } + } + } +}; +/* */ + +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(); + } + }, + { + name: 'Add data', + handler: function(chart) { + chart.data.labels.push(chart.data.labels.length); + chart.data.datasets.forEach(function(dataset, i) { + dataset.data.push(Utils.rand(MIN, MAX)); + }); + chart.update(); + } + }, + { + name: 'Remove data', + handler: function(chart) { + chart.data.labels.shift(); + chart.data.datasets.forEach(function(dataset, i) { + dataset.data.shift(); + }); + chart.update(); + } + } +]; + +module.exports = { + actions: actions, + config: config, +}; +``` From 962ee4be6485b9964677f981d89a276f7aed0de7 Mon Sep 17 00:00:00 2001 From: stockiNail Date: Mon, 6 Jun 2022 12:07:18 +0200 Subject: [PATCH 7/9] Add element diagrams to the annotation types guide --- docs/guide/types/box.md | 8 ++++++++ docs/guide/types/ellipse.md | 6 ++++++ docs/guide/types/label.md | 6 ++++++ docs/guide/types/line.md | 8 ++++++++ docs/guide/types/point.md | 6 ++++++ docs/guide/types/polygon.md | 6 ++++++ 6 files changed, 40 insertions(+) diff --git a/docs/guide/types/box.md b/docs/guide/types/box.md index 021f88ec2..9c6a16408 100644 --- a/docs/guide/types/box.md +++ b/docs/guide/types/box.md @@ -138,3 +138,11 @@ 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 box. If this value is an object, the `x` property defines the horizontal alignment in the box. Similarly, the `y` property defines the vertical alignment in the box. Possible options for both properties are `'start'`, `'center'`, `'end'`, a string in percentage format. Omitted property have value of the default, `'center'`. + +## Element + +The following diagram is showing the element properties about a `'box'` annotation: + +![box](../../img/elementBoxProps.png) + +The label of a box annotation is described as a [label annotation](./label#element) and accessible by `element.label`. diff --git a/docs/guide/types/ellipse.md b/docs/guide/types/ellipse.md index f78b081a1..4ead77c7c 100644 --- a/docs/guide/types/ellipse.md +++ b/docs/guide/types/ellipse.md @@ -91,3 +91,9 @@ If one of the axes does not match an axis in the chart, the ellipse will take th | `shadowBlur` | The amount of blur applied to shadow. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowBlur). | `shadowOffsetX` | The distance that shadow will be offset horizontally. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowOffsetX). | `shadowOffsetY` | The distance that shadow will be offset vertically. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowOffsetY). + +## Element + +The following diagram is showing the element properties about a `'ellipse'` annotation: + +![ellipse](../../img/elementEllipseProps.png) diff --git a/docs/guide/types/label.md b/docs/guide/types/label.md index 8ab09f698..e02af9ebc 100644 --- a/docs/guide/types/label.md +++ b/docs/guide/types/label.md @@ -217,3 +217,9 @@ All of these options can be [Scriptable](../options#scriptable-options). | `position` | `string` | `'auto'` | The position of callout, with respect to the label. Could be `left`, `top`, `right`, `bottom` or `auto`. | `side` | `number` | `5` | Width of the starter line of callout pointer. | `start` | `number`\|`string` | `'50%'` | The percentage of the separator dimension to use as starting point for callout pointer. Could be set in pixel by a number, or in percentage of the separator dimension by a string. + +## Element + +The following diagram is showing the element properties about a `'label'` annotation: + +![label](../../img/elementLabelProps.png) diff --git a/docs/guide/types/line.md b/docs/guide/types/line.md index 6afb0479d..ea55e8534 100644 --- a/docs/guide/types/line.md +++ b/docs/guide/types/line.md @@ -184,3 +184,11 @@ All of these options can be [Scriptable](../options#scriptable-options) | `shadowOffsetX` | `number` | `lineAnnotation.shadowOffsetX` | The distance that shadow, of the arrow head, will be offset horizontally. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowOffsetX). | `shadowOffsetY` | `number` | `lineAnnotation.shadowOffsetY` | The distance that shadow, of the arrow head, will be offset vertically. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowOffsetY). | `width` | `number` | `6` | The width of the arrow head in pixels. + +## Element + +The following diagram is showing the element properties about a `'line'` annotation: + +![line](../../img/elementLineProps.png) + +The label of a box annotation is described as a [label annotation](./label#element) and accessible by `element.label`. diff --git a/docs/guide/types/point.md b/docs/guide/types/point.md index 84063d2de..9f29a42a0 100644 --- a/docs/guide/types/point.md +++ b/docs/guide/types/point.md @@ -103,3 +103,9 @@ The 4 coordinates, xMin, xMax, yMin, yMax are optional. If not specified, the bo | `shadowBlur` | The amount of blur applied to shadow. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowBlur). | `shadowOffsetX` | The distance that shadow will be offset horizontally. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowOffsetX). | `shadowOffsetY` | The distance that shadow will be offset vertically. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowOffsetY). + +## Element + +The following diagram is showing the element properties about a `'point'` annotation: + +![point](../../img/elementPointProps.png) diff --git a/docs/guide/types/polygon.md b/docs/guide/types/polygon.md index fd9afb368..6476ae141 100644 --- a/docs/guide/types/polygon.md +++ b/docs/guide/types/polygon.md @@ -165,3 +165,9 @@ module.exports = { config }; ``` + +## Element + +The following diagram is showing the element properties about a `'polygon'` annotation: + +![polygon](../../img/elementPolygonProps.png) From 767bba96efa6e64005b75e54e744f25164b35be3 Mon Sep 17 00:00:00 2001 From: stockiNail Date: Wed, 28 Sep 2022 23:26:30 +0200 Subject: [PATCH 8/9] apply review --- src/annotation.js | 16 +++++++++++----- src/hooks.js | 42 ++++++------------------------------------ 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/src/annotation.js b/src/annotation.js index 076df7063..1c5442a8b 100644 --- a/src/annotation.js +++ b/src/annotation.js @@ -1,7 +1,7 @@ import {Chart} from 'chart.js'; import {clipArea, unclipArea, isObject, isArray} from 'chart.js/helpers'; import {handleEvent, eventHooks, updateListeners} from './events'; -import {drawElement, elementHooks, updateHooks, resetCounters} from './hooks'; +import {invokeHook, elementHooks, updateHooks} from './hooks'; import {adjustScaleRange, verifyScaleOptions} from './scale'; import {updateElements, resolveType} from './elements'; import {annotationTypes} from './types'; @@ -83,7 +83,6 @@ export default { }, beforeDraw(chart, _args, options) { - resetCounters(chartStates.get(chart)); draw(chart, 'beforeDraw', options.clip); }, @@ -156,7 +155,14 @@ function draw(chart, caller, clip) { const drawableElements = getDrawableElements(state.visibleElements, caller).sort((a, b) => a.element.options.z - b.element.options.z); for (const item of drawableElements) { - drawElement(chart, state, item); + const el = item.element; + if (item.main) { + invokeHook(state, el, 'beforeDraw'); + el.draw(ctx, chartArea); + invokeHook(state, el, 'afterDraw'); + } else { + el.draw(ctx, chartArea); + } } if (clip) { @@ -168,12 +174,12 @@ function getDrawableElements(elements, caller) { const drawableElements = []; for (const el of elements) { if (el.options.drawTime === caller) { - drawableElements.push({element: el}); + drawableElements.push({element: el, main: true}); } if (el.elements && el.elements.length) { for (const sub of el.elements) { if (sub.options.display && sub.options.drawTime === caller) { - drawableElements.push({element: sub, main: el}); + drawableElements.push({element: sub}); } } } diff --git a/src/hooks.js b/src/hooks.js index e2b20b7c6..5f42b8037 100644 --- a/src/hooks.js +++ b/src/hooks.js @@ -32,43 +32,13 @@ export function updateHooks(chart, state, options) { } /** - * @param {Chart} chart * @param {Object} state - * @param {{element: AnnotationElement, main?: AnnotationElement}} options + * @param {AnnotationElement} element + * @param {string} hook */ -export function drawElement(chart, state, item) { - const {element, main} = item; - const el = main || element; - el._drawnElements += 1; - beforeDraw(state, el); - element.draw(chart.ctx); - afterDraw(state, el); -} - -/** - * @param {Object} state - */ -export function resetCounters(state) { - state.visibleElements.forEach(function(el) { - el._drawnElements = 0; - const subElements = (el.elements || []).filter(item => item.options.display); - el._drawCount = subElements.length + 1; - }); -} - -function beforeDraw(state, el) { - if (state.hooked && el._drawnElements === 1) { - invokeHook(state, el, 'beforeDraw'); - } -} - -function afterDraw(state, el) { - if (state.hooked && el._drawnElements === el._drawCount) { - invokeHook(state, el, 'afterDraw'); +export function invokeHook(state, element, hook) { + if (state.hooked) { + const callbackHook = element.options[hook] || state.hooks[hook]; + return callback(callbackHook, [element.$context]); } } - -function invokeHook(state, el, hook) { - const callbackHook = el.options[hook] || state.hooks[hook]; - callback(callbackHook, [el.$context]); -} From bb5de657a8ee2210b8a45f126b56c9a91f982eb5 Mon Sep 17 00:00:00 2001 From: stockiNail Date: Wed, 28 Sep 2022 23:31:16 +0200 Subject: [PATCH 9/9] fix CC --- src/annotation.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/annotation.js b/src/annotation.js index 1c5442a8b..5c6849ae0 100644 --- a/src/annotation.js +++ b/src/annotation.js @@ -155,14 +155,7 @@ function draw(chart, caller, clip) { const drawableElements = getDrawableElements(state.visibleElements, caller).sort((a, b) => a.element.options.z - b.element.options.z); for (const item of drawableElements) { - const el = item.element; - if (item.main) { - invokeHook(state, el, 'beforeDraw'); - el.draw(ctx, chartArea); - invokeHook(state, el, 'afterDraw'); - } else { - el.draw(ctx, chartArea); - } + drawElement(ctx, chartArea, state, item); } if (clip) { @@ -186,3 +179,14 @@ function getDrawableElements(elements, caller) { } return drawableElements; } + +function drawElement(ctx, chartArea, state, item) { + const el = item.element; + if (item.main) { + invokeHook(state, el, 'beforeDraw'); + el.draw(ctx, chartArea); + invokeHook(state, el, 'afterDraw'); + } else { + el.draw(ctx, chartArea); + } +}