<template>
<div>
<div class="ink-wrap">
<div class="ink-bar">
<span class="readout" id="ink-readout"
>Drag a labelled point to reposition it</span
>
</div>
<div id="chart">
<apexchart
type="line"
height="380"
:options="chartOptions"
:series="series"
></apexchart>
</div>
<div class="ink-note">
The chart sets <code>chart.ink.enabled</code>, so its point annotations
are <b>draggable</b>: grab a labelled marker and move it.
<b>Click a note</b> to open its floating editor card: rename it inline,
pick an accent color, toggle bold, step the text size, grow/shrink or
reshape the <b>marker</b>, or <b>delete</b> the note (Enter commits,
Escape closes; the swatches come from
<code>chart.ink.noteColors</code>). On release of a drag the new
position is converted back to data coordinates and written to
<code>annotations.points[].x/y</code>, so the annotation stays glued to
that data location on later zoom or resize. Use <b>+ Note</b> (the
palette, from <code>chart.ink.palette</code>) then click the plot to
drop a new labelled note. The x-axis <b>Window</b> band moves (drag its
middle) or resizes (drag an edge), and the y-axis <b>Target</b> line
drags vertically. Fires <code>annotationDragged</code> /
<code>annotationEdited</code> / <code>annotationStyled</code> /
<code>annotationCreated</code> / <code>annotationDeleted</code>. With
the history (Rewind) feature enabled, <b>Ctrl/Cmd+Z</b> undoes any edit
and Shift+Ctrl/Cmd+Z redoes it. Needs the <code>ink</code> feature. (Set
<code>draggable:false</code> on a single annotation to pin it.)
</div>
</div>
</div>
</template>
<script>
import VueApexCharts from 'vue-apexcharts'
import ApexCharts from 'apexcharts'
function signal() {
var out = []
for (var i = 0; i < 40; i++) {
var v = 50 + 22 * Math.sin(i / 4.0) + 9 * Math.sin(i / 1.4) + i * 0.4
out.push([i, Math.round(v)])
}
return out
}
export default {
components: {
apexchart: VueApexCharts,
},
data: function () {
return {
series: [
{ name: 'Signal', data: signal() }
],
chartOptions: {
chart: {
id: 'signal',
type: 'line',
height: 380,
fontFamily: 'Helvetica, Arial, sans-serif',
animations: { enabled: false },
ink: { enabled: true, palette: true },
history: { enabled: true },
toolbar: { show: false },
zoom: { enabled: false },
},
stroke: { width: 2, curve: 'smooth' },
colors: ['#0EA5E9'],
title: { text: 'Signal (drag the callouts)', align: 'left' },
xaxis: { type: 'numeric', title: { text: 'Sample' } },
yaxis: { title: { text: 'Value' } },
annotations: {
points: [
{
x: 6, y: 74, id: 'peak',
marker: { size: 7, fillColor: '#fff', strokeColor: '#0EA5E9', strokeWidth: 3 },
label: { text: 'Peak', borderColor: '#0EA5E9', style: { color: '#fff', background: '#0EA5E9' } },
},
{
x: 20, y: 52, id: 'dip',
marker: { size: 7, fillColor: '#fff', strokeColor: '#ef4444', strokeWidth: 3 },
label: { text: 'Watch', borderColor: '#ef4444', style: { color: '#fff', background: '#ef4444' } },
},
],
xaxis: [
{
x: 10, x2: 16, id: 'window', fillColor: '#7DD3FC', opacity: 0.28,
label: { text: 'Window', style: { color: '#fff', background: '#0EA5E9' } },
},
],
yaxis: [
{
y: 62, id: 'target', borderColor: '#f59e0b', strokeDashArray: 4,
label: { text: 'Target', style: { color: '#fff', background: '#f59e0b' } },
},
],
},
},
}
},
mounted: function () {
// The vue-apexcharts wrapper owns the render, so reach the live instance by
// its chart.id. Poll until it exists, then wire the same readout the vanilla
// build does. The ink feature drives the chart instance directly (drag / edit
// annotations), so no reactive data changes and the chart is not re-rendered.
var timer = window.setInterval(function () {
var chart = ApexCharts.getChartByID('signal')
if (!chart) return
window.clearInterval(timer)
var readout = document.getElementById('ink-readout')
chart.addEventListener('annotationDragged', function (c, o) {
readout.textContent =
"Moved '" + o.id + "' to x=" + Number(o.x).toFixed(2) + ', y=' + Number(o.y).toFixed(2)
})
chart.addEventListener('annotationStyled', function (c, o) {
readout.textContent = "Restyled '" + o.id + "'"
})
chart.addEventListener('annotationDeleted', function (c, o) {
readout.textContent = "Deleted '" + o.id + "' (Ctrl/Cmd+Z restores it)"
})
}, 50)
},,
}
</script>
<style>
.ink-wrap {
max-width: 820px;
margin: 12px auto;
font-family: Helvetica, Arial, sans-serif;
}
.ink-bar {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
margin: 4px 6px 10px;
}
.ink-bar .readout {
font-family: monospace;
font-size: 13px;
color: #445;
}
.ink-note {
background: #eef2ff;
border-left: 3px solid #0ea5e9;
padding: 11px 15px;
font-size: 13px;
color: #234;
border-radius: 2px;
line-height: 1.6;
margin: 12px 6px;
}
.ink-note code {
background: #dfe3ff;
padding: 1px 5px;
border-radius: 3px;
}
</style>