Further robustify trending news

- Convert back to RGB for max compat
- Better variable names
- Add fallback if there's no blurhash
- Refactor color utils
- Use alpha instead of light/dark colors
This commit is contained in:
Lim Chee Aun 2023-10-25 19:18:47 +08:00
parent 3a32cbf974
commit 5d5ab906ba
3 changed files with 77 additions and 65 deletions

View file

@ -54,10 +54,6 @@
}
a {
--other-color: var(--light-color);
@media (prefers-color-scheme: dark) {
--other-color: var(--dark-color);
}
min-width: 240px;
flex-grow: 1;
max-width: 320px;
@ -65,14 +61,14 @@
color: inherit;
border-radius: 16px;
overflow: hidden;
background-color: var(--other-color);
background-color: var(--accent-alpha-color);
border: 4px solid transparent;
box-shadow: 0 4px 8px -2px var(--drop-shadow-color);
transition: all 0.15s ease-out;
display: flex;
background-image: linear-gradient(
to bottom,
var(--accent-color) -50%,
var(--accent-color, var(--link-text-color)) -50%,
transparent
);
background-clip: border-box;
@ -82,7 +78,7 @@
max-height: 50vh;
&:not(:active):is(:hover, :focus-visible) {
border-color: var(--accent-color);
border-color: var(--accent-color, var(--link-light-color));
box-shadow: 0 4px 8px var(--drop-shadow-color),
0 8px 16px var(--drop-shadow-color);
transform-origin: center bottom;
@ -107,7 +103,7 @@
background-repeat: no-repeat;
background-image: linear-gradient(
to bottom,
var(--other-color) 70%,
var(--accent-alpha-color) 70%,
var(--bg-color) 100%
);
transition: background-position-y 0.15s ease-out;

View file

@ -12,6 +12,7 @@ import Menu2 from '../components/menu2';
import RelativeTime from '../components/relative-time';
import Timeline from '../components/timeline';
import { api } from '../utils/api';
import { oklab2rgb, rgb2oklab } from '../utils/color-utils';
import { filteredItems } from '../utils/filters';
import pmem from '../utils/pmem';
import states from '../utils/states';
@ -161,35 +162,16 @@ function Trending({ columnMode, ...props }) {
const domain = new URL(url).hostname
.replace(/^www\./, '')
.replace(/\/$/, '');
const averageColor = getBlurHashAverageColor(blurhash);
const labAverageColor = rgb2oklab(averageColor);
// const lightColor = averageColor.map((c) => {
// const v = c + 120;
// return v > 255 ? 255 : v;
// });
// const darkColor = averageColor.map((c) => {
// const v = c - 100;
// return v < 0 ? 0 : v;
// });
const accentColor = labAverageColor.map((c, i) => {
if (i === 0) {
return 0.65;
}
return c;
});
const lightColor = labAverageColor.map((c, i) => {
if (i === 0) {
return 0.9;
}
return c;
});
const darkColor = labAverageColor.map((c, i) => {
if (i === 0) {
return 0.4;
}
return c;
});
let accentColor;
if (blurhash) {
const averageColor = getBlurHashAverageColor(blurhash);
const labAverageColor = rgb2oklab(averageColor);
accentColor = oklab2rgb([
0.6,
labAverageColor[1],
labAverageColor[2],
]);
}
return (
<a
@ -197,14 +179,16 @@ function Trending({ columnMode, ...props }) {
href={url}
target="_blank"
rel="noopener noreferrer"
style={{
'--average-color': `rgb(${averageColor?.join(',')})`,
// '--light-color': `rgb(${lightColor?.join(',')})`,
// '--dark-color': `rgb(${darkColor?.join(',')})`,
'--accent-color': `oklab(${accentColor?.join(' ')})`,
'--light-color': `oklab(${lightColor?.join(' ')})`,
'--dark-color': `oklab(${darkColor?.join(' ')})`,
}}
style={
accentColor
? {
'--accent-color': `rgb(${accentColor.join(',')})`,
'--accent-alpha-color': `rgba(${accentColor.join(
',',
)}, 0.4)`,
}
: {}
}
>
<article>
<figure>
@ -311,24 +295,4 @@ function Trending({ columnMode, ...props }) {
);
}
// https://gist.github.com/earthbound19/e7fe15fdf8ca3ef814750a61bc75b5ce
const gammaToLinear = (c) =>
c >= 0.04045 ? Math.pow((c + 0.055) / 1.055, 2.4) : c / 12.92;
function rgb2oklab([r, g, b]) {
r = gammaToLinear(r / 255);
g = gammaToLinear(g / 255);
b = gammaToLinear(b / 255);
var l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
var m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
var s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
l = Math.cbrt(l);
m = Math.cbrt(m);
s = Math.cbrt(s);
return [
l * +0.2104542553 + m * +0.793617785 + s * -0.0040720468,
l * +1.9779984951 + m * -2.428592205 + s * +0.4505937099,
l * +0.0259040371 + m * +0.7827717662 + s * -0.808675766,
];
}
export default Trending;

52
src/utils/color-utils.js Normal file
View file

@ -0,0 +1,52 @@
// https://gist.github.com/earthbound19/e7fe15fdf8ca3ef814750a61bc75b5ce
function clamp(value, min, max) {
return Math.max(Math.min(value, max), min);
}
const gammaToLinear = (c) =>
c >= 0.04045 ? Math.pow((c + 0.055) / 1.055, 2.4) : c / 12.92;
const linearToGamma = (c) =>
c >= 0.0031308 ? 1.055 * Math.pow(c, 1 / 2.4) - 0.055 : 12.92 * c;
export function rgb2oklab([r, g, b]) {
r = gammaToLinear(r / 255);
g = gammaToLinear(g / 255);
b = gammaToLinear(b / 255);
var l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
var m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
var s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
l = Math.cbrt(l);
m = Math.cbrt(m);
s = Math.cbrt(s);
return [
l * +0.2104542553 + m * +0.793617785 + s * -0.0040720468,
l * +1.9779984951 + m * -2.428592205 + s * +0.4505937099,
l * +0.0259040371 + m * +0.7827717662 + s * -0.808675766,
];
}
export function oklab2rgb([L, a, b]) {
var l = L + a * +0.3963377774 + b * +0.2158037573;
var m = L + a * -0.1055613458 + b * -0.0638541728;
var s = L + a * -0.0894841775 + b * -1.291485548;
// The ** operator here cubes; same as l_*l_*l_ in the C++ example:
l = l ** 3;
m = m ** 3;
s = s ** 3;
var r = l * +4.0767416621 + m * -3.3077115913 + s * +0.2309699292;
var g = l * -1.2684380046 + m * +2.6097574011 + s * -0.3413193965;
var b = l * -0.0041960863 + m * -0.7034186147 + s * +1.707614701;
// Convert linear RGB values returned from oklab math to sRGB for our use before returning them:
r = 255 * linearToGamma(r);
g = 255 * linearToGamma(g);
b = 255 * linearToGamma(b);
// OPTION: clamp r g and b values to the range 0-255; but if you use the values immediately to draw, JavaScript clamps them on use:
r = clamp(r, 0, 255);
g = clamp(g, 0, 255);
b = clamp(b, 0, 255);
// OPTION: round the values. May not be necessary if you use them immediately for rendering in JavaScript, as JavaScript (also) discards decimals on render:
r = Math.round(r);
g = Math.round(g);
b = Math.round(b);
return [r, g, b];
}