Tickmarks with Text

Wijmo's gauges allow you to show tickmarks along their faces using the showTicks and tickSpacing properties.

By default, the tickmarks do not display any text. But you can easily add text elements next to each tickmark using a little code.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import { Globalize, Point, clamp, addClass, toggleClass } from '@grapecity/wijmo'; import { RadialGauge, LinearGauge, GaugeDirection } from '@grapecity/wijmo.gauge'; import { ComboBox } from '@grapecity/wijmo.input'; document.readyState === 'complete' ? init() : window.onload = init; function init() { // create gauges let theRadialGauge = new RadialGauge('#theRadialGauge', { min: 0, max: 500, value: 100, startAngle: -45, sweepAngle: 270, showTicks: true, tickSpacing: 50, showText: 'Value', isReadOnly: false, refreshed: updateTicks }); let theLinearGauge = new LinearGauge('#theLinearGauge', { min: 0, max: 500, value: 100, showTicks: true, tickSpacing: 50, isReadOnly: false, refreshed: updateTicks }); // configure gauges new ComboBox('#start', { itemsSource: [-90, -45, 0, 45, 90], selectedItem: -45, selectedIndexChanged: function (s, e) { theRadialGauge.startAngle = s.selectedItem; } }); new ComboBox('#sweep', { itemsSource: [-360, -270, -180, -90, 90, 180, 270, 360], selectedItem: -270, selectedIndexChanged: function (s, e) { theRadialGauge.sweepAngle = s.selectedItem; } }); new ComboBox('#spacing', { itemsSource: [25, 50, 100, 200, 250], selectedItem: 100, selectedIndexChanged: function (s, e) { theRadialGauge.tickSpacing = s.selectedItem; theLinearGauge.tickSpacing = s.selectedItem; } }); new ComboBox('#direction', { itemsSource: 'Left,Right,Up,Down'.split(','), selectedItem: 'Right', selectedIndexChanged: function (s, e) { theLinearGauge.direction = s.selectedItem; toggleClass(theLinearGauge.hostElement, 'vertical', s.text.match(/Up|Down/) != null); } }); // add text to tickmarks function updateTicks(s) { // get tickText element let tickText = s.hostElement.querySelector('.tick-text'); if (!tickText) { let svg = s.hostElement.querySelector('svg'); tickText = document.createElementNS('http://www.w3.org/2000/svg', 'g'); addClass(tickText, 'tick-text'); svg.appendChild(tickText); } // clear tickText element while (tickText.firstChild) { tickText.removeChild(tickText.firstChild); } // update tickText parameters if (s.showTicks) { let step = s.tickSpacing || s.step; if (step) { if (s instanceof RadialGauge) { updateTicksRadial(s, tickText, step); } else if (s instanceof LinearGauge) { updateTicksLinear(s, tickText, step); } } } } function updateTicksRadial(s, tickText, step) { let rc = s.faceBounds, cx = rc.width / 2, cy = rc.height / 2, r = Math.min(rc.width, rc.height) / 2 * 1.15; for (let val = s.min; val <= s.max; val += step) { let pct = (s.max > s.min) ? (val - s.min) / (s.max - s.min) : 0, angle = (s.startAngle + 180 + s.sweepAngle * clamp(pct, 0, 1)) * Math.PI / 180; let te = document.createElementNS('http://www.w3.org/2000/svg', 'text'); te.setAttribute('x', (cx + r * Math.cos(angle)).toString()); te.setAttribute('y', (cy + r * Math.sin(angle)).toString()); te.textContent = Globalize.format(val, s.format); tickText.appendChild(te); } } function updateTicksLinear(s, tickText, step) { let rc = s.faceBounds; let pt = new Point(); pt.x = rc.left - rc.width * 0.75; pt.y = rc.top - rc.height * 0.75; for (let val = s.min; val <= s.max; val += step) { let pct = (s.max > s.min) ? (val - s.min) / (s.max - s.min) : 0; switch (s.direction) { case GaugeDirection.Left: pt.x = rc.right - rc.width * pct; break; case GaugeDirection.Right: pt.x = rc.left + rc.width * pct; break; case GaugeDirection.Up: pt.y = rc.bottom - rc.height * pct; break; case GaugeDirection.Down: pt.y = rc.top + rc.height * pct; break; } let te = document.createElementNS('http://www.w3.org/2000/svg', 'text'); te.setAttribute('x', pt.x.toString()); te.setAttribute('y', pt.y.toString()); te.textContent = Globalize.format(val, s.format); tickText.appendChild(te); } } } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Tickmark Text</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-xs-6"> <div id="theRadialGauge"></div> </div> <div class="col-xs-6"> <label> startAngle <input id="start"> </label> <label> sweepAngle <input id="sweep"> </label> <label> tickSpacing <input id="spacing"> </label> </div> </div> <div class="row"> <div class="col-xs-6"> <div id="theLinearGauge"></div> </div> <div class="col-xs-6"> <label> direction <input id="direction"> </label> </div> </div> </div> </body> </html> label { display: block; text-align: right; width: 300px; } label .wj-combobox { width: 120px; } .wj-gauge { margin: 4em auto; } .wj-gauge .tick-text text { text-anchor: middle; dominant-baseline: middle; /* center-aligns CSS text vertically! */ } .wj-radialgauge { width: 200px; } .wj-radialgauge .tick-text text { font-size: 50%; } .wj-lineargauge { width: 400px; } .wj-lineargauge.vertical { height: 300px; width: 2em; } .wj-lineargauge .tick-text text { font-size: 75%; } body { font-size: 16px; margin-bottom: 48pt; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import { Component, enableProdMode, NgModule } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { WjInputModule } from '@grapecity/wijmo.angular2.input'; import { WjGaugeModule } from '@grapecity/wijmo.angular2.gauge'; import { Gauge, LinearGauge, RadialGauge, GaugeDirection } from '@grapecity/wijmo.gauge'; import { ComboBox } from '@grapecity/wijmo.input'; import { Point, Globalize, toggleClass, addClass, clamp } from '@grapecity/wijmo'; @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { // update linear gauge direction and style directionChanged(gauge: LinearGauge, cmbDirection: ComboBox) { gauge.direction = GaugeDirection[cmbDirection.text]; toggleClass(gauge.hostElement, 'vertical', cmbDirection.text.match(/Up|Down/) != null); } // this should not be necessary, but I can't bind directly to the 'refreshed' event... init(gauge: any) { gauge.refreshed.addHandler(() => { this.updateTicks(gauge); }) } // update the tickmark text on a gauge updateTicks(gauge: Gauge) { // get tickText element let tickText = gauge.hostElement.querySelector('.tick-text'); if (!tickText) { let svg = gauge.hostElement.querySelector('svg'); tickText = document.createElementNS('http://www.w3.org/2000/svg', 'g'); addClass(tickText, 'tick-text'); svg.appendChild(tickText); } // clear tickText element while (tickText.firstChild) { tickText.removeChild(tickText.firstChild); } // update tickText parameters if (gauge.showTicks) { let step = gauge.tickSpacing || gauge.step; if (step) { if (gauge instanceof RadialGauge) { this.updateTicksRadial(gauge, tickText, step); } else if (gauge instanceof LinearGauge) { this.updateTicksLinear(gauge, tickText, step); } } } } // update the tickmark text on a radial gauge updateTicksRadial(gauge: RadialGauge, tickText: Element, step: number) { let rc = gauge.faceBounds, cx = rc.width / 2, cy = rc.height / 2, r = Math.min(rc.width, rc.height) / 2 * 1.15; for (let val = gauge.min; val <= gauge.max; val += step) { let pct = (gauge.max > gauge.min) ? (val - gauge.min) / (gauge.max - gauge.min) : 0, angle = (gauge.startAngle + 180 + gauge.sweepAngle * clamp(pct, 0, 1)) * Math.PI / 180; let te = document.createElementNS('http://www.w3.org/2000/svg', 'text'); te.setAttribute('x', (cx + r * Math.cos(angle)).toString()); te.setAttribute('y', (cy + r * Math.sin(angle)).toString()); te.textContent = Globalize.format(val, gauge.format); tickText.appendChild(te); } } // update the tickmark text on a linear gauge updateTicksLinear(gauge: LinearGauge, tickText: Element, step: number) { let rc = gauge.faceBounds; let pt = new Point(); pt.x = rc.left - rc.width * 0.75; pt.y = rc.top - rc.height * 0.75; for (let val = gauge.min; val <= gauge.max; val += step) { let pct = (gauge.max > gauge.min) ? (val - gauge.min) / (gauge.max - gauge.min) : 0; switch (gauge.direction) { case GaugeDirection.Left: pt.x = rc.right - rc.width * pct; break; case GaugeDirection.Right: pt.x = rc.left + rc.width * pct; break; case GaugeDirection.Up: pt.y = rc.bottom - rc.height * pct; break; case GaugeDirection.Down: pt.y = rc.top + rc.height * pct; break; } let te = document.createElementNS('http://www.w3.org/2000/svg', 'text'); te.setAttribute('x', pt.x.toString()); te.setAttribute('y', pt.y.toString()); te.textContent = Globalize.format(val, gauge.format); tickText.appendChild(te); } } } @NgModule({ imports: [WjGaugeModule, WjInputModule, BrowserModule], declarations: [AppComponent], bootstrap: [AppComponent] }) export class AppModule { } enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Tickmark Text</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.min.js"></script> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html> <div class="container-fluid"> <div class="row"> <div class="col-xs-6"> <wj-radial-gauge #theRadialGauge [min]="0" [max]="500" [value]="100" [startAngle]="-45" [sweepAngle]="270" [showTicks]="true" [tickSpacing]="50" [showText]="'Value'" [isReadOnly]="false" (initialized)="init(theRadialGauge)" (refreshed)="updateTicks(theRadialGauge)"> </wj-radial-gauge> </div> <div class="col-xs-6"> <label> startAngle <wj-combo-box [itemsSource]="[-90, -45, 0, 45, 90]" [(selectedValue)]="theRadialGauge.startAngle"> </wj-combo-box> </label> <label> sweepAngle <wj-combo-box [itemsSource]="[-360, -270, -180, -90, 90, 180, 270, 360]" [(selectedValue)]="theRadialGauge.sweepAngle"> </wj-combo-box> </label> <label> tickSpacing <wj-combo-box [itemsSource]="[25, 50, 100, 200, 250]" [(selectedValue)]="theRadialGauge.tickSpacing"> </wj-combo-box> </label> </div> </div> <div class="row"> <div class="col-xs-6"> <wj-linear-gauge #theLinearGauge [min]="0" [max]="500" [value]="100" [showTicks]="true" [tickSpacing]="theRadialGauge.tickSpacing" [isReadOnly]="false" (initialized)="init(theLinearGauge)" (refreshed)="updateTicks(theLinearGauge)"> </wj-linear-gauge> </div> <div class="col-xs-6"> <label> direction <wj-combo-box #cmbDirection [itemsSource]="['Left', 'Right', 'Up', 'Down']" [selectedItem]="'Right'" (selectedIndexChanged)="directionChanged(theLinearGauge, cmbDirection)"> </wj-combo-box> </label> </div> </div> </div> label { display: block; text-align: right; width: 300px; } label .wj-combobox { width: 120px; } .wj-gauge { margin: 4em auto; } .wj-gauge .tick-text text { text-anchor: middle; dominant-baseline: middle; /* center-aligns CSS text vertically! */ } .wj-radialgauge { width: 200px; } .wj-radialgauge .tick-text text { font-size: 50%; } .wj-lineargauge { width: 400px; } .wj-lineargauge.vertical { height: 300px; width: 2em; } .wj-lineargauge .tick-text text { font-size: 75%; } body { font-size: 16px; margin-bottom: 48pt; } <template> <div class="container-fluid"> <div class="row"> <div class="col-xs-6"> <wj-radial-gauge :min="0" :max="500" :value="100" :start-angle="startAngle" :sweep-angle="sweepAngle" :show-ticks="true" :tick-spacing="tickSpacing" :showText="'Value'" :isReadOnly="false" :refreshed="updateTicks"> </wj-radial-gauge> </div> <div class="col-xs-6"> <label> startAngle <wj-combo-box :itemsSource="startAngles" :selectedValue="startAngle" :selectedIndexChanged="startAngleChanged"> </wj-combo-box> </label> <label> sweepAngle <wj-combo-box :itemsSource="sweepAngles" :selectedValue="sweepAngle" :selectedIndexChanged="sweepAngleChanged"> </wj-combo-box> </label> <label> tickSpacing <wj-combo-box :itemsSource="tickSpacings" :selectedValue="tickSpacing" :selectedIndexChanged="tickSpacingChanged"> </wj-combo-box> </label> </div> </div> <div class="row"> <div class="col-xs-6" :class="{ vertical: isVertical }"> <wj-linear-gauge :min="0" :max="500" :value="100" :showTicks="true" :direction="direction" :tickSpacing="tickSpacing" :isReadOnly="false" :refreshed="updateTicks"> </wj-linear-gauge> </div> <div class="col-xs-6"> <label> direction <wj-combo-box :itemsSource="directions" :selectedItem="direction" :selectedIndexChanged="directionChanged"> </wj-combo-box> </label> </div> </div> </div> </template> <script> import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import Vue from 'vue'; import { RadialGauge, LinearGauge } from '@grapecity/wijmo.vue2.gauge'; import { ComboBox } from '@grapecity/wijmo.vue2.input'; import { GaugeDirection } from '@grapecity/wijmo.gauge'; import { Globalize, Point, addClass, toggleClass, clamp } from '@grapecity/wijmo'; let App = Vue.extend({ name: 'app', data: function() { // arrays used to populate combo boxes let startAngles = [-90, -45, 0, 45, 90]; let sweepAngles = [-360, -270, -180, -90, 90, 180, 270, 360]; let tickSpacings = [25, 50, 100, 200, 250]; let directions = ['Left', 'Right', 'Up', 'Down']; return { startAngles: startAngles, sweepAngles: sweepAngles, tickSpacings: tickSpacings, directions: directions, // app state startAngle: startAngles[1], sweepAngle: sweepAngles[6], tickSpacing: tickSpacings[1], direction: GaugeDirection.Right, isVertical: false } }, methods: { startAngleChanged: function(s, e) { this.startAngle = s.selectedItem; }, sweepAngleChanged: function(s, e) { this.sweepAngle = s.selectedItem; }, tickSpacingChanged: function(s, e) { this.tickSpacing = s.selectedItem; }, directionChanged: function(s, e) { this.direction = GaugeDirection[s.selectedItem]; this.isVertical = GaugeDirection[this.direction].match(/Up|Down/) != null; }, updateTicks: function(gauge) { let tickText = gauge.hostElement.querySelector('.tick-text'); if (!tickText) { let svg = gauge.hostElement.querySelector('svg'); tickText = document.createElementNS('http://www.w3.org/2000/svg', 'g'); addClass(tickText, 'tick-text'); svg.appendChild(tickText); } // clear tickText element while (tickText.firstChild) { tickText.removeChild(tickText.firstChild); } // update tickText parameters if (gauge.showTicks) { let step = gauge.tickSpacing || gauge.step; if (step) { if ('startAngle' in gauge) { this.updateTicksRadial(gauge, tickText, step); } else { this.updateTicksLinear(gauge, tickText, step); } } } }, updateTicksRadial: function(gauge, tickText, step) { let rc = gauge.faceBounds, cx = rc.width / 2, cy = rc.height / 2, r = Math.min(rc.width, rc.height) / 2 * 1.15; for (let val = gauge.min; val <= gauge.max; val += step) { let pct = (gauge.max > gauge.min) ? (val - gauge.min) / (gauge.max - gauge.min) : 0, angle = (gauge.startAngle + 180 + gauge.sweepAngle * clamp(pct, 0, 1)) * Math.PI / 180; let te = document.createElementNS('http://www.w3.org/2000/svg', 'text'); te.setAttribute('x', (cx + r * Math.cos(angle)).toString()); te.setAttribute('y', (cy + r * Math.sin(angle)).toString()); te.textContent = Globalize.format(val, gauge.format); tickText.appendChild(te); } }, updateTicksLinear: function(gauge, tickText, step) { let rc = gauge.faceBounds; let pt = new Point(); pt.x = rc.left - rc.width * 0.75; pt.y = rc.top - rc.height * 0.75; for (let val = gauge.min; val <= gauge.max; val += step) { let pct = (gauge.max > gauge.min) ? (val - gauge.min) / (gauge.max - gauge.min) : 0; switch (gauge.direction) { case GaugeDirection.Left: pt.x = rc.right - rc.width * pct; break; case GaugeDirection.Right: pt.x = rc.left + rc.width * pct; break; case GaugeDirection.Up: pt.y = rc.bottom - rc.height * pct; break; case GaugeDirection.Down: pt.y = rc.top + rc.height * pct; break; } let te = document.createElementNS('http://www.w3.org/2000/svg', 'text'); te.setAttribute('x', pt.x.toString()); te.setAttribute('y', pt.y.toString()); te.textContent = Globalize.format(val, gauge.format); tickText.appendChild(te); } } } }); new Vue({ render: h => h(App) }).$mount('#app'); </script> <style> label { display: block; text-align: right; width: 300px; } label .wj-combobox { width: 120px; } .wj-gauge { margin: 4em auto; } .wj-gauge .tick-text text { text-anchor: middle; dominant-baseline: middle; /* center-aligns CSS text vertically! */ } .wj-radialgauge { width: 200px; } .wj-radialgauge .tick-text text { font-size: 50%; } .wj-lineargauge { width: 400px; } .vertical .wj-lineargauge { height: 300px; width: 2em; } .wj-lineargauge .tick-text text { font-size: 75%; } body { font-size: 16px; margin-bottom: 48pt; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Tickmark Text</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app.vue'); </script> </head> <body> <div id="app"></div> </body> </html> import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './app.css'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { RadialGauge, LinearGauge } from '@grapecity/wijmo.react.gauge'; import { ComboBox } from '@grapecity/wijmo.react.input'; import { GaugeDirection } from '@grapecity/wijmo.gauge'; import { Point, Globalize, addClass, clamp } from '@grapecity/wijmo'; class App extends React.Component { // initialize state constructor(props) { super(props); // arrays used to populate combo boxes this.startAngles = [-90, -45, 0, 45, 90]; this.sweepAngles = [-360, -270, -180, -90, 90, 180, 270, 360]; this.tickSpacings = [25, 50, 100, 200, 250]; this.directions = ['Left', 'Right', 'Up', 'Down']; this.state = { startAngle: this.startAngles[1], sweepAngle: this.sweepAngles[6], tickSpacing: this.tickSpacings[1], direction: GaugeDirection.Right }; } render() { return (<div className="container-fluid"> <div className="row"> <div className="col-xs-6"> <RadialGauge style={{ height: '200px' }} min={0} max={500} value={100} showTicks={true} showText={'Value'} isReadOnly={false} startAngle={this.state.startAngle} sweepAngle={this.state.sweepAngle} tickSpacing={this.state.tickSpacing} refreshed={sender => { this.updateTicks(sender); }}> </RadialGauge> </div> <div className="col-xs-6"> <label> startAngle{' '} <ComboBox itemsSource={this.startAngles} selectedItem={this.state.startAngle} selectedIndexChanged={sender => { this.setState({ startAngle: sender.selectedItem }); }}> </ComboBox> </label> <label> sweepAngle{' '} <ComboBox itemsSource={this.sweepAngles} selectedItem={this.state.sweepAngle} selectedIndexChanged={sender => { this.setState({ sweepAngle: sender.selectedItem }); }}> </ComboBox> </label> <label> tickSpacing{' '} <ComboBox itemsSource={this.tickSpacings} selectedItem={this.state.tickSpacing} selectedIndexChanged={sender => { this.setState({ tickSpacing: sender.selectedItem }); }}> </ComboBox> </label> </div> </div> <div className="row"> <div className={'col-xs-6' + (this.isVertical() ? ' vertical' : '')}> <LinearGauge min={0} max={500} value={100} showTicks={true} isReadOnly={false} direction={this.state.direction} tickSpacing={this.state.tickSpacing} refreshed={(sender) => { this.updateTicks(sender); }}> </LinearGauge> </div> <div className="col-xs-6"> <label> direction{' '} <ComboBox itemsSource={this.directions} selectedItem={GaugeDirection[this.state.direction]} selectedIndexChanged={sender => { this.setState({ direction: GaugeDirection[sender.selectedItem] }); }}> </ComboBox> </label> </div> </div> </div>); } // used to style the linear gauge isVertical() { return GaugeDirection[this.state.direction].match(/Up|Down/) != null; } // update the tickmark text for radial or linear gauges updateTicks(gauge) { // get tickText element let tickText = gauge.hostElement.querySelector('.tick-text'); if (!tickText) { let svg = gauge.hostElement.querySelector('svg'); tickText = document.createElementNS('http://www.w3.org/2000/svg', 'g'); addClass(tickText, 'tick-text'); svg.appendChild(tickText); } // clear tickText element while (tickText.firstChild) { tickText.removeChild(tickText.firstChild); } // update tickText parameters if (gauge.showTicks) { let step = gauge.tickSpacing || gauge.step; if (step) { if ('startAngle' in gauge) { this.updateTicksRadial(gauge, tickText, step); } else { this.updateTicksLinear(gauge, tickText, step); } } } } // update tickmark text for radial gauges updateTicksRadial(gauge, tickText, step) { let rc = gauge.faceBounds, cx = rc.width / 2, cy = rc.height / 2, r = Math.min(rc.width, rc.height) / 2 * 1.15; for (let val = gauge.min; val <= gauge.max; val += step) { let pct = (gauge.max > gauge.min) ? (val - gauge.min) / (gauge.max - gauge.min) : 0, angle = (gauge.startAngle + 180 + gauge.sweepAngle * clamp(pct, 0, 1)) * Math.PI / 180; let te = document.createElementNS('http://www.w3.org/2000/svg', 'text'); te.setAttribute('x', (cx + r * Math.cos(angle)).toString()); te.setAttribute('y', (cy + r * Math.sin(angle)).toString()); te.textContent = Globalize.format(val, gauge.format); tickText.appendChild(te); } } // update tickmark text for linear gauges updateTicksLinear(gauge, tickText, step) { let rc = gauge.faceBounds; let pt = new Point(); pt.x = rc.left - rc.width * 0.75; pt.y = rc.top - rc.height * 0.75; for (let val = gauge.min; val <= gauge.max; val += step) { let pct = (gauge.max > gauge.min) ? (val - gauge.min) / (gauge.max - gauge.min) : 0; switch (gauge.direction) { case GaugeDirection.Left: pt.x = rc.right - rc.width * pct; break; case GaugeDirection.Right: pt.x = rc.left + rc.width * pct; break; case GaugeDirection.Up: pt.y = rc.bottom - rc.height * pct; break; case GaugeDirection.Down: pt.y = rc.top + rc.height * pct; break; } let te = document.createElementNS('http://www.w3.org/2000/svg', 'text'); te.setAttribute('x', pt.x.toString()); te.setAttribute('y', pt.y.toString()); te.textContent = Globalize.format(val, gauge.format); tickText.appendChild(te); } } } ReactDOM.render(<App />, document.getElementById('app')); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Tickmark Text</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html> label { display: block; text-align: right; width: 300px; } label .wj-combobox { width: 120px; } .wj-gauge { margin: 4em auto; } .wj-gauge .tick-text text { text-anchor: middle; dominant-baseline: middle; /* center-aligns CSS text vertically! */ } .wj-radialgauge { width: 200px; } .wj-radialgauge .tick-text text { font-size: 50%; } .wj-lineargauge { width: 400px; } .vertical .wj-lineargauge { height: 300px; width: 2em; } .wj-lineargauge .tick-text text { font-size: 75%; } body { font-size: 16px; margin-bottom: 48pt; }