toot medias: extract color from blurhash
All checks were successful
/ depoly (push) Successful in 1m17s

This commit is contained in:
thislight 2024-11-11 16:53:34 +08:00
parent c372ea4a92
commit 2aa2cf21da
No known key found for this signature in database
GPG key ID: FCFE5192241CCD4E
5 changed files with 226 additions and 40 deletions

View file

@ -6,6 +6,7 @@ import {
Show,
createRenderEffect,
createEffect,
createMemo,
} from "solid-js";
import tootStyle from "./toot.module.css";
import { formatRelative } from "date-fns";
@ -29,13 +30,13 @@ import { Divider } from "@suid/material";
import cardStyle from "../material/cards.module.css";
import Button from "../material/Button.js";
import MediaAttachmentGrid from "./MediaAttachmentGrid.js";
import { FastAverageColor } from "fast-average-color";
import Color from "colorjs.io";
import { useDateFnLocale } from "../platform/i18n";
import { canShare, share } from "../platform/share";
import { makeAcctText, useDefaultSession } from "../masto/clients";
import TootContent from "./toot-components/TootContent";
import BoostIcon from "./toot-components/BoostIcon";
import { averageColorHex } from "../platform/blurhash";
type TootActionGroupProps<T extends mastodon.v1.Status> = {
onRetoot?: (value: T) => void;
@ -205,31 +206,44 @@ export function TootPreviewCard(props: {
}
});
const onImgLoad = (event: Event & { currentTarget: HTMLImageElement }) => {
// TODO: better extraction algorithm
// I'd like to use a pattern panel and match one in the panel from the extracted color
const fac = new FastAverageColor();
const result = fac.getColor(event.currentTarget);
if (result.error) {
console.error(result.error);
fac.destroy();
const imgAverageColor = createMemo(() => {
if (!props.src.image) return;
return new Color(averageColorHex(props.src.blurhash));
});
const prefersWhiteText = createMemo(() => {
const oc = imgAverageColor();
if (!oc) return;
const colorWhite = new Color("white");
return colorWhite.luminance / oc.luminance > 3.5;
});
const focusSurfaceColor = createMemo(() => {
const oc = imgAverageColor();
if (!oc) return;
if (prefersWhiteText()) {
return new Color(oc).darken(0.2);
} else {
return new Color(oc).lighten(0.2);
}
});
const textColorName = createMemo(() => {
const useWhiteText = prefersWhiteText();
if (typeof useWhiteText === "undefined") {
return;
}
root.style.setProperty("--tutu-color-surface", result.hex);
const focusSurface = result.isDark
? new Color(result.hex).darken(0.2)
: new Color(result.hex).lighten(0.2);
root.style.setProperty("--tutu-color-surface-d", focusSurface.toString());
const textColor = result.isDark ? "white" : "black";
const secondaryTextColor = new Color(textColor);
secondaryTextColor.alpha = 0.75;
root.style.setProperty("--tutu-color-on-surface", textColor);
root.style.setProperty(
"--tutu-color-secondary-text-on-surface",
secondaryTextColor.toString(),
);
fac.destroy();
};
return useWhiteText ? "white" : "black";
});
const secondaryTextColor = createMemo(() => {
const tcn = textColorName();
if (!tcn) return;
const tc = new Color(tcn);
tc.alpha = 0.75;
return tc;
});
return (
<a
@ -238,12 +252,18 @@ export function TootPreviewCard(props: {
href={props.src.url}
target="_blank"
referrerPolicy="unsafe-url"
style={{
"--tutu-color-surface": imgAverageColor()?.toString(),
"--tutu-color-surface-d": focusSurfaceColor()?.toString(),
"--tutu-color-on-surface": textColorName(),
"--tutu-color-secondary-text-on-surface":
secondaryTextColor()?.toString(),
}}
>
<Show when={props.src.image}>
<img
crossOrigin="anonymous"
src={props.src.image!}
onLoad={onImgLoad}
width={props.src.width || undefined}
height={props.src.height || undefined}
loading="lazy"
@ -373,16 +393,16 @@ const RegularToot: Component<TootCardProps> = (props) => {
<Show when={!!status().reblog}>
<div class={tootStyle.tootRetootGrp}>
<BoostIcon />
<Body2
ref={(e: { innerHTML: string }) => {
createRenderEffect(() => {
e.innerHTML = resolveCustomEmoji(
status().account.displayName,
toot().emojis,
);
});
}}
></Body2>
<Body2
ref={(e: { innerHTML: string }) => {
createRenderEffect(() => {
e.innerHTML = resolveCustomEmoji(
status().account.displayName,
toot().emojis,
);
});
}}
></Body2>
<span>boosts</span>
</div>
</Show>