DevExtreme v23.2 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.

Custom Annotations

Annotations are designed to show additional information about visualized data. You can display images, text blocks, and custom content in annotations. To create custom annotations, specify the following Chart properties.

annotations[]

The annotations array specifies a collection of annotations. This demo uses a data source array to create this collection. Each object has the argument property and a data object. The argument property positions an annotation at a specific argument, and the data object stores data from a data source.

You can use the annotations array to configure each annotation individually.

commonAnnotationSettings

Use the commonAnnotationSettings object to specify settings common to all annotations in the chart. In this object, assign custom to the type property. To anchor annotations to a series point, specify the series property. Finally, declare the SVG markup in the annotation template.

Note that individual annotation settings override the common annotation settings.

customizeAnnotation

You can also implement the customizeAnnotation function to customize an individual chart annotation.

en.wikipedia.org
Backend API
<template> <div> <DxChart id="chart" :data-source="populationData" title="Top 5 Most Populated States in US" > <DxSeries type="bar" argument-field="name" value-field="population" name="Population" /> <DxLegend :visible="false" /> <DxCommonAnnotationSettings :allow-dragging="true" type="custom" series="Population" template="annotationTemplate" /> <DxAnnotation v-for="data in populationData" :argument="data.name" :data="data" :key="data.name" /> <template #annotationTemplate="{ data }"> <AnnotationTemplate :annotation="data"/> </template> </DxChart> </div> </template> <script setup lang="ts"> import { DxChart, DxSeries, DxLegend, DxAnnotation, DxCommonAnnotationSettings, } from 'devextreme-vue/chart'; import { populationData } from './data.ts'; import AnnotationTemplate from './AnnotationTemplate.vue'; </script> <style scoped> #chart { height: 440px; } </style>
<template> <svg class="annotation"> <image :href="getImagePath()" width="60" height="40" /> <rect x="0" y="0" class="border" /> <text x="70" y="25" class="state" >{{ annotation.argument }}</text> <text x="0" y="60" > <tspan class="caption">Capital:</tspan><tspan class="capital" dx="5" >{{ data.capital }}</tspan><tspan dy="14" x="0" class="caption" >Population:</tspan><tspan class="population" dx="5" >{{ formatNumber(data.population) }}</tspan><tspan dy="14" x="0" class="caption" >Area:</tspan><tspan class="area" dx="5" >{{ formatNumber(data.area) }}</tspan><tspan dx="5">km</tspan><tspan class="sup" dy="-2" >2</tspan> </text> </svg> </template> <script setup lang="ts"> import { ref } from 'vue'; const props = withDefaults(defineProps<{ annotation?: Record<string, any> }>(), { annotation: () => ({}), }); const data = ref(props.annotation.data); const getImagePath = () => `../../../../images/flags/${data.value.name.replace(/\s/, '')}.svg`; const formatNumber = new Intl.NumberFormat('en-US', { minimumFractionDigits: 0 }).format; </script> <style> .annotation { font-size: 12px; } .border { width: 60px; height: 40px; stroke: rgba(191, 191, 191, 0.25); stroke-width: 1px; fill: transparent; } .state { font-weight: 500; font-size: 14px; } .caption { font-weight: 500; } .sup { font-size: 0.8em; } </style>
window.exports = window.exports || {}; window.config = { transpiler: 'plugin-babel', meta: { '*.vue': { loader: 'vue-loader', }, '*.ts': { loader: 'demo-ts-loader', }, '*.svg': { loader: 'svg-loader', }, 'devextreme/time_zone_utils.js': { 'esModule': true, }, 'devextreme/localization.js': { 'esModule': true, }, 'devextreme/viz/palette.js': { 'esModule': true, }, }, paths: { 'root:': '../../../../../', 'npm:': 'https://unpkg.com/', }, map: { 'vue': 'npm:vue@3.3.4/dist/vue.esm-browser.js', 'vue-loader': 'npm:dx-systemjs-vue-browser@1.1.1/index.js', 'demo-ts-loader': 'root:utils/demo-ts-loader.js', 'svg-loader': 'root:utils/svg-loader.js', 'mitt': 'npm:mitt/dist/mitt.umd.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@23.2.5/cjs', 'devextreme-vue': 'npm:devextreme-vue@23.2.5/cjs', 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.6.4/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.5/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.51/dist/dx-gantt.js', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', 'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js', 'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.4/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js', }, packages: { 'devextreme-vue': { main: 'index.js', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.12/inferno/package.json', ], babelOptions: { sourceMaps: false, stage0: true, }, }; System.config(window.config);
export const populationData = [{ name: 'California', population: 38802500, capital: 'Sacramento', area: 423967, }, { name: 'Texas', population: 26956958, capital: 'Austin', area: 695662, }, { name: 'Florida', population: 19893297, capital: 'Tallahassee', area: 170312, }, { name: 'New York', population: 19746227, capital: 'Albany', area: 141297, }, { name: 'Illinois', population: 12880580, capital: 'Springfield', area: 149995, }];
import { createApp } from 'vue'; import App from './App.vue'; createApp(App).mount('#app');
<!DOCTYPE html> <html> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/23.2.5/css/dx.light.css" /> <script src="https://unpkg.com/typescript@4.2.4/lib/typescript.js"></script> <script type="module"> import * as vueCompilerSFC from "https://unpkg.com/@vue/compiler-sfc@3.3.4/dist/compiler-sfc.esm-browser.js"; window.vueCompilerSFC = vueCompilerSFC; </script> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import("./index.ts"); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"> </div> </div> </body> </html>