@Colin @arturv2000
Made another try to make the scale divisions configurable. See if that does the trick for you.
Code is HERE
<template>
<div class="hn-sng">
<div class="label">{{label}}</div>
<svg ref="hn-gauge" width="100%" height="100%" viewBox="0 0 100 100">
<g>
<path v-for="(item, index) in sectors" :key="index" :ref="'sector-' + index" class="sector"
stroke-width="5" d="M 10 90 A 47.5 47.5 25 1 1 90 90"></path>
</g>
<g>
<path class="tick-minor" :style="{'stroke-dasharray':minor }" pathLength="496" stroke-width="5" d="M 10 90 A 47.5 47.5 25 1 1 90 90" ></path>
<path ref="arc" class="tick-major" :style="{'stroke-dasharray':major }" pathLength="248" stroke-width="5" d="M 10 90 A 47.5 47.5 25 1 1 90 90"></path>
</g>
<g>
<text v-for="(item, index) in numbers" :key="index" class="num" text-anchor="middle" y="-37"
:style="`rotate: ${item.r}deg;`">{{item.n}}</text>
</g>
<g>
<text class="measurement" y="48" x="50%" text-anchor="middle">{{measurement}}</text>
<text class="unit" y="75" x="50%" text-anchor="middle">{{unit}}</text>
<text class="value" y="90" x="50%" text-anchor="middle">{{formattedValue}}</text>
<text v-if="showUpdate" ref="update" class="update" y="100" x="0"
text-anchor="left">{{formattedUpdate}}</text>
</g>
<g ref="o-needle" class="o-needle">
<path d="M 0,0 -1.5,0 -0.15,-40 0.15,-40 1.5,0 z"></path>
<circle cx="0" cy="0" r="3"></circle>
</g>
</svg>
</div>
</template>
<script>
export default{
data(){
return {
//define me here
min:-50,
max:50,
unit:"kg",
label:"Yet another gauge",
measurement:"Plums",
updateLabel:"Last update:",
divisors:{major:10,minor:10},
sectors:[{start:-50,end:0,color:"#63cc25"},{start:10,end:20,color:"orange"},{start:20,end:50,color:"red"}],
showUpdate:true,
//no need to change
value:0,
updateTime:0
}
},
methods:{
getElement: function(name,base){
if(base){
return this.$refs[name]
}
return this.$refs[name][0]
},
validate: function(data){
let ret
if(typeof data !== "number"){
ret = parseFloat(data)
if(isNaN(ret)){
console.log("BAD DATA! gauge id:",this.id,"data:",data)
return null
}
}
else{
ret = data
}
return ret
},
range:function (n, p, r) {
if (n < p.minIn) {
n=p.minIn;
}
if (n > p.maxIn) {
n = p.maxIn;
}
if(r){
return Math.round(((n - p.minIn) / (p.maxIn - p.minIn) * (p.maxOut - p.minOut)) + p.minOut);
}
return ((n - p.minIn) / (p.maxIn - p.minIn) * (p.maxOut - p.minOut)) + p.minOut;
},
generateNumbers:function(min,max){
let nums = []
let t = (max - min) / this.divisors.major;
let i = min;
let ro = -123
let rp = 246 / this.divisors.major
for (let e = 0; e < this.divisors.major + 1; ++e){
nums.push({n: i,r: ro + (e * rp) })
i = parseFloat((i + t).toFixed(2));
}
return nums
},
sectorData:function(full){
let params, pos, span, sec, str
let ret = []
this.sectors.forEach((sector,idx) => {
sec = {name:'sector-'+idx,color:sector.color}
params = {minIn:this.min, maxIn:this.max, minOut:0, maxOut:full}
pos = this.range(sector.start,params,false)
params = {minIn:0, maxIn:this.max-this.min, minOut:0, maxOut:full}
span = this.range(sector.end - sector.start,params,false)
sec.css = "0 "+pos+" "+span+" var(--dash)"
ret.push(sec)
})
return ret
},
rotation:function(v){
const params = {minIn:this.min, maxIn:this.max, minOut:-123, maxOut:123};
return this.range(v,params,false) +'deg'
}
},
watch: {
msg: function(){
if(this.msg.payload != undefined){
const v = this.validate(this.msg.payload)
if(v == null){
return
}
this.value = v
this.updateTime = Date.now()
this.getElement('o-needle',true).style.rotate = this.rotation(this.value)
}
}
},
computed: {
formattedValue: function () {
return this.value.toFixed(1)
},
formattedUpdate:function (){
return this.updateLabel+" "+this.time
},
time:function(){
return new Intl.DateTimeFormat('default', {hour: 'numeric', minute: 'numeric',second: 'numeric'}).format(this.updateTime);
},
numbers:function(){
return this.generateNumbers(this.min,this.max)
},
major:function(){
return "1," + ((246 / this.divisors.major) - 1)
},
minor:function(){
let step = 492 / this.divisors.major / this.divisors.minor
let line = "0," + step + ","
step --
for(let i=0;i<this.divisors.minor-1;++i){
line += "1,"+step+","
}
line = line.slice(0, -1);
return line
},
offset:function(){
return -1
}
},
mounted(){
const dal = this.getElement('arc',true).getTotalLength()
const sec = this.sectorData(dal)
const gauge = this.getElement('hn-gauge',true)
gauge.style.setProperty('--dash',dal)
sec.forEach(s =>{
const sector = this.getElement(s.name,false)
sector.style.setProperty("stroke-dasharray",s.css)
sector.style.setProperty("stroke",s.color)
})
}
}
</script>
<style>
.hn-sng {
position: relative;
}
.hn-sng .label {
position: absolute;
font-size: 1rem;
color: currentColor;
text-align: center;
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.hn-sng .value {
fill: currentColor;
font-weigth: 600;
}
.hn-sng .unit {
fill: currentColor;
font-size: 0.4rem;
}
.hn-sng .measurement {
fill: currentColor;
font-size: 0.5rem;
}
.hn-sng .update {
fill: currentColor;
fill-opacity: 0.6;
font-size: 0.35rem;
}
.hn-sng .num {
transform-origin: center 64%;
transform: translate(50%, 64%);
fill: currentColor;
fill-opacity: 0.6;
font-size: .35rem;
}
.hn-sng .tick-minor {
stroke-dashoffset: -2;
fill: none;
stroke: currentColor;
stroke-opacity: 0.6;
}
.hn-sng .tick-major {
stroke-dashoffset: -1;
fill: none;
stroke: currentColor;
}
.hn-sng .sector {
fill: none;
stroke: transparent;
}
.hn-sng .sector:first-of-type {
stroke-dashoffset:-0.5;
}
.hn-sng .o-needle {
transform-origin: center 64%;
transform: translate(50%, 64%);
transition: rotate .5s;
}
.hn-sng .o-needle path,
.hn-sng .o-needle circle {
fill: currentColor;
}
</style>
