fix: use d3 to interpolate colors (#1118)

This commit is contained in:
akloeckner
2024-08-07 18:43:54 +02:00
committed by GitHub
parent c872c02e7d
commit b8676f5571
5 changed files with 56 additions and 53 deletions

36
package-lock.json generated
View File

@@ -5,12 +5,12 @@
"requires": true,
"packages": {
"": {
"name": "mini-graph-card",
"version": "0.11.0",
"version": "0.12.2-dev.1",
"license": "MIT",
"dependencies": {
"@kalkih/lz-string": "^1.4.5",
"custom-card-helpers": "^1.6.6",
"d3-interpolate": "^3.0.1",
"lit-element": "^2.2.1",
"localforage": "^1.7.3",
"spark-md5": "^3.0.1"
@@ -3907,6 +3907,25 @@
"typescript": "^3.8.3"
}
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/dateformat": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
@@ -18952,6 +18971,19 @@
"typescript": "^3.8.3"
}
},
"d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="
},
"d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"requires": {
"d3-color": "1 - 3"
}
},
"dateformat": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",

View File

@@ -21,6 +21,7 @@
"dependencies": {
"@kalkih/lz-string": "^1.4.5",
"custom-card-helpers": "^1.6.6",
"d3-interpolate": "^3.0.1",
"lit-element": "^2.2.1",
"localforage": "^1.7.3",
"spark-md5": "^3.0.1"

View File

@@ -1,4 +1,4 @@
import { interpolateColor } from './utils';
import { interpolateRgb } from 'd3-interpolate';
import {
X, Y, V,
ONE_HOUR,
@@ -162,10 +162,10 @@ export default class Graph {
let color;
if (stop.value > this._max && arr[index + 1]) {
const factor = (this._max - arr[index + 1].value) / (stop.value - arr[index + 1].value);
color = interpolateColor(arr[index + 1].color, stop.color, factor);
color = interpolateRgb(arr[index + 1].color, stop.color)(factor);
} else if (stop.value < this._min && arr[index - 1]) {
const factor = (arr[index - 1].value - this._min) / (arr[index - 1].value - stop.value);
color = interpolateColor(arr[index - 1].color, stop.color, factor);
color = interpolateRgb(arr[index - 1].color, stop.color)(factor);
}
let offset;
if (scale <= 0) {

View File

@@ -2,6 +2,7 @@ import { LitElement, html, svg } from 'lit-element';
import localForage from 'localforage/src/localforage';
import { stateIcon } from 'custom-card-helpers';
import SparkMD5 from 'spark-md5';
import { interpolateRgb } from 'd3-interpolate';
import Graph from './graph';
import style from './style';
import handleClick from './handleClick';
@@ -18,7 +19,6 @@ import {
import {
getMin, getAvg, getMax,
getTime, getMilli,
interpolateColor,
compress, decompress,
getFirstDefinedItem,
compareArray,
@@ -149,7 +149,7 @@ class MiniGraphCard extends LitElement {
shouldUpdate(changedProps) {
if (UPDATE_PROPS.some(prop => changedProps.has(prop))) {
this.color = this.intColor(
this.color = this.computeColor(
this.tooltip.value !== undefined
? this.tooltip.value : this.entity[0] && this.entity[0].state,
this.tooltip.entity || 0,
@@ -356,7 +356,7 @@ class MiniGraphCard extends LitElement {
renderIndicator(state, index) {
return svg`
<svg width='10' height='10'>
<rect width='10' height='10' fill=${this.intColor(state, index)} />
<rect width='10' height='10' fill=${this.computeColor(state, index)} />
</svg>
`;
}
@@ -475,7 +475,7 @@ class MiniGraphCard extends LitElement {
if (!fill) return;
const svgFill = this.gradient[i]
? `url(#grad-${this.id}-${i})`
: this.intColor(this.entity[i].state, i);
: this.computeColor(this.entity[i].state, i);
return svg`
<rect class='fill--rect'
?inactive=${this.tooltip.entity !== undefined && this.tooltip.entity !== i}
@@ -605,17 +605,6 @@ class MiniGraphCard extends LitElement {
handleClick(this, this._hass, this.config, this.config.tap_action, entity.entity_id || entity);
}
computeColor(inState, i) {
const { color_thresholds, line_color } = this.config;
const state = Number(inState) || 0;
const threshold = {
color: line_color[i] || line_color[0],
...color_thresholds.slice(-1)[0],
...color_thresholds.find(ele => ele.value < state),
};
return this.config.entities[i].color || threshold.color;
}
get visibleEntities() {
return this.config.entities.filter(entity => entity.show_graph !== false);
}
@@ -641,28 +630,25 @@ class MiniGraphCard extends LitElement {
return this.secondaryYaxisEntities.map(entity => this.Graph[entity.index]);
}
intColor(inState, i) {
computeColor(inState, i) {
const { color_thresholds, line_color } = this.config;
const state = Number(inState) || 0;
let intColor;
if (color_thresholds.length > 0) {
if (this.config.show.graph === 'bar') {
const { color } = color_thresholds.find(ele => ele.value < state)
|| color_thresholds.slice(-1)[0];
intColor = color;
const { color } = color_thresholds.find(ele => ele.value < state)
|| color_thresholds.slice(-1)[0];
intColor = color;
const index = color_thresholds.findIndex(ele => ele.value < state);
const c1 = color_thresholds[index];
const c2 = color_thresholds[index - 1];
if (c2) {
const factor = (c2.value - state) / (c2.value - c1.value);
intColor = interpolateRgb(c2.color, c1.color)(factor);
} else {
const index = color_thresholds.findIndex(ele => ele.value < state);
const c1 = color_thresholds[index];
const c2 = color_thresholds[index - 1];
if (c2) {
const factor = (c2.value - inState) / (c2.value - c1.value);
intColor = interpolateColor(c2.color, c1.color, factor);
} else {
intColor = index
? color_thresholds[color_thresholds.length - 1].color
: color_thresholds[0].color;
}
intColor = index
? color_thresholds[color_thresholds.length - 1].color
: color_thresholds[0].color;
}
}

View File

@@ -13,22 +13,6 @@ const getMax = (arr, val) => arr.reduce((max, p) => (
const getTime = (date, extra, locale = 'en-US') => date.toLocaleString(locale, { hour: 'numeric', minute: 'numeric', ...extra });
const getMilli = hours => hours * 60 ** 2 * 10 ** 3;
const interpolateColor = (a, b, factor) => {
const ah = +a.replace('#', '0x');
const ar = ah >> 16;
const ag = (ah >> 8) & 0xff;
const ab = ah & 0xff;
const bh = +b.replace('#', '0x');
const br = bh >> 16;
const bg = (bh >> 8) & 0xff;
const bb = bh & 0xff;
const rr = ar + factor * (br - ar);
const rg = ag + factor * (bg - ag);
const rb = ab + factor * (bb - ab);
return `#${(((1 << 24) + (rr << 16) + (rg << 8) + rb) | 0).toString(16).slice(1)}`;
};
const compress = data => lzStringCompress(JSON.stringify(data));
const decompress = data => (typeof data === 'string' ? JSON.parse(lzStringDecompress(data)) : data);
@@ -44,7 +28,7 @@ const log = (message) => {
};
export {
getMin, getAvg, getMax, getTime, getMilli, interpolateColor, compress, decompress, log,
getMin, getAvg, getMax, getTime, getMilli, compress, decompress, log,
getFirstDefinedItem,
compareArray,
};