Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
144 changes: 144 additions & 0 deletions samples/source/line/line-with-annotations-zoom-level.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<title>Line Chart with Annotations (Zoom Level)</title>

<style>
#chart {
max-width: 650px;
margin: 35px auto;
}
#chart-zoom-wrapper {
zoom: 80%;
}
</style>

<html>
<div id="chart-zoom-wrapper">
{{ charts[0] }}
</div>
</html>

<scripts>
<script src="../../assets/stock-prices.js"></script>
</scripts>

<chart>
<options>
chart: {
height: 350,
type: 'line',
id: 'areachart-2'
},
annotations: {
yaxis: [{
y: 8200,
borderColor: '#00E396',
label: {
borderColor: '#00E396',
style: {
color: '#fff',
background: '#00E396',
},
text: 'Support',
}
}, {
y: 8600,
y2: 9000,
borderColor: '#000',
fillColor: '#FEB019',
opacity: 0.2,
label: {
borderColor: '#333',
style: {
fontSize: '10px',
color: '#333',
background: '#FEB019',
},
text: 'Y-axis range',
}
}],
xaxis: [{
x: new Date('23 Nov 2017').getTime(),
strokeDashArray: 0,
borderColor: '#775DD0',
label: {
borderColor: '#775DD0',
style: {
color: '#fff',
background: '#775DD0',
},
text: 'Anno Test',
}
}, {
x: new Date('26 Nov 2017').getTime(),
x2: new Date('28 Nov 2017').getTime(),
fillColor: '#B3F7CA',
opacity: 0.4,
label: {
borderColor: '#B3F7CA',
style: {
fontSize: '10px',
color: '#fff',
background: '#00E396',
},
offsetY: -10,
text: 'X-axis range',
}
}],
points: [{
x: new Date('01 Dec 2017').getTime(),
y: 8607.55,
marker: {
size: 8,
fillColor: '#fff',
strokeColor: 'red',
radius: 2,
cssClass: 'apexcharts-custom-class'
},
label: {
borderColor: '#FF4560',
offsetY: 0,
style: {
color: '#fff',
background: '#FF4560',
},

text: 'Point Annotation',
}
}, {
x: new Date('08 Dec 2017').getTime(),
y: 9340.85,
marker: {
size: 0
},
image: {
path: '../../assets/images/ico-instagram.png'
}
}]
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'straight'
},
grid: {
padding: {
right: 30,
left: 20
}
},
title: {
text: 'Line with Annotations',
align: 'left'
},
labels: series.monthDataSeries1.dates,
xaxis: {
type: 'datetime',
},
</options>

<series>
[{
data: series.monthDataSeries1.prices
}]
</series>
</chart>
188 changes: 188 additions & 0 deletions samples/vanilla-js/line/line-with-annotations-zoom-level.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Line Chart with Annotations (Zoom Level)</title>

<link href="../../assets/styles.css" rel="stylesheet" />

<style>

#chart {
max-width: 650px;
margin: 35px auto;
}
#chart-zoom-wrapper {
zoom: 80%;
}

</style>

<script>
window.Promise ||
document.write(
'<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"><\/script>'
)
window.Promise ||
document.write(
'<script src="https://cdn.jsdelivr.net/npm/eligrey-classlist-js-polyfill@1.2.20171210/classList.min.js"><\/script>'
)
window.Promise ||
document.write(
'<script src="https://cdn.jsdelivr.net/npm/findindex_polyfill_mdn"><\/script>'
)
</script>


<script src="../../../dist/apexcharts.js"></script>


<script>
// Replace Math.random() with a pseudo-random number generator to get reproducible results in e2e tests
// Based on https://gist.github.com/blixt/f17b47c62508be59987b
var _seed = 42;
Math.random = function() {
_seed = _seed * 16807 % 2147483647;
return (_seed - 1) / 2147483646;
};
</script>

<script src="../../assets/stock-prices.js"></script>
</head>

<body>
<div id="chart-zoom-wrapper">
<div id="chart"></div>
</div>

<script>

var options = {
series: [{
data: series.monthDataSeries1.prices
}],
chart: {
height: 350,
type: 'line',
id: 'areachart-2'
},
annotations: {
yaxis: [{
y: 8200,
borderColor: '#00E396',
label: {
borderColor: '#00E396',
style: {
color: '#fff',
background: '#00E396',
},
text: 'Support',
}
}, {
y: 8600,
y2: 9000,
borderColor: '#000',
fillColor: '#FEB019',
opacity: 0.2,
label: {
borderColor: '#333',
style: {
fontSize: '10px',
color: '#333',
background: '#FEB019',
},
text: 'Y-axis range',
}
}],
xaxis: [{
x: new Date('23 Nov 2017').getTime(),
strokeDashArray: 0,
borderColor: '#775DD0',
label: {
borderColor: '#775DD0',
style: {
color: '#fff',
background: '#775DD0',
},
text: 'Anno Test',
}
}, {
x: new Date('26 Nov 2017').getTime(),
x2: new Date('28 Nov 2017').getTime(),
fillColor: '#B3F7CA',
opacity: 0.4,
label: {
borderColor: '#B3F7CA',
style: {
fontSize: '10px',
color: '#fff',
background: '#00E396',
},
offsetY: -10,
text: 'X-axis range',
}
}],
points: [{
x: new Date('01 Dec 2017').getTime(),
y: 8607.55,
marker: {
size: 8,
fillColor: '#fff',
strokeColor: 'red',
radius: 2,
cssClass: 'apexcharts-custom-class'
},
label: {
borderColor: '#FF4560',
offsetY: 0,
style: {
color: '#fff',
background: '#FF4560',
},

text: 'Point Annotation',
}
}, {
x: new Date('08 Dec 2017').getTime(),
y: 9340.85,
marker: {
size: 0
},
image: {
path: '../../assets/images/ico-instagram.png'
}
}]
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'straight'
},
grid: {
padding: {
right: 30,
left: 20
}
},
title: {
text: 'Line with Annotations',
align: 'left'
},
labels: series.monthDataSeries1.dates,
xaxis: {
type: 'datetime',
},
};

var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();


</script>


</body>
</html>
20 changes: 12 additions & 8 deletions src/modules/annotations/Helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default class Helpers {
)

if (xAnno !== null) {
const xAnnoCoord = xAnno.getBoundingClientRect()
const xAnnoCoord = xAnno.getBBox()
xAnno.setAttribute(
'x',
parseFloat(xAnno.getAttribute('x')) - xAnnoCoord.height + 4
Expand All @@ -39,9 +39,13 @@ export default class Helpers {
return null
}

const elGridRect = w.globals.dom.baseEl
.querySelector('.apexcharts-grid')
.getBoundingClientRect()
// We compute the difference between the bounding client rect and the BBox to
// correctly scale the drawn rectangle when chart is in a container with a
// CSS zoom level != 100%.
const gridEl = w.globals.dom.baseEl.querySelector('.apexcharts-grid')
const elGridRect = gridEl.getBoundingClientRect()
const gridBBox = gridEl.getBBox()
const zoom = elGridRect.width / gridBBox.width || 1
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

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

const zoom = elGridRect.width / gridBBox.width || 1 does not guard against gridBBox.width === 0. In that case the expression becomes Infinity, and subsequent divisions by zoom collapse coordinates/sizes toward 0. Use an explicit zero-check (and consider checking both width/height if you want extra safety) so zoom reliably falls back to 1 when the BBox is degenerate.

Suggested change
const zoom = elGridRect.width / gridBBox.width || 1
let zoom = 1
if (gridBBox.width) {
const candidateZoom = elGridRect.width / gridBBox.width
if (Number.isFinite(candidateZoom) && candidateZoom > 0) {
zoom = candidateZoom
}
}

Copilot uses AI. Check for mistakes.

const coords = annoEl.getBoundingClientRect()

Expand All @@ -56,13 +60,13 @@ export default class Helpers {
;[ptop, pbottom, pleft, pright] = [pleft, pright, ptop, pbottom]
}

const x1 = coords.left - elGridRect.left - pleft
const y1 = coords.top - elGridRect.top - ptop
const x1 = (coords.left - elGridRect.left) / zoom - pleft
const y1 = (coords.top - elGridRect.top) / zoom - ptop
const elRect = this.annoCtx.graphics.drawRect(
x1 - w.globals.barPadForNumericAxis,
y1,
coords.width + pleft + pright,
coords.height + ptop + pbottom,
coords.width / zoom + pleft + pright,
coords.height / zoom + ptop + pbottom,
anno.label.borderRadius,
anno.label.style.background,
1,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading