DevExtreme v24.1 is now available.

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

Your search did not match any results.

Custom Axis Position

The Chart component initially displays axes along pane borders. Several other predefined axis layout properties are available. In this demo, the customPosition property is used to move each axis, so that it is displayed on the specified value of another axis. When you position the argument axis, use values from the value axis (in the same type and format) and vice versa.

Backend API
<dx-chart id="chart" [dataSource]="dataSource"> <dxo-common-series-settings type="scatter"></dxo-common-series-settings> <dxi-series argumentField="x1" valueField="y1"></dxi-series> <dxi-series argumentField="x2" valueField="y2"> <dxo-point symbol="triangleDown"></dxo-point> </dxi-series> <dxo-argument-axis #argument [visualRange]="[-20, 20]" [offset]="0" [customPosition]="0" > </dxo-argument-axis> <dxi-value-axis #value [visualRange]="[-20, 20]" [endOnTick]="false" [offset]="0" [customPosition]="0" > </dxi-value-axis> <dxo-legend [visible]="false"></dxo-legend> </dx-chart> <div class="options"> <div class="caption">Options</div> <div class="common"> <div class="block left"> <span>Argument Axis</span> <div class="option"> <span>Custom position:</span> <dx-number-box [value]="0" [showSpinButtons]="true" [(value)]="argument.customPosition" [inputAttr]="{ 'aria-label': 'Custom Position' }" > </dx-number-box> </div> <div class="option"> <span>Offset:</span> <dx-number-box [value]="0" [showSpinButtons]="true" [(value)]="argument.offset" [inputAttr]="{ 'aria-label': 'Offset' }" > </dx-number-box> </div> </div> <div class="block right"> <span>Value Axis</span> <div class="option"> <span>Custom position:</span> <dx-number-box [value]="0" [showSpinButtons]="true" [(value)]="value.customPosition" [inputAttr]="{ 'aria-label': 'Custom Position' }" > </dx-number-box> </div> <div class="option"> <span>Offset:</span> <dx-number-box [value]="0" [showSpinButtons]="true" [(value)]="value.offset" [inputAttr]="{ 'aria-label': 'Offset' }" > </dx-number-box> </div> </div> </div> </div>
import { NgModule, Component, enableProdMode } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { DxChartModule, DxNumberBoxModule } from 'devextreme-angular'; import { Service, ScatterData } from './app.service'; if (!/localhost/.test( { enableProdMode(); } @Component({ selector: 'demo-app', providers: [Service], templateUrl: 'app/app.component.html', styleUrls: ['app/app.component.css'], preserveWhitespaces: true, }) export class AppComponent { dataSource: ScatterData[]; constructor(service: Service) { this.dataSource = service.generateDataSource(); } } @NgModule({ imports: [ BrowserModule, DxChartModule, DxNumberBoxModule, ], declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
.options { padding: 20px; margin-top: 20px; background-color: rgba(191, 191, 191, 0.15); } .option { margin-top: 10px; display: flex; align-items: center; justify-content: space-between; } .option > span { margin-right: 20px; } .caption { font-size: 18px; font-weight: 700; } ::ng-deep .option > .dx-numberbox { width: 90px; margin-left: auto; } ::ng-deep .common { width: 488px; } ::ng-deep ::ng-deep .block { vertical-align: middle; margin-top: 10px; } ::ng-deep .left { display: inline-block; } ::ng-deep .right { float: right; } ::ng-deep .block > span { font-size: 18px; font-weight: 500; }
import { Injectable } from '@angular/core'; export class ScatterData { x1: number; y1: number; x2: number; y2: number; } @Injectable() export class Service { random(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } generateDataSource() { let x1; let x2; let y1; let y2; let i; const ds = []; for (i = 0; i < 20; i++) { x1 = this.random(5, 15); y1 = this.random(5, 15); ds.push({ x1, y1, x2, y2, }); } for (i = 0; i < 20; i++) { x2 = this.random(5, 15); y2 = this.random(-15, -5); ds.push({ x1, y1, x2, y2, }); } for (i = 0; i < 20; i++) { x2 = this.random(-15, -5); y2 = this.random(5, 15); ds.push({ x1, y1, x2, y2, }); } for (i = 0; i < 20; i++) { x1 = this.random(-15, -5); y1 = this.random(-15, -5); ds.push({ x1, y1, x2, y2, }); } return ds; } }
// In real applications, you should not transpile code in the browser. // You can see how to create your own application with Angular and DevExtreme here: // const componentNames = [ 'accordion', 'action-sheet', 'autocomplete', 'bar-gauge', 'box', 'bullet', 'button-group', 'button', 'calendar', 'chart', 'check-box', 'circular-gauge', 'color-box', 'context-menu', 'data-grid', 'date-box', 'date-range-box', 'defer-rendering', 'diagram', 'draggable', 'drawer', 'drop-down-box', 'drop-down-button', 'file-manager', 'file-uploader', 'filter-builder', 'form', 'funnel', 'gallery', 'gantt', 'html-editor', 'linear-gauge', 'list', 'load-indicator', 'load-panel', 'lookup', 'map', 'menu', 'multi-view', 'nested', 'number-box', 'pie-chart', 'pivot-grid-field-chooser', 'pivot-grid', 'polar-chart', 'popover', 'popup', 'progress-bar', 'radio-group', 'range-selector', 'range-slider', 'recurrence-editor', 'resizable', 'responsive-box', 'sankey', 'scheduler', 'scroll-view', 'select-box', 'slider', 'sortable', 'sparkline', 'speed-dial-action', 'splitter', 'switch', 'tab-panel', 'tabs', 'tag-box', 'text-area', 'text-box', 'tile-view', 'toast', 'toolbar', 'tooltip', 'tree-list', 'tree-map', 'tree-view', 'validation-group', 'validation-summary', 'validator', 'vector-map', ]; window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, }, meta: { 'typescript': { 'exports': 'ts', }, 'devextreme/time_zone_utils.js': { 'esModule': true, }, 'devextreme/localization.js': { 'esModule': true, }, 'devextreme/viz/palette.js': { 'esModule': true, }, '@angular/platform-browser-dynamic': { 'esModule': true, }, '@angular/platform-browser': { 'esModule': true, }, '@angular/core': { 'esModule': true, }, '@angular/common': { 'esModule': true, }, '@angular/common/http': { 'esModule': true, }, '@angular/animations': { 'esModule': true, }, '@angular/forms': { 'esModule': true, }, }, paths: { 'npm:': '', 'bundles:': '../../../../bundles/', }, map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', /* @angular */ '@angular/compiler': 'bundles:@angular/compiler.umd.js', '@angular/platform-browser-dynamic': 'bundles:@angular/platform-browser-dynamic.umd.js', '@angular/core': 'bundles:@angular/core.umd.js', '@angular/core/primitives/signals': 'bundles:@angular/core.primitives.signals.umd.js', '@angular/common': 'bundles:@angular/common.umd.js', '@angular/common/http': 'bundles:@angular/common-http.umd.js', '@angular/platform-browser': 'bundles:@angular/platform-browser.umd.js', '@angular/platform-browser/animations': 'bundles:@angular/platform-browser.umd.js', '@angular/forms': 'bundles:@angular/forms.umd.js', /* devextreme */ 'devextreme': 'npm:devextreme@24.1.3/cjs', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.13', 'devextreme/bundles/dx.all': 'npm:devextreme@24.1.3/bundles/dx.all.js', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.8', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.56', /* devextreme-angular umd maps */ 'devextreme-angular': 'bundles:devextreme-angular/devextreme-angular.umd.js', 'devextreme-angular/core': 'bundles:devextreme-angular/devextreme-angular-core.umd.js', ...componentNames.reduce((acc, name) => { acc[`devextreme-angular/ui/${name}`] = `bundles:devextreme-angular/devextreme-angular-ui-${name}.umd.js`; return acc; }, {}), 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'tslib': 'npm:tslib@2.6.1/tslib.js', 'rxjs': 'npm:rxjs@7.5.3/dist/bundles/rxjs.umd.js', 'rxjs/operators': 'npm:rxjs@7.5.3/dist/cjs/operators/index.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', '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', // Prettier 'prettier/standalone': 'npm:prettier@2.8.8/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js', }, packages: { 'app': { main: './app.component.ts', defaultExtension: 'ts', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, 'rxjs': { defaultExtension: 'js', }, 'rxjs/operators': { defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.13/inferno/package.json', 'npm:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devexpress-diagram@2.2.8/package.json', 'npm:devexpress-gantt@4.1.56/package.json', ], }; System.config(window.config); // System.import('@angular/compiler').catch(console.error.bind(console));
<!DOCTYPE html> <html xmlns="" lang="en"> <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=5.0" /> <link rel="stylesheet" type="text/css" href="" /> <script src=""></script> <script src=""></script> <script src=""></script> <script src=""></script> <script src="config.js"></script> <script> System.import("app").catch(console.error.bind(console)); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <demo-app>Loading...</demo-app> </div> </body> </html>

Regardless of which automatic axis layout type you use, the Chart Control allows you to apply manual offsets. Specify the offset property in pixels to keep the axis position unchanged when users scroll or zoom the Chart data. The offset property shifts the axis from the specified position as follows:

  • If the axis is horizontal, a negative offset shifts the axis to the top, a positive offset shifts it to the bottom.

  • If the axis is vertical, a negative offset shifts the axis to the left, a positive offset shifts it to the right.

In this demo, you can use controls under the Chart to change the customPosition and offset properties for both axes.