You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
9.3 KiB
JavaScript
265 lines
9.3 KiB
JavaScript
class GaugeWidget extends BaseWidget {
|
|
static OUT_OF_RANGE_COLOR = '#8e1414';
|
|
|
|
$el;
|
|
#redraw;
|
|
#cstyle;
|
|
|
|
constructor(data, renderer) {
|
|
super(data, renderer);
|
|
this.perc = null;
|
|
this.value = 0;
|
|
this.min = 0;
|
|
this.max = 100;
|
|
this.dec = 0;
|
|
this.unit = '';
|
|
this.icon = '';
|
|
this.tout = null;
|
|
|
|
switch (this.type) {
|
|
case 'gauge': this.#redraw = this.#redrawGauge; break;
|
|
case 'gauge_l': this.#redraw = this.#redrawGaugeL; break;
|
|
case 'gauge_r': this.#redraw = this.#redrawGaugeR; break;
|
|
}
|
|
|
|
this.makeLayout({
|
|
type: 'canvas',
|
|
name: 'el',
|
|
events: {
|
|
resize: () => this.#redraw()
|
|
}
|
|
});
|
|
this.#cstyle = window.getComputedStyle(this.$el);
|
|
|
|
this.update(data);
|
|
wait2Frame().then(() => this.#redraw());
|
|
}
|
|
|
|
close() {
|
|
if (this.tout) clearTimeout(this.tout);
|
|
this.tout = null;
|
|
}
|
|
|
|
update(data) {
|
|
super.update(data);
|
|
if ('value' in data) this.value = Number(data.value);
|
|
if ('min' in data) this.min = Number(data.min);
|
|
if ('max' in data) this.max = Number(data.max);
|
|
if ('dec' in data) this.dec = Number(data.dec);
|
|
if ('unit' in data) this.unit = data.unit;
|
|
if ('icon' in data) this.icon = data.icon;
|
|
this.#redraw();
|
|
}
|
|
|
|
#redrawGauge() {
|
|
this.color = intToCol(this.data.color) ?? this.#cstyle.getPropertyValue('--prim');
|
|
let cv = this.$el;
|
|
let rw = cv.parentNode.clientWidth;
|
|
if (!rw) return;
|
|
|
|
const ratio = window.devicePixelRatio;
|
|
let rh = Math.floor(rw * 0.47);
|
|
cv.style.width = rw + 'px';
|
|
cv.style.height = rh + 'px';
|
|
cv.width = Math.floor(rw * ratio);
|
|
cv.height = Math.floor(rh * ratio);
|
|
|
|
let cx = cv.getContext("2d");
|
|
let perc = (this.value - this.min) * 100 / (this.max - this.min);
|
|
if (perc < 0) perc = 0;
|
|
if (perc > 100) perc = 100;
|
|
if (this.perc == null) this.perc = perc;
|
|
else {
|
|
if (Math.abs(this.perc - perc) <= 0.15) this.perc = perc;
|
|
else this.perc += (perc - this.perc) * 0.15;
|
|
if (this.perc != perc) setTimeout(() => this.#redraw(), 20);
|
|
}
|
|
|
|
cx.clearRect(0, 0, cv.width, cv.height);
|
|
cx.lineWidth = cv.width / 8;
|
|
cx.strokeStyle = this.#cstyle.getPropertyValue('--dark');
|
|
cx.beginPath();
|
|
cx.arc(cv.width / 2, cv.height * 0.97, cv.width / 2 - cx.lineWidth, Math.PI * (1 + this.perc / 100), Math.PI * 2);
|
|
cx.stroke();
|
|
|
|
cx.strokeStyle = this.color;
|
|
cx.beginPath();
|
|
cx.arc(cv.width / 2, cv.height * 0.97, cv.width / 2 - cx.lineWidth, Math.PI, Math.PI * (1 + this.perc / 100));
|
|
cx.stroke();
|
|
|
|
let font = this.#cstyle.fontFamily;
|
|
|
|
cx.fillStyle = this.color;
|
|
cx.font = '10px ' + font;
|
|
cx.textAlign = "center";
|
|
|
|
let text = this.unit;
|
|
let len = Math.max(
|
|
(this.value.toFixed(this.dec) + text).length,
|
|
(this.min.toFixed(this.dec) + text).length,
|
|
(this.max.toFixed(this.dec) + text).length
|
|
);
|
|
if (len == 1) text += ' ';
|
|
else if (len == 2) text += ' ';
|
|
|
|
let w = Math.max(
|
|
cx.measureText(this.value.toFixed(this.dec) + text).width,
|
|
cx.measureText(this.min.toFixed(this.dec) + text).width,
|
|
cx.measureText(this.max.toFixed(this.dec) + text).width
|
|
);
|
|
|
|
if (this.value > this.max || this.value < this.min) cx.fillStyle = GaugeWidget.OUT_OF_RANGE_COLOR;
|
|
else cx.fillStyle = this.#cstyle.getPropertyValue('--font2');
|
|
cx.font = cv.width * 0.43 * 10 / w + 'px ' + font;
|
|
cx.fillText(this.value.toFixed(this.dec) + this.unit, cv.width / 2, cv.height * 0.93);
|
|
|
|
cx.font = '10px ' + font;
|
|
w = Math.max(
|
|
cx.measureText(Math.round(this.min)).width,
|
|
cx.measureText(Math.round(this.max)).width
|
|
);
|
|
cx.fillStyle = this.#cstyle.getPropertyValue('--font');
|
|
cx.font = cx.lineWidth * 0.55 * 10 / w + 'px ' + font;
|
|
cx.fillText(this.min, cx.lineWidth, cv.height * 0.92);
|
|
cx.fillText(this.max, cv.width - cx.lineWidth, cv.height * 0.92);
|
|
}
|
|
|
|
#redrawGaugeR() {
|
|
this.color = intToCol(this.data.color) ?? this.#cstyle.getPropertyValue('--prim');
|
|
let cv = this.$el;
|
|
let rw = cv.parentNode.clientWidth;
|
|
if (!rw) return;
|
|
|
|
const ratio = window.devicePixelRatio;
|
|
cv.style.width = rw + 'px';
|
|
cv.style.height = cv.style.width;
|
|
cv.width = Math.floor(rw * ratio);
|
|
cv.height = cv.width;
|
|
|
|
let cx = cv.getContext("2d");
|
|
let perc = (this.value - this.min) * 100 / (this.max - this.min);
|
|
if (perc < 0) perc = 0;
|
|
if (perc > 100) perc = 100;
|
|
if (this.perc == null) this.perc = perc;
|
|
else {
|
|
if (Math.abs(this.perc - perc) <= 0.15) this.perc = perc;
|
|
else this.perc += (perc - this.perc) * 0.15;
|
|
if (this.perc != perc) setTimeout(() => this.#redraw(), 20);
|
|
}
|
|
|
|
let joint = Math.PI * (0.5 + 2 * (this.perc / 100));
|
|
|
|
cx.clearRect(0, 0, cv.width, cv.height);
|
|
cx.lineWidth = cv.width / 8;
|
|
cx.strokeStyle = this.#cstyle.getPropertyValue('--dark');
|
|
cx.beginPath();
|
|
cx.arc(cv.width / 2, cv.height / 2, cv.width / 2 - cx.lineWidth, joint, Math.PI * 2.5);
|
|
cx.stroke();
|
|
|
|
cx.strokeStyle = this.color;
|
|
cx.beginPath();
|
|
cx.arc(cv.width / 2, cv.height / 2, cv.width / 2 - cx.lineWidth, Math.PI / 2, joint);
|
|
cx.stroke();
|
|
|
|
let font = this.#cstyle.fontFamily;
|
|
|
|
cx.fillStyle = this.color;
|
|
cx.font = '10px ' + font;
|
|
cx.textAlign = "center";
|
|
cx.textBaseline = "middle";
|
|
|
|
let text = this.unit;
|
|
let len = Math.max(
|
|
(this.value.toFixed(this.dec) + text).length,
|
|
(this.min.toFixed(this.dec) + text).length,
|
|
(this.max.toFixed(this.dec) + text).length
|
|
);
|
|
if (len == 1) text += ' ';
|
|
else if (len == 2) text += ' ';
|
|
|
|
let w = Math.max(
|
|
cx.measureText(this.value.toFixed(this.dec) + text).width,
|
|
cx.measureText(this.min.toFixed(this.dec) + text).width,
|
|
cx.measureText(this.max.toFixed(this.dec) + text).width
|
|
);
|
|
|
|
if (this.value > this.max || this.value < this.min) cx.fillStyle = GaugeWidget.OUT_OF_RANGE_COLOR;
|
|
else cx.fillStyle = this.#cstyle.getPropertyValue('--font2');
|
|
cx.font = cv.width * 0.5 * 10 / w + 'px ' + font;
|
|
cx.fillText(this.value.toFixed(this.dec) + this.unit, cv.width / 2, cv.height * 0.52);
|
|
}
|
|
|
|
#redrawGaugeL() {
|
|
this.color = intToCol(this.data.color) ?? this.#cstyle.getPropertyValue('--prim');
|
|
let cv = this.$el;
|
|
let rw = cv.parentNode.clientWidth;
|
|
if (!rw) return;
|
|
|
|
const ratio = window.devicePixelRatio;
|
|
let height = 30;
|
|
let r = ratio;
|
|
let sw = 2 * r;
|
|
let off = 5 * r;
|
|
|
|
cv.style.width = rw + 'px';
|
|
cv.style.height = height + 'px';
|
|
cv.width = Math.floor(rw * r);
|
|
cv.height = Math.floor(height * r);
|
|
|
|
const cx = cv.getContext("2d");
|
|
let perc = (this.value - this.min) * 100 / (this.max - this.min);
|
|
if (perc < 0) perc = 0;
|
|
if (perc > 100) perc = 100;
|
|
if (this.perc == null) this.perc = perc;
|
|
else {
|
|
if (Math.abs(this.perc - perc) <= 0.15) this.perc = perc;
|
|
else this.perc += (perc - this.perc) * 0.15;
|
|
if (this.perc != perc) setTimeout(() => this.#redraw(), 20);
|
|
}
|
|
|
|
let wid = cv.width - sw - off * 2;
|
|
|
|
cx.clearRect(0, 0, cv.width, cv.height);
|
|
cx.fillStyle = this.#cstyle.getPropertyValue('--back');
|
|
cx.beginPath();
|
|
cx.roundRect(off + sw / 2, sw / 2, wid, cv.height - sw, 5 * r);
|
|
cx.fill();
|
|
|
|
cx.fillStyle = this.color;
|
|
cx.beginPath();
|
|
cx.roundRect(off + sw / 2, sw / 2, wid * this.perc / 100, cv.height - sw, 5 * r);
|
|
cx.fill();
|
|
|
|
if (this.value > this.max || this.value < this.min) cx.fillStyle = GaugeWidget.OUT_OF_RANGE_COLOR;
|
|
else cx.fillStyle = this.#cstyle.getPropertyValue('--font');
|
|
|
|
let font = this.#cstyle.fontFamily;
|
|
|
|
cx.font = (19 * r) + 'px ' + font;
|
|
cx.textAlign = "center";
|
|
cx.textBaseline = "middle";
|
|
|
|
let txt = this.value.toFixed(this.dec) + this.unit;
|
|
cx.fillText(txt, cv.width / 2, cv.height * 0.52);
|
|
|
|
if (this.icon) {
|
|
let tw = cx.measureText(txt).width;
|
|
cx.font = (20 * r) + 'px FA5';
|
|
cx.textAlign = "right";
|
|
cx.fillText(getIcon(this.icon), cv.width / 2 - tw / 2 - off, cv.height * 0.52);
|
|
}
|
|
|
|
cx.fillStyle = this.#cstyle.getPropertyValue('--font');
|
|
cx.font = (12 * r) + 'px ' + font;
|
|
cx.textAlign = "left";
|
|
cx.fillText(this.min.toFixed(this.dec), off + sw / 2 + off, cv.height * 0.52);
|
|
|
|
cx.textAlign = "right";
|
|
cx.fillText(this.max.toFixed(this.dec), cv.width - (off + sw / 2 + off), cv.height * 0.52);
|
|
}
|
|
}
|
|
|
|
Renderer.register('gauge', GaugeWidget);
|
|
Renderer.register('gauge_l', GaugeWidget);
|
|
Renderer.register('gauge_r', GaugeWidget);
|