3.2.1.0
Tweaked and updated the day tab background shades depending on cloud & rain. Give comfortable special treatment to avoid the green as it just looks weird.
This commit is contained in:
@@ -45,26 +45,13 @@
|
||||
import { h, Fragment } from '../../vendor/preact.js';
|
||||
import { useRef, useEffect, useState } from '../../vendor/preact-hooks.js';
|
||||
import htm from '../../vendor/htm.js';
|
||||
import { confidenceBand } from '../utils.js';
|
||||
import { confidenceBand, utciCategory } from '../utils.js';
|
||||
import { FREE_DAYS, FILTER_PROFILES, OUTDOORS_VARIANTS, profileButtonOrder, activityVariantKeys, placeVariantKeys, workVariantKeys } from '../config.js';
|
||||
import { CloudIcon, PrecipIcon, CustomSelect } from '../components.js';
|
||||
import { SubscribeModal } from './SubscribeModal.js';
|
||||
|
||||
const html = htm.bind(h);
|
||||
|
||||
// Converts a base RGB colour into the same 135° gradient used by the thermal
|
||||
// bands legend: 50% white blend to pastelise, then a subtle light→dark sweep.
|
||||
function weatherGradient(r, g, b) {
|
||||
const blend = (c) => Math.round(c + (255 - c) * 0.82);
|
||||
const [mr, mg, mb] = [blend(r), blend(g), blend(b)];
|
||||
const lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.30));
|
||||
const darken = (c) => Math.round(c * 0.96);
|
||||
const light = `rgb(${lighten(mr)},${lighten(mg)},${lighten(mb)})`;
|
||||
const mid = `rgb(${mr},${mg},${mb})`;
|
||||
const dark = `rgb(${darken(mr)},${darken(mg)},${darken(mb)})`;
|
||||
return `linear-gradient(135deg, ${light} 0%, ${mid} 60%, ${dark} 100%)`;
|
||||
}
|
||||
|
||||
// Active-tab variant: a more solid (less pastelised) version of the weather
|
||||
// colour, drawn as a radial gradient whose strong colour sits at the outer
|
||||
// edge and softens toward a lighter centre — so the hue reads as radiating
|
||||
@@ -333,21 +320,47 @@ export function DayTabs({
|
||||
const repDt = noonRow ? noonRow.dt : dDate;
|
||||
const showPrecip = dayPrecip >= 0.3 || daySnow >= 0.1;
|
||||
|
||||
// Base RGB for each weather condition — pastelised by weatherGradient()
|
||||
const [wR, wG, wB] = (daySnow >= 0.1) ? [100, 160, 230]
|
||||
: (dayHi > 44) ? [136, 0, 0]
|
||||
: (dayHi > 38) ? [210, 60, 30]
|
||||
: showPrecip ? [ 50, 120, 200]
|
||||
: (dayHi > 32) ? [220, 110, 30]
|
||||
: (dayHi > 26) ? [220, 180, 30]
|
||||
: modalCloudCat === 'overcast' ? [100, 110, 130]
|
||||
: (dayHi !== null && dayHi < 9) ? [ 60, 110, 180]
|
||||
: modalCloudCat === 'scattered' ? [160, 150, 130]
|
||||
: modalCloudCat === 'wispy' ? [200, 180, 130]
|
||||
: [220, 190, 50];
|
||||
// Danger days keep a dark, saturated edge with a lighter centre.
|
||||
const isDanger = dayHi !== null && dayHi > 44;
|
||||
const wBg = weatherGradient(wR, wG, wB);
|
||||
// Day-tab colour — PRIORITY model, no hue blending.
|
||||
// Mixing a warm yellow with grey/blue passes through green, so rather
|
||||
// than blend temperature with sky we pick ONE dimension by priority and
|
||||
// only vary its shade: heat → snow → rain → cloud → sun (first wins).
|
||||
// • Hot/extreme days always keep their heat colour (a safety signal).
|
||||
// • Otherwise rain wins, then cloud, then a clear "sunny"/cold colour.
|
||||
const mix = (a, b, t) => a.map((v, i) => Math.round(v + (b[i] - v) * Math.max(0, Math.min(1, t))));
|
||||
const hex2rgb = (hx) => { const n = parseInt(hx.replace('#', ''), 16); return [(n >> 16) & 255, (n >> 8) & 255, n & 255]; };
|
||||
const tBand = dayHi !== null ? utciCategory(dayHi) : { bg: '#f8e554' };
|
||||
|
||||
const ccVals = repRows.map(r => r.cc).filter(v => isFinite(v));
|
||||
const avgCloud = ccVals.length ? ccVals.reduce((s, v) => s + v, 0) / ccVals.length : 0;
|
||||
|
||||
const isHot = dayHi !== null && dayHi >= 29; // Hot band and above
|
||||
const isDanger = tBand.solid === true;
|
||||
|
||||
let rgb;
|
||||
if (isHot) {
|
||||
// Keep the heat hue; cloud only mutes it a touch (orange/red never
|
||||
// greens) and rain does not override heat.
|
||||
const heatFactor = dayHi >= 39 ? 0.15 : dayHi >= 34 ? 0.30 : 0.45;
|
||||
const t = Math.max(0, Math.min(1, (avgCloud - 30) / 70)) * heatFactor;
|
||||
rgb = mix(hex2rgb(tBand.bg), [170, 162, 152], t);
|
||||
} else if (daySnow >= 0.1) {
|
||||
// Snow: pale → icy blue with depth.
|
||||
rgb = mix([226, 234, 244], [176, 202, 232], Math.max(0, Math.min(1, daySnow / 1.5)));
|
||||
} else if (showPrecip) {
|
||||
// Rain: light → deep blue with amount (pure blue, no temperature hue).
|
||||
rgb = mix([150, 176, 212], [44, 84, 150], Math.max(0, Math.min(1, dayPrecip / 8)));
|
||||
} else if (avgCloud >= 30) {
|
||||
// Cloud: light → dark grey with cover (pure grey, no temperature hue).
|
||||
rgb = mix([205, 206, 208], [110, 115, 122], Math.max(0, Math.min(1, (avgCloud - 30) / 70)));
|
||||
} else if (dayHi !== null && dayHi < 10) {
|
||||
// Clear & cold: keep the cold temperature blue.
|
||||
rgb = hex2rgb(tBand.bg);
|
||||
} else {
|
||||
// Clear & mild/warm: a plain sunny yellow.
|
||||
rgb = hex2rgb('#f8e554');
|
||||
}
|
||||
const [wR, wG, wB] = rgb;
|
||||
|
||||
const wBgNeutral = isDanger
|
||||
? weatherGradientNeutral(wR, wG, wB, 0.15, 0.72)
|
||||
: weatherGradientNeutral(wR, wG, wB);
|
||||
|
||||
Reference in New Issue
Block a user