mirror of
https://github.com/narrrl/holy-flavor-rater.git
synced 2026-11-03 16:41:27 +01:00
144 lines
4.8 KiB
TypeScript
144 lines
4.8 KiB
TypeScript
/**
|
|
* The SVG filter definitions behind the liquid-glass look, mounted exactly once
|
|
* (from `AppProviders`) so `url(#…)` resolves anywhere in the app.
|
|
*
|
|
* Why these are static: refraction reads as *liquid* because the backdrop moves
|
|
* behind a fixed displacement map — not because the map itself animates.
|
|
* Animating `baseFrequency` would re-rasterise the turbulence every frame; a
|
|
* static seed over the drifting aurora/canvas backdrop costs one raster pass
|
|
* and looks the same.
|
|
*
|
|
* These are consumed through `theme.tokens.glass.rimFilter` /
|
|
* `rimFilterChroma`, and only ever on a thin masked ring (see `Glass.tsx`) —
|
|
* never over a whole pane, which would be Chrome-only and far too expensive on
|
|
* a grid of cards.
|
|
*/
|
|
|
|
/** Turbulence shared by both rim filters: soft, lens-like lobes rather than sand. */
|
|
const Turbulence = () => (
|
|
<>
|
|
<feTurbulence
|
|
type="fractalNoise"
|
|
baseFrequency="0.008 0.012"
|
|
numOctaves={2}
|
|
seed={7}
|
|
result="noise"
|
|
/>
|
|
<feGaussianBlur in="noise" stdDeviation="1.2" result="soft" />
|
|
</>
|
|
);
|
|
|
|
/** Isolates one channel of the source so it can be displaced independently. */
|
|
const channelMatrix = (channel: 'r' | 'g' | 'b') =>
|
|
({
|
|
r: '1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0',
|
|
g: '0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0',
|
|
b: '0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0',
|
|
})[channel];
|
|
|
|
export const GlassDefs = () => (
|
|
<svg
|
|
width="0"
|
|
height="0"
|
|
aria-hidden="true"
|
|
focusable="false"
|
|
style={{ position: 'absolute', width: 0, height: 0, overflow: 'hidden' }}
|
|
>
|
|
<defs>
|
|
{/* Monochrome refraction — the workhorse, used on the `panel` tier. */}
|
|
<filter
|
|
id="hfr-rim"
|
|
x="-20%"
|
|
y="-20%"
|
|
width="140%"
|
|
height="140%"
|
|
colorInterpolationFilters="sRGB"
|
|
>
|
|
<Turbulence />
|
|
<feDisplacementMap
|
|
in="SourceGraphic"
|
|
in2="soft"
|
|
scale={11}
|
|
xChannelSelector="R"
|
|
yChannelSelector="G"
|
|
result="disp"
|
|
/>
|
|
{/* Displacement near an edge pulls in samples from outside the backdrop
|
|
image, which come back as transparent black and paint a dark fringe
|
|
along the rim. Compositing the displaced result *over* the original
|
|
backdrop fills those gaps with the undisplaced pixels instead. */}
|
|
<feComposite in="disp" in2="SourceGraphic" operator="over" />
|
|
</filter>
|
|
|
|
{/* Chromatic refraction — R/G/B displaced by different amounts, then
|
|
screened back together, giving the prismatic edge real glass has.
|
|
Three displacement passes, so `chrome` tier only. */}
|
|
<filter
|
|
id="hfr-rim-chroma"
|
|
x="-20%"
|
|
y="-20%"
|
|
width="140%"
|
|
height="140%"
|
|
colorInterpolationFilters="sRGB"
|
|
>
|
|
<Turbulence />
|
|
<feColorMatrix
|
|
in="SourceGraphic"
|
|
type="matrix"
|
|
values={channelMatrix('r')}
|
|
result="rOnly"
|
|
/>
|
|
<feDisplacementMap
|
|
in="rOnly"
|
|
in2="soft"
|
|
scale={14}
|
|
xChannelSelector="R"
|
|
yChannelSelector="G"
|
|
result="rDisp"
|
|
/>
|
|
<feColorMatrix
|
|
in="SourceGraphic"
|
|
type="matrix"
|
|
values={channelMatrix('g')}
|
|
result="gOnly"
|
|
/>
|
|
<feDisplacementMap
|
|
in="gOnly"
|
|
in2="soft"
|
|
scale={11}
|
|
xChannelSelector="R"
|
|
yChannelSelector="G"
|
|
result="gDisp"
|
|
/>
|
|
<feColorMatrix
|
|
in="SourceGraphic"
|
|
type="matrix"
|
|
values={channelMatrix('b')}
|
|
result="bOnly"
|
|
/>
|
|
<feDisplacementMap
|
|
in="bOnly"
|
|
in2="soft"
|
|
scale={8}
|
|
xChannelSelector="R"
|
|
yChannelSelector="G"
|
|
result="bDisp"
|
|
/>
|
|
<feBlend in="rDisp" in2="gDisp" mode="screen" result="rg" />
|
|
<feBlend in="rg" in2="bDisp" mode="screen" result="chroma" />
|
|
{/* Same edge-fringe patch as #hfr-rim. */}
|
|
<feComposite in="chroma" in2="SourceGraphic" operator="over" />
|
|
</filter>
|
|
</defs>
|
|
</svg>
|
|
);
|
|
|
|
/**
|
|
* Fine grain, as a self-contained data URI rather than a `filter: url()` chain —
|
|
* it tiles as a plain background-image, so it costs nothing per frame.
|
|
* Layered at low opacity over the aurora it kills gradient banding and gives
|
|
* the rim refraction something fine-grained to bend.
|
|
*/
|
|
export const GRAIN_DATA_URI =
|
|
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E\")";
|