tangled
alpha
login
or
join now
flo-bit.dev
/
blento
your personal website on atproto - mirror
blento.app
20
fork
atom
overview
issues
pulls
pipelines
fluid text
Florian
4 days ago
7b75dd80
f817bf7e
+45
-9
1 changed file
expand all
collapse all
unified
split
src
lib
cards
visual
FluidTextCard
FluidTextCard.svelte
+45
-9
src/lib/cards/visual/FluidTextCard/FluidTextCard.svelte
···
1
1
<script lang="ts">
2
2
-
import { colorToHue, getCSSVar, getHexOfCardColor } from '../../helper';
2
2
+
import { colorToHue, getHexCSSVar, getHexOfCardColor } from '../../helper';
3
3
import type { ContentComponentProps } from '../../types';
4
4
import { onMount, onDestroy, tick } from 'svelte';
5
5
let { item }: ContentComponentProps = $props();
···
13
13
let maskReady = false;
14
14
let isInitialized = $state(false);
15
15
let resizeObserver: ResizeObserver | null = null;
16
16
+
let themeObserver: MutationObserver | null = null;
16
17
17
18
// Pure hash function for shader keyword caching
18
19
function hashCode(s: string) {
···
132
133
ctx.clearRect(0, 0, maskCanvas.width, maskCanvas.height);
133
134
ctx.scale(dpr, dpr);
134
135
135
135
-
//const color = getCSSVar('--color-base-900');
136
136
-
137
137
-
ctx.fillStyle = 'black';
136
136
+
const isDark = document.documentElement.classList.contains('dark');
137
137
+
const bgColor =
138
138
+
item.color === 'transparent'
139
139
+
? getHexCSSVar(isDark ? '--color-base-900' : '--color-base-50')
140
140
+
: 'black';
141
141
+
ctx.fillStyle = bgColor;
138
142
ctx.fillRect(0, 0, width, height);
139
143
140
144
// Font size as percentage of container width
141
145
const textFontSize = Math.round(width * fontSize);
142
146
ctx.font = `${fontWeight} ${textFontSize}px ${fontFamily}`;
143
147
144
144
-
ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)';
145
145
-
ctx.lineWidth = 2;
148
148
+
ctx.lineWidth = 3;
146
149
ctx.textAlign = 'center';
147
150
148
151
const metrics = ctx.measureText(text);
···
157
160
ctx.textBaseline = 'middle';
158
161
}
159
162
160
160
-
ctx.strokeText(text, width / 2, textY);
163
163
+
if (item.color === 'transparent') {
164
164
+
// Partially cut out the stroke area so fluid shows through
165
165
+
ctx.globalCompositeOperation = 'destination-out';
166
166
+
ctx.globalAlpha = 0.7;
167
167
+
ctx.strokeStyle = 'white';
168
168
+
ctx.strokeText(text, width / 2, textY);
169
169
+
ctx.globalAlpha = 1;
170
170
+
ctx.globalCompositeOperation = 'source-over';
171
171
+
172
172
+
// Add overlay: brighten in dark mode, darken in light mode
173
173
+
ctx.strokeStyle = isDark ? 'rgba(255, 255, 255, 0.15)' : 'rgba(0, 0, 0, 0.2)';
174
174
+
ctx.strokeText(text, width / 2, textY);
175
175
+
} else {
176
176
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)';
177
177
+
ctx.strokeText(text, width / 2, textY);
178
178
+
}
179
179
+
161
180
ctx.globalCompositeOperation = 'destination-out';
162
181
ctx.fillText(text, width / 2, textY);
163
182
ctx.globalCompositeOperation = 'source-over';
···
214
233
if (isInitialized) scheduleMaskDraw();
215
234
});
216
235
}
236
236
+
237
237
+
// Watch for dark mode changes to redraw mask with correct background
238
238
+
if (item.color === 'transparent') {
239
239
+
themeObserver = new MutationObserver(() => {
240
240
+
if (isInitialized) scheduleMaskDraw();
241
241
+
});
242
242
+
themeObserver.observe(document.documentElement, {
243
243
+
attributes: true,
244
244
+
attributeFilter: ['class']
245
245
+
});
246
246
+
}
217
247
});
218
248
219
249
onDestroy(() => {
···
221
251
if (splatIntervalId) clearInterval(splatIntervalId);
222
252
if (maskDrawRaf) cancelAnimationFrame(maskDrawRaf);
223
253
if (resizeObserver) resizeObserver.disconnect();
254
254
+
if (themeObserver) themeObserver.disconnect();
224
255
});
225
256
226
257
function initFluidSimulation(startHue: number, endHue: number) {
···
246
277
COLOR_UPDATE_SPEED: 10,
247
278
PAUSED: false,
248
279
BACK_COLOR: { r: 0, g: 0, b: 0 },
249
249
-
TRANSPARENT: false,
280
280
+
TRANSPARENT: item.color === 'transparent',
250
281
BLOOM: false,
251
282
BLOOM_ITERATIONS: 8,
252
283
BLOOM_RESOLUTION: 256,
···
1701
1732
}
1702
1733
</script>
1703
1734
1704
1704
-
<div bind:this={container} class="relative h-full w-full overflow-hidden bg-black">
1735
1735
+
<div
1736
1736
+
bind:this={container}
1737
1737
+
class="relative h-full w-full overflow-hidden {item.color === 'transparent'
1738
1738
+
? 'bg-base-50 dark:bg-base-900'
1739
1739
+
: 'bg-black'}"
1740
1740
+
>
1705
1741
<canvas bind:this={fluidCanvas} class="absolute h-full w-full"></canvas>
1706
1742
<canvas bind:this={maskCanvas} class="absolute h-full w-full"></canvas>
1707
1743
</div>