devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
435 lines (433 loc) • 16.2 kB
JavaScript
/**
* DevExtreme (esm/viz/gauges/circular_indicators.js)
* Version: 24.2.6
* Build date: Mon Mar 17 2025
*
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import {
BaseIndicator,
BaseTextCloudMarker,
BaseRangeBar
} from "./base_indicators";
import {
getCosAndSin,
convertAngleToRendererSpace,
normalizeAngle
} from "../core/utils";
const _Number = Number;
const _getCosAndSin = getCosAndSin;
const _convertAngleToRendererSpace = convertAngleToRendererSpace;
function correctRadius(layout, size) {
if (layout && layout.radius - size <= 0) {
layout.radius = size + 1
}
return layout
}
const SimpleIndicator = BaseIndicator.inherit({
_move: function() {
const options = this._options;
const angle = _convertAngleToRendererSpace(this._actualPosition);
this._rootElement.rotate(angle, options.x, options.y);
this._trackerElement && this._trackerElement.rotate(angle, options.x, options.y)
},
_isEnabled: function() {
return this._options.width > 0
},
_isVisible: function(layout) {
return layout.radius - _Number(this._options.indentFromCenter) > 0
},
_getTrackerSettings: function() {
const options = this._options;
const radius = this._getRadius();
const indentFromCenter = this._getIndentFromCenter();
const x = options.x;
const y = options.y - (radius + indentFromCenter) / 2;
let width = options.width / 2;
let length = (radius - indentFromCenter) / 2;
width > 10 || (width = 10);
length > 10 || (length = 10);
return {
points: [x - width, y - length, x - width, y + length, x + width, y + length, x + width, y - length]
}
},
_render: function() {
this._renderPointer()
},
_clearPointer: function() {
delete this._element
},
_clear: function() {
this._clearPointer()
},
_getIndentFromCenter: function(radius) {
return Number(this._options.indentFromCenter) || 0
},
_getRadius: function() {
return 0
},
measure: function(layout) {
const result = {
max: layout.radius
};
if (this._options.indentFromCenter < 0) {
result.inverseHorizontalOffset = result.inverseVerticalOffset = -_Number(this._options.indentFromCenter)
}
return result
},
getTooltipParameters: function() {
const options = this._options;
const cosSin = _getCosAndSin(this._actualPosition);
const r = (this._getRadius() + this._getIndentFromCenter()) / 2;
return {
x: options.x + cosSin.cos * r,
y: options.y - cosSin.sin * r,
value: this._currentValue,
color: options.color,
offset: options.width / 2
}
}
});
const NeedleIndicator = SimpleIndicator.inherit({
_isVisible: function(layout) {
const indentFromCenter = this._adjustOffset(Number(this._options.indentFromCenter), layout.radius);
const offset = this._adjustOffset(Number(this._options.offset), layout.radius);
return layout.radius - indentFromCenter - offset > 0
},
getOffset: function() {
return 0
},
_adjustOffset: function(value, radius) {
const minRadius = Number(this._options.beginAdaptingAtRadius);
const diff = radius / minRadius;
if (diff < 1) {
value = Math.floor(value * diff)
}
return value || 0
},
_getIndentFromCenter: function(radius) {
return this._adjustOffset(Number(this._options.indentFromCenter), this._options.radius)
},
_getRadius: function() {
const options = this._options;
return options.radius - this._adjustOffset(Number(options.offset), options.radius)
},
_renderSpindle: function() {
const that = this;
const options = that._options;
const radius = options.radius;
const spindleSize = 2 * this._adjustOffset(_Number(options.spindleSize) / 2, radius);
let gapSize = 2 * this._adjustOffset(_Number(options.spindleGapSize) / 2, radius) || 0;
if (gapSize > 0) {
gapSize = gapSize <= spindleSize ? gapSize : spindleSize
}
if (spindleSize > 0) {
that._spindleOuter = that._spindleOuter || that._renderer.circle().append(that._rootElement);
that._spindleInner = that._spindleInner || that._renderer.circle().append(that._rootElement);
that._spindleOuter.attr({
class: "dxg-spindle-border",
cx: options.x,
cy: options.y,
r: spindleSize / 2
});
that._spindleInner.attr({
class: "dxg-spindle-hole",
cx: options.x,
cy: options.y,
r: gapSize / 2,
fill: options.containerBackgroundColor
})
}
},
_render: function() {
this.callBase();
this._renderSpindle()
},
_clear: function() {
this.callBase();
delete this._spindleOuter;
delete this._spindleInner
}
});
const rectangleNeedle = NeedleIndicator.inherit({
_renderPointer: function() {
const options = this._options;
const y2 = options.y - this._getRadius();
const y1 = options.y - this._getIndentFromCenter();
const x1 = options.x - options.width / 2;
const x2 = x1 + _Number(options.width);
this._element = this._element || this._renderer.path([], "area").append(this._rootElement);
this._element.attr({
points: [x1, y1, x1, y2, x2, y2, x2, y1]
})
}
});
const triangleNeedle = NeedleIndicator.inherit({
_renderPointer: function() {
const options = this._options;
const y2 = options.y - this._getRadius();
const y1 = options.y - this._getIndentFromCenter();
const x1 = options.x - options.width / 2;
const x2 = options.x + options.width / 2;
this._element = this._element || this._renderer.path([], "area").append(this._rootElement);
this._element.attr({
points: [x1, y1, options.x, y2, x2, y1]
})
}
});
const twoColorNeedle = NeedleIndicator.inherit({
_renderPointer: function() {
const options = this._options;
const x1 = options.x - options.width / 2;
const x2 = options.x + options.width / 2;
const y4 = options.y - this._getRadius();
const y1 = options.y - this._getIndentFromCenter();
const fraction = _Number(options.secondFraction) || 0;
let y2;
let y3;
if (fraction >= 1) {
y2 = y3 = y1
} else if (fraction <= 0) {
y2 = y3 = y4
} else {
y3 = y4 + (y1 - y4) * fraction;
y2 = y3 + _Number(options.space)
}
this._firstElement = this._firstElement || this._renderer.path([], "area").append(this._rootElement);
this._spaceElement = this._spaceElement || this._renderer.path([], "area").append(this._rootElement);
this._secondElement = this._secondElement || this._renderer.path([], "area").append(this._rootElement);
this._firstElement.attr({
points: [x1, y1, x1, y2, x2, y2, x2, y1]
});
this._spaceElement.attr({
points: [x1, y2, x1, y3, x2, y3, x2, y2],
class: "dxg-hole",
fill: options.containerBackgroundColor
});
this._secondElement.attr({
points: [x1, y3, x1, y4, x2, y4, x2, y3],
class: "dxg-part",
fill: options.secondColor
})
},
_clearPointer: function() {
delete this._firstElement;
delete this._secondElement;
delete this._spaceElement
}
});
const triangleMarker = SimpleIndicator.inherit({
_isEnabled: function() {
return this._options.length > 0 && this._options.width > 0
},
_isVisible: layout => true,
resize(layout) {
return this.callBase(correctRadius(layout, 0))
},
_render: function() {
const options = this._options;
const x = options.x;
const y1 = options.y - options.radius;
const dx = options.width / 2 || 0;
const y2 = y1 - _Number(options.length);
this._element = this._element || this._renderer.path([], "area").append(this._rootElement);
const settings = {
points: [x, y1, x - dx, y2, x + dx, y2],
stroke: "none",
"stroke-width": 0,
"stroke-linecap": "square"
};
if (options.space > 0) {
settings["stroke-width"] = Math.min(options.space, options.width / 4) || 0;
settings.stroke = settings["stroke-width"] > 0 ? options.containerBackgroundColor || "none" : "none"
}
this._element.attr(settings).sharp()
},
_clear: function() {
delete this._element
},
_getTrackerSettings: function() {
const options = this._options;
const x = options.x;
const y = options.y - options.radius - options.length / 2;
let width = options.width / 2;
let length = options.length / 2;
width > 10 || (width = 10);
length > 10 || (length = 10);
return {
points: [x - width, y - length, x - width, y + length, x + width, y + length, x + width, y - length]
}
},
measure: function(layout) {
return {
min: layout.radius,
max: layout.radius + _Number(this._options.length)
}
},
getTooltipParameters: function() {
const options = this._options;
const cosSin = _getCosAndSin(this._actualPosition);
const r = options.radius + options.length / 2;
const parameters = this.callBase();
parameters.x = options.x + cosSin.cos * r;
parameters.y = options.y - cosSin.sin * r;
parameters.offset = options.length / 2;
return parameters
}
});
const textCloud = BaseTextCloudMarker.inherit({
_isEnabled: function() {
return true
},
_isVisible: layout => true,
resize(layout) {
return this.callBase(correctRadius(layout, 0))
},
_getTextCloudOptions: function() {
const cosSin = _getCosAndSin(this._actualPosition);
const nAngle = normalizeAngle(this._actualPosition);
return {
x: this._options.x + cosSin.cos * this._options.radius,
y: this._options.y - cosSin.sin * this._options.radius,
type: nAngle > 270 ? "left-top" : nAngle > 180 ? "top-right" : nAngle > 90 ? "right-bottom" : "bottom-left"
}
},
measure: function(layout) {
const arrowLength = _Number(this._options.arrowLength) || 0;
this._measureText();
const verticalOffset = this._textFullHeight + arrowLength;
const horizontalOffset = this._textFullWidth + arrowLength;
return {
min: layout.radius,
max: layout.radius,
horizontalOffset: horizontalOffset,
verticalOffset: verticalOffset,
inverseHorizontalOffset: horizontalOffset,
inverseVerticalOffset: verticalOffset
}
}
});
const rangeBar = BaseRangeBar.inherit({
_isEnabled: function() {
return this._options.size > 0
},
_isVisible: layout => true,
resize(layout) {
return this.callBase(correctRadius(layout, _Number(this._options.size)))
},
_createBarItem: function() {
return this._renderer.arc().attr({
"stroke-linejoin": "round"
}).append(this._rootElement)
},
_createTracker: function() {
return this._renderer.arc().attr({
"stroke-linejoin": "round"
})
},
_setBarSides: function() {
this._maxSide = this._options.radius;
this._minSide = this._maxSide - _Number(this._options.size)
},
_getSpace: function() {
const options = this._options;
return options.space > 0 ? 180 * options.space / options.radius / Math.PI : 0
},
_isTextVisible: function() {
const options = this._options.text || {};
return options.indent > 0
},
_setTextItemsSides: function() {
const options = this._options;
const indent = _Number(options.text.indent);
this._lineFrom = options.y - options.radius;
this._lineTo = this._lineFrom - indent;
this._textRadius = options.radius + indent
},
_getPositions: function() {
const basePosition = this._basePosition;
const actualPosition = this._actualPosition;
let mainPosition1;
let mainPosition2;
if (basePosition >= actualPosition) {
mainPosition1 = basePosition;
mainPosition2 = actualPosition
} else {
mainPosition1 = actualPosition;
mainPosition2 = basePosition
}
return {
start: this._startPosition,
end: this._endPosition,
main1: mainPosition1,
main2: mainPosition2,
back1: Math.min(mainPosition1 + this._space, this._startPosition),
back2: Math.max(mainPosition2 - this._space, this._endPosition)
}
},
_buildItemSettings: function(from, to) {
return {
x: this._options.x,
y: this._options.y,
innerRadius: this._minSide,
outerRadius: this._maxSide,
startAngle: to,
endAngle: from
}
},
_updateTextPosition: function() {
const cosSin = _getCosAndSin(this._actualPosition);
let x = this._options.x + this._textRadius * cosSin.cos;
let y = this._options.y - this._textRadius * cosSin.sin;
x += cosSin.cos * this._textWidth * .6;
y -= cosSin.sin * this._textHeight * .6;
this._text.attr({
x: x,
y: y + this._textVerticalOffset
})
},
_updateLinePosition: function() {
const x = this._options.x;
let x1;
let x2;
if (this._basePosition > this._actualPosition) {
x1 = x - 2;
x2 = x
} else if (this._basePosition < this._actualPosition) {
x1 = x;
x2 = x + 2
} else {
x1 = x - 1;
x2 = x + 1
}
this._line.attr({
points: [x1, this._lineFrom, x1, this._lineTo, x2, this._lineTo, x2, this._lineFrom]
}).rotate(_convertAngleToRendererSpace(this._actualPosition), x, this._options.y).sharp()
},
_getTooltipPosition: function() {
const cosSin = _getCosAndSin((this._basePosition + this._actualPosition) / 2);
const r = (this._minSide + this._maxSide) / 2;
return {
x: this._options.x + cosSin.cos * r,
y: this._options.y - cosSin.sin * r
}
},
measure: function(layout) {
const that = this;
const result = {
min: layout.radius - _Number(that._options.size),
max: layout.radius
};
that._measureText();
if (that._hasText) {
result.max += _Number(that._options.text.indent);
result.horizontalOffset = that._textWidth;
result.verticalOffset = that._textHeight
}
return result
}
});
export {
rectangleNeedle as _default, rectangleNeedle as rectangleneedle, triangleNeedle as triangleneedle, twoColorNeedle as twocolorneedle, triangleMarker as trianglemarker, textCloud as textcloud, rangeBar as rangebar
};