Skip to content

Commit 692c60b

Browse files
committed
chore: restore shot.mp3 sound effect and commit local about page enhancements
1 parent e321763 commit 692c60b

3 files changed

Lines changed: 253 additions & 3 deletions

File tree

assets/audio/contact/shot.mp3

16.6 KB
Binary file not shown.

assets/css/about.css

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,56 @@ html {
2626
height: 100%;
2727
}
2828

29+
.section > :not(.pixel-trail-canvas):not(.pixel-rain-canvas) {
30+
position: relative;
31+
z-index: 2;
32+
}
33+
34+
#main {
35+
overflow: hidden;
36+
background: #777d8c;
37+
}
38+
39+
#main::before {
40+
content: '';
41+
position: absolute;
42+
inset: 0;
43+
background-image:
44+
linear-gradient(rgba(255, 255, 255, 0.035) 1px, transparent 1px),
45+
linear-gradient(90deg, rgba(255, 255, 255, 0.035) 1px, transparent 1px);
46+
background-size: 22px 22px;
47+
opacity: 0.45;
48+
pointer-events: none;
49+
}
50+
51+
#main img {
52+
position: relative;
53+
z-index: 2;
54+
filter: drop-shadow(0 18px 0 rgba(62, 82, 109, 0.45));
55+
}
56+
57+
.pixel-trail-canvas {
58+
position: absolute;
59+
inset: 0;
60+
width: 100%;
61+
height: 100%;
62+
pointer-events: none;
63+
z-index: 1;
64+
image-rendering: pixelated;
65+
image-rendering: crisp-edges;
66+
}
67+
68+
.pixel-rain-canvas {
69+
position: absolute;
70+
inset: 0;
71+
width: 100%;
72+
height: 100%;
73+
pointer-events: none;
74+
z-index: 0;
75+
image-rendering: pixelated;
76+
image-rendering: crisp-edges;
77+
}
78+
2979
.z-1 {
3080
z-index: 1;
3181
}
@@ -103,4 +153,4 @@ html {
103153

104154
.nes-btn:hover {
105155
background-color: #0D47A1;
106-
}
156+
}

assets/js/about.js

Lines changed: 202 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,208 @@ document.addEventListener('DOMContentLoaded', () => {
1818
});
1919
});
2020

21+
document.addEventListener('DOMContentLoaded', () => {
22+
const sections = document.querySelectorAll('.section');
23+
const palette = ['#f8fafc', '#c7d2fe', '#93c5fd', '#a3e635', '#facc15'];
24+
const pixelSize = 8;
25+
const maxPixels = 140;
26+
27+
sections.forEach((section) => {
28+
const canvas = document.createElement('canvas');
29+
canvas.className = 'pixel-trail-canvas';
30+
canvas.setAttribute('aria-hidden', 'true');
31+
section.prepend(canvas);
32+
33+
const ctx = canvas.getContext('2d');
34+
if (!ctx) return;
35+
36+
const pixels = [];
37+
let animationFrame = null;
38+
let dpr = Math.min(window.devicePixelRatio || 1, 2);
39+
let lastSpawnAt = 0;
40+
41+
function resizeCanvas() {
42+
const bounds = section.getBoundingClientRect();
43+
dpr = Math.min(window.devicePixelRatio || 1, 2);
44+
canvas.width = Math.floor(bounds.width * dpr);
45+
canvas.height = Math.floor(bounds.height * dpr);
46+
ctx.setTransform(1, 0, 0, 1, 0, 0);
47+
ctx.scale(dpr, dpr);
48+
}
49+
50+
function spawnPixel(clientX, clientY) {
51+
const bounds = section.getBoundingClientRect();
52+
const x = clientX - bounds.left;
53+
const y = clientY - bounds.top;
54+
55+
for (let i = 0; i < 3; i++) {
56+
pixels.push({
57+
x: x + (Math.random() - 0.5) * 18,
58+
y: y + (Math.random() - 0.5) * 18,
59+
vx: (Math.random() - 0.5) * 1.4,
60+
vy: (Math.random() - 0.5) * 1.4 - 0.3,
61+
life: 1,
62+
fade: 0.018 + Math.random() * 0.02,
63+
size: pixelSize + Math.floor(Math.random() * 2) * 4,
64+
color: palette[Math.floor(Math.random() * palette.length)]
65+
});
66+
}
67+
68+
if (pixels.length > maxPixels) {
69+
pixels.splice(0, pixels.length - maxPixels);
70+
}
71+
72+
if (!animationFrame) {
73+
animationFrame = requestAnimationFrame(renderPixels);
74+
}
75+
}
76+
77+
function renderPixels() {
78+
const width = canvas.width / dpr;
79+
const height = canvas.height / dpr;
80+
ctx.clearRect(0, 0, width, height);
81+
82+
for (let i = pixels.length - 1; i >= 0; i--) {
83+
const pixel = pixels[i];
84+
pixel.x += pixel.vx;
85+
pixel.y += pixel.vy;
86+
pixel.vy += 0.01;
87+
pixel.life -= pixel.fade;
88+
89+
if (pixel.life <= 0) {
90+
pixels.splice(i, 1);
91+
continue;
92+
}
93+
94+
ctx.globalAlpha = pixel.life;
95+
ctx.fillStyle = pixel.color;
96+
ctx.fillRect(
97+
Math.round(pixel.x / pixelSize) * pixelSize,
98+
Math.round(pixel.y / pixelSize) * pixelSize,
99+
pixel.size,
100+
pixel.size
101+
);
102+
}
103+
104+
ctx.globalAlpha = 1;
105+
106+
if (pixels.length > 0) {
107+
animationFrame = requestAnimationFrame(renderPixels);
108+
} else {
109+
animationFrame = null;
110+
}
111+
}
112+
113+
function handlePointer(clientX, clientY) {
114+
const now = performance.now();
115+
if (now - lastSpawnAt < 22) return;
116+
lastSpawnAt = now;
117+
spawnPixel(clientX, clientY);
118+
}
119+
120+
section.addEventListener('mousemove', (event) => {
121+
handlePointer(event.clientX, event.clientY);
122+
});
123+
124+
section.addEventListener('touchmove', (event) => {
125+
const touch = event.touches[0];
126+
if (!touch) return;
127+
handlePointer(touch.clientX, touch.clientY);
128+
}, { passive: true });
129+
130+
window.addEventListener('resize', resizeCanvas);
131+
resizeCanvas();
132+
});
133+
});
134+
135+
document.addEventListener('DOMContentLoaded', () => {
136+
const mainSection = document.getElementById('main');
137+
if (!mainSection) return;
138+
139+
const canvas = document.createElement('canvas');
140+
canvas.className = 'pixel-rain-canvas';
141+
canvas.setAttribute('aria-hidden', 'true');
142+
mainSection.prepend(canvas);
143+
144+
const ctx = canvas.getContext('2d');
145+
if (!ctx) return;
146+
147+
const drops = [];
148+
const dropCount = 52;
149+
const palette = ['#c7d2fe', '#93c5fd', '#f8fafc', '#a3e635'];
150+
let dpr = Math.min(window.devicePixelRatio || 1, 2);
151+
let width = 0;
152+
let height = 0;
153+
154+
function resizeCanvas() {
155+
const bounds = mainSection.getBoundingClientRect();
156+
dpr = Math.min(window.devicePixelRatio || 1, 2);
157+
width = bounds.width;
158+
height = bounds.height;
159+
canvas.width = Math.floor(width * dpr);
160+
canvas.height = Math.floor(height * dpr);
161+
ctx.setTransform(1, 0, 0, 1, 0, 0);
162+
ctx.scale(dpr, dpr);
163+
}
164+
165+
function resetDrop(drop, initial = false) {
166+
const startBand = Math.max(width, height) * 0.35;
167+
drop.x = initial ? Math.random() * width : (Math.random() * (width + startBand)) - startBand;
168+
drop.y = initial ? Math.random() * height : -Math.random() * (height * 0.45 + 80);
169+
drop.vx = 0.9 + Math.random() * 1.4;
170+
drop.vy = 1.3 + Math.random() * 1.9;
171+
drop.length = 10 + Math.floor(Math.random() * 4) * 4;
172+
drop.size = 4 + Math.floor(Math.random() * 2) * 4;
173+
drop.alpha = 0.15 + Math.random() * 0.22;
174+
drop.color = palette[Math.floor(Math.random() * palette.length)];
175+
}
176+
177+
function initDrops() {
178+
drops.length = 0;
179+
for (let i = 0; i < dropCount; i++) {
180+
const drop = {};
181+
resetDrop(drop, true);
182+
drops.push(drop);
183+
}
184+
}
185+
186+
function renderRain() {
187+
ctx.clearRect(0, 0, width, height);
188+
189+
for (const drop of drops) {
190+
drop.x += drop.vx;
191+
drop.y += drop.vy;
192+
193+
if (drop.y - drop.length > height || drop.x - drop.length > width) {
194+
resetDrop(drop, false);
195+
}
196+
197+
ctx.globalAlpha = drop.alpha;
198+
ctx.fillStyle = drop.color;
199+
200+
for (let segment = 0; segment < 3; segment++) {
201+
const segX = Math.round((drop.x - segment * drop.length) / 8) * 8;
202+
const segY = Math.round((drop.y - segment * drop.length) / 8) * 8;
203+
const segAlpha = drop.alpha * (1 - segment * 0.28);
204+
ctx.globalAlpha = segAlpha;
205+
ctx.fillRect(segX, segY, drop.size, drop.size);
206+
}
207+
}
208+
209+
ctx.globalAlpha = 1;
210+
requestAnimationFrame(renderRain);
211+
}
212+
213+
window.addEventListener('resize', () => {
214+
resizeCanvas();
215+
initDrops();
216+
});
217+
218+
resizeCanvas();
219+
initDrops();
220+
requestAnimationFrame(renderRain);
221+
});
222+
21223
document.addEventListener('DOMContentLoaded', () => {
22224
const sections = document.querySelectorAll('.section');
23225
let currentSection = 'main';
@@ -196,5 +398,3 @@ document.getElementById('menu-toggle').addEventListener('click', function () {
196398
const menu = document.getElementById('mobile-menu');
197399
menu.classList.toggle('hidden');
198400
});
199-
200-

0 commit comments

Comments
 (0)