Skip to content

Commit 76029d3

Browse files
committed
feat: update single QR desktop layout
1 parent aadc862 commit 76029d3

File tree

5 files changed

+453
-229
lines changed

5 files changed

+453
-229
lines changed
79.4 KB
Loading

src/assets/customTheme.css

+16-10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/* Root */
2+
@media (color-gamut: p3) {}
3+
14
main {
25
min-height: 63vh;
36
}
@@ -42,6 +45,10 @@ article:has(>:first-child:is(.images)) {
4245
gap: var(--pico-block-spacing-horizontal);
4346
}
4447

48+
.reverse> :first-child {
49+
order: 2;
50+
}
51+
4552
.three-column-grid {
4653
grid-template-columns: repeat(3, 1fr);
4754
gap: var(--pico-block-spacing-horizontal);
@@ -53,6 +60,15 @@ article:has(>:first-child:is(.images)) {
5360
.three-column-grid {
5461
display: grid;
5562
}
63+
64+
65+
.sticky-on-desktop {
66+
position: sticky !important;
67+
top: 0;
68+
z-index: 2;
69+
padding-top: 1rem;
70+
padding-bottom: 1rem;
71+
}
5672
}
5773

5874
.mobile-grid {
@@ -248,16 +264,6 @@ textarea.unset-width {
248264
min-width: fit-content;
249265
}
250266

251-
.sticky {
252-
position: sticky;
253-
top: 0;
254-
background: var(--pico-background-color);
255-
margin-top: calc(var(--pico-nav-element-spacing-vertical) * -1);
256-
z-index: 2;
257-
padding-bottom: 1rem;
258-
;
259-
}
260-
261267
.flex-start {
262268
align-items: flex-start;
263269
}

src/components/BackgroundConfetti.vue

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<script setup lang="ts">
2+
import { onMounted, ref } from "vue";
3+
4+
const props = defineProps({
5+
fireOnLoad: {
6+
type: Boolean,
7+
default: true,
8+
},
9+
});
10+
11+
interface ConfettoType {
12+
randomModifier: number;
13+
color: { front: string; back: string };
14+
dimensions: { x: number; y: number };
15+
position: { x: number; y: number };
16+
rotation: number;
17+
scale: { x: number; y: number };
18+
velocity: { x: number; y: number };
19+
update: () => void;
20+
}
21+
22+
/// Confetti stuff
23+
// ammount to add on each button press
24+
const confettiCount = 200;
25+
const gravityConfetti = 0.2;
26+
const dragConfetti = 0.075;
27+
const terminalVelocity = 8;
28+
29+
const canvas = ref<HTMLCanvasElement | null>(null);
30+
const confetti: ConfettoType[] = [];
31+
32+
const colors = [
33+
{ front: "oklch(63.25% 0.28 24.75)", back: "oklch(56.91% 0.26 28.12)" }, // Purple
34+
{ front: "oklch(53.23% 0.2 252.37)", back: "oklch(45.48% 0.17 252.22)" }, // Light Blue
35+
{ front: "#fff", back: "#f3f3f3" }, // Light Blue
36+
];
37+
38+
const randomRange = (min: number, max: number) =>
39+
Math.random() * (max - min) + min;
40+
41+
const initConfettoVelocity = (xRange: number[], yRange: number[]) => {
42+
const x = randomRange(xRange[0], xRange[1]);
43+
const range = yRange[1] - yRange[0] + 1;
44+
let y =
45+
yRange[1] - Math.abs(randomRange(0, range) + randomRange(0, range) - range);
46+
if (y >= yRange[1] - 1) {
47+
y += Math.random() < 0.25 ? randomRange(1, 3) : 0;
48+
}
49+
return { x, y: -y };
50+
};
51+
interface Dimensions {
52+
x: number;
53+
y: number;
54+
}
55+
56+
interface Position {
57+
x: number;
58+
y: number;
59+
}
60+
61+
interface Scale {
62+
x: number;
63+
y: number;
64+
}
65+
66+
interface Velocity {
67+
x: number;
68+
y: number;
69+
}
70+
71+
class Confetto {
72+
randomModifier: number;
73+
color: { front: string; back: string };
74+
dimensions: Dimensions;
75+
position: Position;
76+
rotation: number;
77+
scale: Scale;
78+
velocity: Velocity;
79+
80+
constructor() {
81+
this.randomModifier = randomRange(0, 99);
82+
this.color = colors[Math.floor(randomRange(0, colors.length))];
83+
this.dimensions = { x: randomRange(5, 9), y: randomRange(8, 15) };
84+
this.position = {
85+
x: randomRange(
86+
canvas.value!.width / 2 - canvas.value!.offsetWidth / 4,
87+
canvas.value!.width / 2 + canvas.value!.offsetWidth / 4
88+
),
89+
y: randomRange(canvas.value!.height, 0),
90+
};
91+
this.rotation = randomRange(0, 2 * Math.PI);
92+
this.scale = { x: 1, y: 1 };
93+
this.velocity = initConfettoVelocity([-9, 9], [6, 11]);
94+
}
95+
96+
update(): void {
97+
this.velocity.x -= this.velocity.x * dragConfetti;
98+
this.velocity.y = Math.min(
99+
this.velocity.y + gravityConfetti,
100+
terminalVelocity
101+
);
102+
this.velocity.x += Math.random() > 0.5 ? Math.random() : -Math.random();
103+
104+
this.position.x += this.velocity.x;
105+
this.position.y += this.velocity.y;
106+
107+
this.scale.y = Math.cos((this.position.y + this.randomModifier) * 0.09);
108+
}
109+
}
110+
111+
const initBurst = () => {
112+
for (let i = 0; i < confettiCount; i++) {
113+
confetti.push(new Confetto());
114+
}
115+
};
116+
117+
const render = () => {
118+
if (!canvas.value) return;
119+
120+
const ctx = canvas.value.getContext("2d");
121+
if (!ctx) return;
122+
123+
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
124+
125+
confetti.forEach((confetto, index) => {
126+
const width = confetto.dimensions.x * confetto.scale.x;
127+
const height = confetto.dimensions.y * confetto.scale.y;
128+
129+
ctx.translate(confetto.position.x, confetto.position.y);
130+
ctx.rotate(confetto.rotation);
131+
132+
confetto.update();
133+
134+
ctx.fillStyle =
135+
confetto.scale.y > 0 ? confetto.color.front : confetto.color.back;
136+
137+
ctx.fillRect(-width / 2, -height / 2, width, height);
138+
139+
ctx.setTransform(1, 0, 0, 1, 0, 0);
140+
});
141+
142+
confetti.forEach((confetto, index) => {
143+
if (!canvas.value) return;
144+
if (confetto.position.y >= canvas.value.height) confetti.splice(index, 1);
145+
});
146+
147+
window.requestAnimationFrame(render);
148+
};
149+
150+
const resizeCanvas = () => {
151+
if (!canvas.value || !canvas.value.parentElement) return;
152+
canvas.value.width = canvas.value.parentElement.clientWidth;
153+
canvas.value.height = canvas.value.parentElement.clientHeight;
154+
};
155+
156+
onMounted(() => {
157+
resizeCanvas();
158+
render();
159+
if (props.fireOnLoad) {
160+
initBurst();
161+
}
162+
});
163+
164+
// Expose so they can be triggered
165+
defineExpose({ initBurst });
166+
</script>
167+
<template>
168+
<canvas ref="canvas"></canvas>
169+
</template>
170+
<style scoped>
171+
/* Canvas takes up whole screen background */
172+
canvas {
173+
position: absolute;
174+
top: 0;
175+
left: 0;
176+
width: 100%;
177+
height: 100%;
178+
179+
/* disable / passthrough mouse events */
180+
pointer-events: none;
181+
opacity: 0.8;
182+
}
183+
</style>

0 commit comments

Comments
 (0)