mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-18 07:56:44 +01:00
Gauge improvements (#30368)
* Gauge last improvements * Change needle * Fixup * Feedback comments * Update src/components/ha-gauge.ts --------- Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
This commit is contained in:
@@ -134,6 +134,21 @@ const CONFIGS = [
|
|||||||
entity: sensor.not_working
|
entity: sensor.not_working
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
heading: "Lower minimum",
|
||||||
|
config: `
|
||||||
|
- type: gauge
|
||||||
|
entity: sensor.brightness_high
|
||||||
|
needle: true
|
||||||
|
severity:
|
||||||
|
green: 0
|
||||||
|
yellow: 0.45
|
||||||
|
red: 0.9
|
||||||
|
min: -0.05
|
||||||
|
name: " "
|
||||||
|
max: 1.9
|
||||||
|
unit: GBP/h`,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@customElement("demo-lovelace-gauge-card")
|
@customElement("demo-lovelace-gauge-card")
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { PropertyValues, TemplateResult } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import { css, LitElement, svg } from "lit";
|
import { css, LitElement, svg } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
@@ -54,6 +54,7 @@ export class HaGauge extends LitElement {
|
|||||||
this._angle = getAngle(this.value, this.min, this.max);
|
this._angle = getAngle(this.value, this.min, this.max);
|
||||||
}
|
}
|
||||||
this._segment_label = this._getSegmentLabel();
|
this._segment_label = this._getSegmentLabel();
|
||||||
|
this._rescaleSvg();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +71,7 @@ export class HaGauge extends LitElement {
|
|||||||
}
|
}
|
||||||
this._angle = getAngle(this.value, this.min, this.max);
|
this._angle = getAngle(this.value, this.min, this.max);
|
||||||
this._segment_label = this._getSegmentLabel();
|
this._segment_label = this._getSegmentLabel();
|
||||||
|
this._rescaleSvg();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
@@ -88,87 +90,91 @@ export class HaGauge extends LitElement {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
${
|
${
|
||||||
this.levels
|
this.levels
|
||||||
? [...this.levels]
|
? (() => {
|
||||||
.sort((a, b) => a.level - b.level)
|
const sortedLevels = [...this.levels].sort(
|
||||||
.map((level, i, arr) => {
|
(a, b) => a.level - b.level
|
||||||
const startLevel = i === 0 ? this.min : arr[i].level;
|
);
|
||||||
const endLevel = i + 1 < arr.length ? arr[i + 1].level : this.max;
|
|
||||||
|
|
||||||
const startAngle = getAngle(startLevel, this.min, this.max);
|
if (
|
||||||
const endAngle = getAngle(endLevel, this.min, this.max);
|
sortedLevels.length > 0 &&
|
||||||
const largeArc = endAngle - startAngle > 180 ? 1 : 0;
|
sortedLevels[0].level !== this.min
|
||||||
|
) {
|
||||||
|
sortedLevels.unshift({
|
||||||
|
level: this.min,
|
||||||
|
stroke: "var(--info-color)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const x1 = -arcRadius * Math.cos((startAngle * Math.PI) / 180);
|
return sortedLevels.map((level, i, arr) => {
|
||||||
const y1 = -arcRadius * Math.sin((startAngle * Math.PI) / 180);
|
const startLevel = level.level;
|
||||||
const x2 = -arcRadius * Math.cos((endAngle * Math.PI) / 180);
|
const endLevel =
|
||||||
const y2 = -arcRadius * Math.sin((endAngle * Math.PI) / 180);
|
i + 1 < arr.length ? arr[i + 1].level : this.max;
|
||||||
|
|
||||||
const firstSegment = i === 0;
|
const startAngle = getAngle(startLevel, this.min, this.max);
|
||||||
const lastSegment = i === arr.length - 1;
|
const endAngle = getAngle(endLevel, this.min, this.max);
|
||||||
|
const largeArc = endAngle - startAngle > 180 ? 1 : 0;
|
||||||
|
|
||||||
const paths: TemplateResult[] = [];
|
const x1 =
|
||||||
|
-arcRadius * Math.cos((startAngle * Math.PI) / 180);
|
||||||
|
const y1 =
|
||||||
|
-arcRadius * Math.sin((startAngle * Math.PI) / 180);
|
||||||
|
const x2 = -arcRadius * Math.cos((endAngle * Math.PI) / 180);
|
||||||
|
const y2 = -arcRadius * Math.sin((endAngle * Math.PI) / 180);
|
||||||
|
|
||||||
if (firstSegment) {
|
const isFirst = i === 0;
|
||||||
paths.push(svg`
|
const isLast = i === arr.length - 1;
|
||||||
<path
|
|
||||||
class="level"
|
|
||||||
stroke="${level.stroke}"
|
|
||||||
style="stroke-linecap: round"
|
|
||||||
d="M ${x1} ${y1} A ${arcRadius} ${arcRadius} 0 ${largeArc} 1 ${x2} ${y2}"
|
|
||||||
/>
|
|
||||||
`);
|
|
||||||
} else if (lastSegment) {
|
|
||||||
const offsetAngle = 0.5;
|
|
||||||
const midAngle = endAngle - offsetAngle;
|
|
||||||
const xm = -arcRadius * Math.cos((midAngle * Math.PI) / 180);
|
|
||||||
const ym = -arcRadius * Math.sin((midAngle * Math.PI) / 180);
|
|
||||||
|
|
||||||
paths.push(svg`
|
if (isFirst) {
|
||||||
<path
|
return svg`
|
||||||
class="level"
|
<path
|
||||||
stroke="${level.stroke}"
|
class="level"
|
||||||
style="stroke-linecap: butt"
|
stroke="${level.stroke}"
|
||||||
d="M ${x1} ${y1} A ${arcRadius} ${arcRadius} 0 ${largeArc} 1 ${xm} ${ym}"
|
style="stroke-linecap: butt"
|
||||||
/>
|
d="M ${x1} ${y1} A ${arcRadius} ${arcRadius} 0 ${largeArc} 1 ${x2} ${y2}"
|
||||||
`);
|
/>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
paths.push(svg`
|
if (isLast) {
|
||||||
<path
|
const offsetAngle = 0.5;
|
||||||
class="level"
|
const midAngle = endAngle - offsetAngle;
|
||||||
stroke="${level.stroke}"
|
const xm =
|
||||||
style="stroke-linecap: round"
|
-arcRadius * Math.cos((midAngle * Math.PI) / 180);
|
||||||
d="M ${xm} ${ym} A ${arcRadius} ${arcRadius} 0 0 1 ${x2} ${y2}"
|
const ym =
|
||||||
/>
|
-arcRadius * Math.sin((midAngle * Math.PI) / 180);
|
||||||
`);
|
|
||||||
} else {
|
|
||||||
paths.push(svg`
|
|
||||||
<path
|
|
||||||
class="level"
|
|
||||||
stroke="${level.stroke}"
|
|
||||||
style="stroke-linecap: butt"
|
|
||||||
d="M ${x1} ${y1} A ${arcRadius} ${arcRadius} 0 ${largeArc} 1 ${x2} ${y2}"
|
|
||||||
/>
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return paths;
|
return svg`
|
||||||
})
|
<path class="level" stroke="${level.stroke}" style="stroke-linecap: butt"
|
||||||
: ""
|
d="M ${x1} ${y1} A ${arcRadius} ${arcRadius} 0 ${largeArc} 1 ${xm} ${ym}" />
|
||||||
}
|
<path class="level" stroke="${level.stroke}" style="stroke-linecap: butt"
|
||||||
|
d="M ${xm} ${ym} A ${arcRadius} ${arcRadius} 0 0 1 ${x2} ${y2}" />
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return svg`
|
||||||
|
<path
|
||||||
|
class="level"
|
||||||
|
stroke="${level.stroke}"
|
||||||
|
style="stroke-linecap: butt"
|
||||||
|
d="M ${x1} ${y1} A ${arcRadius} ${arcRadius} 0 ${largeArc} 1 ${x2} ${y2}"
|
||||||
|
></path>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
})()
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
|
||||||
${
|
${
|
||||||
this.needle
|
this.needle
|
||||||
? svg`
|
? svg`
|
||||||
<line
|
<path
|
||||||
class="needle"
|
class="needle"
|
||||||
x1="-35.0"
|
d="M -36,-2 L -44,-1 A 1,1,0,0,0,-44,1 L -36,2 A 2,2,0,0,0,-36,-2 Z"
|
||||||
y1="0"
|
|
||||||
x2="-45.0"
|
style=${styleMap({ transform: `rotate(${this._angle}deg)` })}
|
||||||
y2="0"
|
/>
|
||||||
style=${styleMap({ transform: `rotate(${this._angle}deg)` })}
|
|
||||||
/>
|
|
||||||
`
|
`
|
||||||
: svg`
|
: svg`
|
||||||
<path
|
<path
|
||||||
@@ -179,7 +185,8 @@ export class HaGauge extends LitElement {
|
|||||||
/>
|
/>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
</svg>
|
||||||
|
<svg class="text">
|
||||||
<text
|
<text
|
||||||
class="value-text"
|
class="value-text"
|
||||||
x="0"
|
x="0"
|
||||||
@@ -204,6 +211,18 @@ export class HaGauge extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _rescaleSvg() {
|
||||||
|
// Set the viewbox of the SVG containing the value to perfectly
|
||||||
|
// fit the text
|
||||||
|
// That way it will auto-scale correctly
|
||||||
|
const svgRoot = this.shadowRoot!.querySelector(".text")!;
|
||||||
|
const box = svgRoot.querySelector("text")!.getBBox()!;
|
||||||
|
svgRoot.setAttribute(
|
||||||
|
"viewBox",
|
||||||
|
`${box.x} ${box.y} ${box.width} ${box.height}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private _getSegmentLabel() {
|
private _getSegmentLabel() {
|
||||||
if (this.levels) {
|
if (this.levels) {
|
||||||
[...this.levels].sort((a, b) => a.level - b.level);
|
[...this.levels].sort((a, b) => a.level - b.level);
|
||||||
@@ -224,32 +243,43 @@ export class HaGauge extends LitElement {
|
|||||||
.levels-base {
|
.levels-base {
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke: var(--primary-background-color);
|
stroke: var(--primary-background-color);
|
||||||
stroke-width: 8;
|
stroke-width: 6;
|
||||||
stroke-linecap: round;
|
stroke-linecap: butt;
|
||||||
}
|
}
|
||||||
|
|
||||||
.level {
|
.level {
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 8;
|
stroke-width: 6;
|
||||||
stroke-linecap: butt;
|
stroke-linecap: butt;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value {
|
.value {
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 8;
|
stroke-width: 6;
|
||||||
stroke: var(--gauge-color);
|
stroke: var(--gauge-color);
|
||||||
stroke-linecap: round;
|
stroke-linecap: butt;
|
||||||
transition: stroke-dashoffset 1s ease 0s;
|
transition: stroke-dashoffset 1s ease 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.needle {
|
.needle {
|
||||||
stroke: var(--primary-text-color);
|
fill: var(--primary-text-color);
|
||||||
stroke-width: 2;
|
stroke: var(--card-background-color);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
stroke-width: 1;
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
transition: all 1s ease 0s;
|
transition: all 1s ease 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
position: absolute;
|
||||||
|
max-height: 40%;
|
||||||
|
max-width: 55%;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 10%;
|
||||||
|
transform: translate(-50%, 0%);
|
||||||
|
}
|
||||||
|
|
||||||
.value-text {
|
.value-text {
|
||||||
font-size: var(--ha-font-size-l);
|
font-size: var(--ha-font-size-l);
|
||||||
fill: var(--primary-text-color);
|
fill: var(--primary-text-color);
|
||||||
|
|||||||
@@ -303,9 +303,8 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
.title {
|
.title {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: var(--ha-font-size-l);
|
font-size: var(--ha-font-size-m);
|
||||||
line-height: var(--ha-line-height-expanded);
|
line-height: var(--ha-line-height-expanded);
|
||||||
padding: 0px 0px var(--ha-space-2) 0px;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -318,6 +317,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
ha-gauge {
|
ha-gauge {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 250px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user