You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
2.5 KiB
102 lines
2.5 KiB
import m from 'mithril'; |
|
import { NumericValue } from 'components/widgets/numeric-value'; |
|
import { Pipe } from 'utilities/pipe'; |
|
|
|
require('./gauge.css'); |
|
|
|
export class Gauge extends NumericValue { |
|
protected bars: SVGGeometryElement[] = []; |
|
|
|
private displayedValue = 0.0; |
|
private targetValue = 0.0; |
|
private valueChangeRate = 0.0; |
|
private litBars = 0; |
|
|
|
private animating = false; |
|
private shuttingDown = false; |
|
|
|
private lastGaugeUpdate = 0.0; |
|
private lastAnimationTick = 0.0; |
|
|
|
constructor(vnode: any) { |
|
super(vnode); |
|
} |
|
|
|
onbeforeremove(vnode: m.Vnode<{}, {}>) { |
|
this.shuttingDown = true; |
|
super.onbeforeremove(vnode); |
|
} |
|
|
|
|
|
protected graphicalRepresentation(vnode: m.Vnode<{}, {}>): m.Children { |
|
return <svg></svg>; |
|
} |
|
|
|
protected onValueChange(value: number) { |
|
let now = Date.now() / 1000.0; |
|
|
|
let ratio = value / (this.maxValue || 1.0); |
|
let numBars = this.bars.length; |
|
let threshold = 0.33 / numBars; |
|
|
|
if(ratio > this.targetValue + threshold || ratio < this.targetValue - threshold) { |
|
let animationTime = this.lastGaugeUpdate == 0.0 ? 0.0001 : Math.max(0.0001, Math.min(0.75, now - this.lastGaugeUpdate)); |
|
|
|
this.valueChangeRate = (ratio - this.displayedValue) / animationTime; |
|
this.targetValue = ratio; |
|
|
|
this.enableAnimation(); |
|
} |
|
|
|
this.lastGaugeUpdate = now; |
|
} |
|
|
|
private enableAnimation() { |
|
if(this.animating) return; |
|
this.animating = true; |
|
this.lastAnimationTick = Date.now() / 1000.0 - 0.016; |
|
requestAnimationFrame(() => this.animate()); |
|
} |
|
|
|
private animate() { |
|
if(this.shuttingDown) return; |
|
|
|
if(this.valueChangeRate == 0.0) { |
|
this.animating = false; |
|
return; |
|
} |
|
|
|
requestAnimationFrame(() => this.animate()); |
|
|
|
let now = Date.now() / 1000.0; |
|
if(now > this.lastAnimationTick + 0.75) { |
|
this.displayedValue = this.targetValue; |
|
this.valueChangeRate = 0.0; |
|
} |
|
else { |
|
let dt = now - this.lastAnimationTick; |
|
if(dt < 0.04) return; // limit framerate to save battery |
|
|
|
this.displayedValue += this.valueChangeRate * dt; |
|
if((this.displayedValue > this.targetValue) == (this.valueChangeRate > 0.0)) { |
|
this.displayedValue = this.targetValue; |
|
this.valueChangeRate = 0.0; |
|
} |
|
} |
|
|
|
this.lastAnimationTick = now; |
|
|
|
let numBars = this.bars.length; |
|
let litBars = Math.round(this.displayedValue * numBars); |
|
if(litBars == this.litBars) |
|
return; |
|
this.litBars = litBars; |
|
|
|
for(let barIdx = 0; barIdx < numBars; ++barIdx) { |
|
let bar = this.bars[barIdx]; |
|
let isLit = barIdx < litBars; |
|
if(bar.classList.contains('lit') != isLit) |
|
bar.classList.toggle('lit', isLit); |
|
} |
|
} |
|
}
|
|
|