Rebuild site with Hugo: unified dark theme, SVG logo, data-driven content

This commit is contained in:
2026-07-08 16:14:01 +08:00
parent 0eaec96b68
commit 1214e48733
47 changed files with 1169 additions and 2766 deletions
+77
View File
@@ -0,0 +1,77 @@
(function () {
const canvas = document.getElementById('drag-ball');
if (!canvas) return;
const ctx = canvas.getContext('2d');
const W = 200, H = 200;
canvas.width = W;
canvas.height = H;
let ballX = W / 2, ballY = H / 2;
let dragging = false;
let offsetX = 0, offsetY = 0;
function drawBall() {
ctx.clearRect(0, 0, W, H);
const r = 30;
const grad = ctx.createRadialGradient(ballX, ballY, 0, ballX, ballY, r);
grad.addColorStop(0, '#ffb300');
grad.addColorStop(1, '#ff6f00');
ctx.beginPath();
ctx.arc(ballX, ballY, r, 0, Math.PI * 2);
ctx.fillStyle = grad;
ctx.fill();
ctx.fillStyle = '#fff';
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('(¬‿¬)', ballX, ballY);
}
function getPos(e) {
const rect = canvas.getBoundingClientRect();
const scaleX = W / rect.width;
const scaleY = H / rect.height;
if (e.touches) {
return {
x: (e.touches[0].clientX - rect.left) * scaleX,
y: (e.touches[0].clientY - rect.top) * scaleY
};
}
return {
x: (e.clientX - rect.left) * scaleX,
y: (e.clientY - rect.top) * scaleY
};
}
canvas.addEventListener('mousedown', (e) => {
const pos = getPos(e);
const dist = Math.hypot(pos.x - ballX, pos.y - ballY);
if (dist < 30) { dragging = true; offsetX = pos.x - ballX; offsetY = pos.y - ballY; }
});
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
const pos = getPos(e);
const dist = Math.hypot(pos.x - ballX, pos.y - ballY);
if (dist < 30) { dragging = true; offsetX = pos.x - ballX; offsetY = pos.y - ballY; }
}, { passive: false });
window.addEventListener('mousemove', (e) => {
if (!dragging) return;
const pos = getPos(e);
ballX = Math.max(30, Math.min(W - 30, pos.x - offsetX));
ballY = Math.max(30, Math.min(H - 30, pos.y - offsetY));
drawBall();
});
window.addEventListener('touchmove', (e) => {
if (!dragging) return;
const pos = getPos(e);
ballX = Math.max(30, Math.min(W - 30, pos.x - offsetX));
ballY = Math.max(30, Math.min(H - 30, pos.y - offsetY));
drawBall();
});
window.addEventListener('mouseup', () => { dragging = false; });
window.addEventListener('touchend', () => { dragging = false; });
drawBall();
})();
+68
View File
@@ -0,0 +1,68 @@
(function () {
const canvas = document.getElementById('star-canvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
let w, h;
function resize() {
w = canvas.width = canvas.parentElement.clientWidth;
h = canvas.height = canvas.parentElement.clientHeight;
}
resize();
window.addEventListener('resize', resize);
const STAR_COUNT = 300;
const colors = ['#ffffff', '#e0f7fa', '#fff9c4', '#fce4ec', '#4fc3f7'];
const stars = [];
for (let i = 0; i < STAR_COUNT; i++) {
stars.push({
x: Math.random() * w,
y: Math.random() * h,
baseR: Math.random() * 1.5 + 0.3,
phase: Math.random() * Math.PI * 2,
speed: Math.random() * 0.02 + 0.005,
color: colors[Math.floor(Math.random() * colors.length)],
glow: Math.random() > 0.7
});
}
function draw() {
ctx.clearRect(0, 0, w, h);
const t = performance.now() * 0.001;
for (const s of stars) {
const r = s.baseR + Math.sin(t * s.speed * 10 + s.phase) * s.baseR * 0.6;
const alpha = 0.4 + Math.sin(t * s.speed * 8 + s.phase) * 0.35;
ctx.globalAlpha = Math.max(0.1, alpha);
ctx.beginPath();
ctx.arc(s.x, s.y, Math.max(0.1, r), 0, Math.PI * 2);
ctx.fillStyle = s.color;
ctx.fill();
if (s.glow) {
const gr = ctx.createRadialGradient(s.x, s.y, 0, s.x, s.y, r * 3);
gr.addColorStop(0, s.color);
gr.addColorStop(1, 'transparent');
ctx.globalAlpha = alpha * 0.15;
ctx.beginPath();
ctx.arc(s.x, s.y, r * 3, 0, Math.PI * 2);
ctx.fillStyle = gr;
ctx.fill();
}
}
ctx.globalAlpha = 1;
requestAnimationFrame(draw);
}
draw();
// reposition on resize
window.addEventListener('resize', () => {
for (const s of stars) {
s.x = (s.x / w) * canvas.width;
s.y = (s.y / h) * canvas.height;
}
});
})();