Skip to content

Commit a2ecff0

Browse files
committed
basic glass blur
1 parent e059ea1 commit a2ecff0

File tree

6 files changed

+230
-16
lines changed

6 files changed

+230
-16
lines changed

jgantts-com/src/App.vue

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import { RouterLink, RouterView } from 'vue-router'
3-
import { ref, onMounted } from 'vue'
3+
import { ref, onMounted, provide } from 'vue'
44
55
import Island from "./components/Island.vue"
66
import DStack from "./library-jgantts/DStack.vue";
@@ -23,11 +23,13 @@ import { BackgroundState } from './Curtain/Types';
2323
2424
import EnvelopeIcon from './assets/icons/envelope.svg'
2525
26+
const backgroundRef = ref(null)
27+
provide("backgroundRef", backgroundRef);
28+
2629
const sleep = (ms: number|undefined) => {
2730
return new Promise(resolve => setTimeout(resolve, ms || 2000));
2831
}
2932
30-
const backgroundRef = ref(null)
3133
const replayButtonRef = ref(null)
3234
3335
const darkModePreference = window.matchMedia("(prefers-color-scheme: dark)")
@@ -59,8 +61,7 @@ function firstRunDone() {
5961
replayButtonRef.value?.firstRunDone()
6062
}
6163
62-
onMounted(() => {
63-
})
64+
6465
</script>
6566

6667
<template>
@@ -76,7 +77,7 @@ onMounted(() => {
7677
</h1>
7778
</VStack>
7879
</Island>
79-
<NavBar />
80+
<!-- <NavBar /> -->
8081
<router-view v-slot="{ Component }">
8182
<transition
8283
name="fade"
@@ -109,7 +110,7 @@ onMounted(() => {
109110
</DStack>
110111
</VStack>
111112
</div>
112-
<Background ref="backgroundRef" @first-run-done="firstRunDone"/>
113+
<Background ref="backgroundRef" @first-run-done="firstRunDone"/>
113114
</div>
114115
</div>
115116
</template>
@@ -239,6 +240,9 @@ onMounted(() => {
239240
.highlight {
240241
color: var(--textAccentOnBase);
241242
}
243+
.highlight2 {
244+
color: var(--textBaseOnBase);
245+
}
242246
243247
.heavy {
244248
font-weight: 800;

jgantts-com/src/Curtain/Curtain.vue

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ let gaussianObjects: GaussianObject[]
6060
let canvasContext: CanvasRenderingContext2D
6161
let canvasElement: HTMLCanvasElement
6262
63+
6364
let resizeTimeout: ReturnType<typeof setTimeout> | undefined;
6465
6566
function throttledResizeHandler() {
@@ -337,6 +338,8 @@ async function renderScene(state: AnimationState|null): Promise<AnimationState>
337338
canvasContext.fillStyle = backgroundPattern ?? "black"
338339
canvasContext.fill()
339340
341+
emit("export-ready", canvasContext.getImageData(0, 0, canvasElement.width, canvasElement.height));
342+
340343
return AnimationState.Inside
341344
}
342345
@@ -548,6 +551,10 @@ const loadCurtain = async (rainbowIn: Rainbow) => {
548551
initializeBackground()
549552
}
550553
554+
const getCanvas = async (): Promise<HTMLCanvasElement> => {
555+
return canvasElement
556+
}
557+
551558
let playStateInternal = BackgroundState.Unset
552559
const pauseMutex = new Mutex()
553560
const pausePlay = async (): Promise<BackgroundState> => {
@@ -589,11 +596,14 @@ const play = async (): Promise<BackgroundState> => {
589596
})
590597
}
591598
599+
592600
const emit = defineEmits([
593601
'curtainCall',
594602
'stageEntrance',
603+
'export-ready',
595604
]);
596-
defineExpose({
605+
defineExpose({
606+
getCanvas,
597607
loadCurtain,
598608
pausePlay,
599609
play,

jgantts-com/src/components/Background.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script setup lang="ts">
2+
let gl: WebGLRenderingContext | null = null;
3+
24
import { ref, onMounted } from 'vue'
35
46
import Curtain from '../Curtain/Curtain.vue';
@@ -344,14 +346,19 @@ async function pausePlay(): Promise<BackgroundState> {
344346
return await curtainRef?.value?.pausePlay()
345347
}
346348
349+
async function getCanvas(): Promise<HTMLCanvasElement|null> {
350+
//@ts-expect-error
351+
return await curtainRef?.value?.getCanvas()
352+
}
353+
347354
let firstRunStarted = false
348355
let firstRunDone = false
349356
let continualRun = false
350357
const emit = defineEmits([
351358
'firstRunDone',
352359
]);
353360
354-
defineExpose({ pausePlay })
361+
defineExpose({ pausePlay, getCanvas });
355362
</script>
356363

357364
<template>
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
<template>
2+
<div class="glass-panel">
3+
<canvas ref="canvas"></canvas>
4+
<div class="content">
5+
<slot />
6+
</div>
7+
</div>
8+
</template>
9+
10+
<script setup lang="ts">
11+
import { ref, inject, onMounted, nextTick } from "vue";
12+
13+
// Inject the background reference provided by your Background component
14+
interface BackgroundRef {
15+
getCanvas: () => HTMLCanvasElement | null;
16+
}
17+
const backgroundRef = inject<Ref<BackgroundRef | null>>("backgroundRef");
18+
19+
onMounted(async () => {
20+
await nextTick();
21+
22+
23+
24+
const canvas = backgroundRef.value?.getCanvas();
25+
if (!canvas) {
26+
console.error("GlassPanel: canvas ref missing");
27+
return;
28+
}
29+
console.log(canvas)
30+
31+
const bgCanvas = backgroundRef?.value?.getCanvas();
32+
if (!bgCanvas) {
33+
console.warn("GlassPanel: background canvas not ready");
34+
return;
35+
}
36+
37+
// Set canvas size to match container
38+
const setSize = () => {
39+
canvas.width = canvas.clientWidth;
40+
canvas.height = canvas.clientHeight;
41+
};
42+
setSize();
43+
window.addEventListener("resize", setSize);
44+
45+
// Initialize WebGL
46+
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
47+
if (!gl) {
48+
console.error("GlassPanel: WebGL not supported");
49+
return;
50+
}
51+
52+
// Vertex shader
53+
const vsSource = `
54+
attribute vec2 position;
55+
void main() {
56+
gl_Position = vec4(position, 0.0, 1.0);
57+
}
58+
`;
59+
60+
// Fragment shader: simple lens distortion
61+
const fsSource = `
62+
precision mediump float;
63+
uniform sampler2D u_texture;
64+
uniform vec2 u_resolution;
65+
uniform vec2 u_mouse;
66+
67+
void main() {
68+
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
69+
vec2 center = u_mouse / u_resolution;
70+
vec2 offset = uv - center;
71+
float r = length(offset);
72+
uv += offset * 0.2 * exp(-5.0 * r*r); // lens warp
73+
gl_FragColor = texture2D(u_texture, uv);
74+
}
75+
`;
76+
77+
const createShader = (type: number, source: string) => {
78+
const shader = gl!.createShader(type)!;
79+
gl!.shaderSource(shader, source);
80+
gl!.compileShader(shader);
81+
if (!gl!.getShaderParameter(shader, gl!.COMPILE_STATUS)) {
82+
console.error(gl!.getShaderInfoLog(shader));
83+
gl!.deleteShader(shader);
84+
return null;
85+
}
86+
return shader;
87+
};
88+
89+
const vs = createShader(gl.VERTEX_SHADER, vsSource)!;
90+
const fs = createShader(gl.FRAGMENT_SHADER, fsSource)!;
91+
92+
const program = gl.createProgram()!;
93+
gl.attachShader(program, vs);
94+
gl.attachShader(program, fs);
95+
gl.linkProgram(program);
96+
gl.useProgram(program);
97+
98+
// Quad covering the canvas
99+
const buffer = gl.createBuffer();
100+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
101+
gl.bufferData(
102+
gl.ARRAY_BUFFER,
103+
new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
104+
gl.STATIC_DRAW
105+
);
106+
107+
const position = gl.getAttribLocation(program, "position");
108+
gl.enableVertexAttribArray(position);
109+
gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
110+
111+
// Uniform locations
112+
const uniforms = {
113+
resolution: gl.getUniformLocation(program, "u_resolution")!,
114+
mouse: gl.getUniformLocation(program, "u_mouse")!,
115+
texture: gl.getUniformLocation(program, "u_texture")!,
116+
};
117+
118+
// Mouse tracking
119+
let mouse = [canvas.width / 2, canvas.height / 2];
120+
canvas.addEventListener("mousemove", (e) => {
121+
const rect = canvas.getBoundingClientRect();
122+
mouse = [e.clientX - rect.left, canvas.height - (e.clientY - rect.top)];
123+
});
124+
125+
// Texture setup
126+
const texture = gl.createTexture()!;
127+
gl.bindTexture(gl.TEXTURE_2D, texture);
128+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
129+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
130+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
131+
132+
// Render loop
133+
const render = () => {
134+
const bgCanvasCurrent = backgroundRef?.value?.getCanvas();
135+
if (!bgCanvasCurrent) return;
136+
137+
gl.viewport(0, 0, canvas.width, canvas.height);
138+
gl.clear(gl.COLOR_BUFFER_BIT);
139+
140+
gl.bindTexture(gl.TEXTURE_2D, texture);
141+
gl.texImage2D(
142+
gl.TEXTURE_2D,
143+
0,
144+
gl.RGBA,
145+
gl.RGBA,
146+
gl.UNSIGNED_BYTE,
147+
bgCanvasCurrent
148+
);
149+
150+
gl.uniform2f(uniforms.resolution, canvas.width, canvas.height);
151+
gl.uniform2f(uniforms.mouse, mouse[0], mouse[1]);
152+
gl.uniform1i(uniforms.texture, 0);
153+
154+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
155+
requestAnimationFrame(render);
156+
};
157+
render();
158+
});
159+
</script>
160+
161+
<style scoped>
162+
.glass-panel {
163+
position: relative;
164+
display: inline-block;
165+
border-radius: 1.5rem;
166+
overflow: hidden;
167+
backdrop-filter: blur(10px);
168+
-webkit-backdrop-filter: blur(10px);
169+
}
170+
171+
.glass-panel canvas {
172+
position: absolute;
173+
inset: 0;
174+
width: 100%;
175+
height: 100%;
176+
pointer-events: none;
177+
}
178+
179+
.glass-panel .content {
180+
position: relative;
181+
z-index: 1;
182+
pointer-events: auto;
183+
color: white;
184+
padding: 2rem;
185+
}
186+
</style>

jgantts-com/src/components/Island.vue

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
<script setup lang="ts">
2-
import { ref, onMounted, defineProps, } from 'vue';
2+
import { ref, onMounted, defineProps, inject } from 'vue';
3+
import GlassPanel from './GlassPanel.vue';
34
45
const props = defineProps<{
56
cornerRadius: String;
67
}>()
8+
79
</script>
810

911
<template>
10-
<div class="main shadow" :style="{ borderRadius: props.cornerRadius + '' }">
12+
<GlassPanel>
1113
<slot />
12-
</div>
14+
</GlassPanel>
1315
</template>
1416

1517
<style scoped>

jgantts-com/src/views/WelcomePage.vue

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,21 @@ import NavBar from '../components/NavBar.vue';
2424
<DStack :breakpoint="Breakpoint._2_M" vSpacing="1.0rem" hSpacing="1.0rem">
2525
<p class="text-h4" style="max-width: 15rem;">
2626
Hi, I'm<br />
27-
Jacob, a<br />
28-
professional<br />
29-
<span class="highlight">web</span> developer.
27+
Jacob.<br />
28+
<span class="highlight">I make</span><br />
29+
<span class="highlight">software.</span>
3030
</p>
3131
<VStack class="text-h5" id="text06" spacing="0.5rem">
32+
<div style="height:0.5rem"/>
33+
<p class="text-h4">
34+
What <span class="highlight">I do</span>:
35+
</p>
3236
<p>
33-
I craft websites tailored to <span class="highlight">your unique needs</span>, capable of doing anything you envision.
37+
I <span class="highlight">tailor</span> software to your unique requirements;<br/>
38+
<span class="highlight">making it capable</span> of doing what needs to be done.
3439
</p>
3540
<p>
36-
From handling technical complexities to ensuring <span>seamless interactions</span>, I make your site work flawlessly so you can <span class="highlight">focus on what matters</span> most.
41+
From <span class="highlight">handling technical complexities</span> to ensuring <span class="highlight">seamless interactions</span>, I make the software work <span class="highlight">so you can focus</span> on your business.
3742
</p>
3843
</VStack>
3944
</DStack>

0 commit comments

Comments
 (0)