Files
whoami/homepage/index.html
T

576 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https: data:; script-src 'self' 'unsafe-inline'; connect-src https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https: *;"><link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='.9em' font-size='90'%3E🛰️%3C/text%3E%3C/svg%3E">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hyonglin.top</title>
<style>
:root {
--bg: #0d1b2a;
--card: rgba(255,255,255,.09);
--card-hover: rgba(255,255,255,.16);
--text: #f0f4fb;
--muted: #a8b6c9;
--accent: #7dd3fc;
--border: rgba(255,255,255,.12);
--shadow: 0 8px 30px rgba(0,0,0,.35);
--glass-blur: 12px;
}
@media (prefers-color-scheme: light) {
:root {
--bg: #eef4fb;
--card: rgba(255,255,255,.62);
--card-hover: rgba(255,255,255,.85);
--text: #15202e;
--muted: #5b6b7f;
--accent: #0e7490;
--border: rgba(20,40,70,.12);
--shadow: 0 8px 30px rgba(30,60,90,.12);
}
.search button { color: #fff; }
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
}
/* ===== 极光背景 ===== */
#aurora {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none;
}
.aurora-overlay {
position: fixed;
inset: 0;
z-index: 1;
pointer-events: none;
background: linear-gradient(
to bottom,
transparent 0%,
transparent 74%,
rgba(13,27,42,.3) 90%,
var(--bg) 100%
);
}
.wrap {
position: relative;
z-index: 2;
max-width: 960px;
margin: 0 auto;
padding: 40px 24px 48px;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* ===== 顶部:标题 + 时钟 + 天气 ===== */
header {
text-align: center;
margin-bottom: 34px;
}
header h1 {
font-size: 2.4rem;
font-weight: 700;
letter-spacing: -0.02em;
text-shadow: 0 2px 24px rgba(125,211,252,.25);
}
header p.sub {
color: var(--muted);
margin-top: 6px;
font-size: .95rem;
}
.hero-row {
display: flex;
justify-content: center;
gap: 28px;
margin-top: 26px;
flex-wrap: wrap;
}
.hero-box {
background: var(--card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 14px 22px;
min-width: 130px;
backdrop-filter: blur(var(--glass-blur));
-webkit-backdrop-filter: blur(var(--glass-blur));
box-shadow: var(--shadow);
}
.hero-box .label {
font-size: .72rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: .08em;
margin-bottom: 6px;
}
.hero-box .value {
font-size: 1.7rem;
font-weight: 650;
font-variant-numeric: tabular-nums;
line-height: 1.1;
}
.hero-box#clock .value { font-size: 1.9rem; }
.hero-box#weather .value { font-size: 1.25rem; display: flex; align-items: center; gap: 8px; }
.hero-box#weather .value .emoji { font-size: 1.5rem; }
/* ===== Bing 搜索框 ===== */
.search {
display: flex;
align-items: center;
gap: 10px;
max-width: 520px;
margin: 26px auto 0;
padding: 6px 6px 6px 18px;
background: var(--card);
border: 1px solid var(--border);
border-radius: 999px;
backdrop-filter: blur(var(--glass-blur));
-webkit-backdrop-filter: blur(var(--glass-blur));
box-shadow: var(--shadow);
transition: border-color .16s ease;
}
.search:focus-within { border-color: var(--accent); }
.search .sicon { font-size: 1rem; opacity: .75; }
.search input {
flex: 1;
min-width: 0;
background: none;
border: none;
outline: none;
color: var(--text);
font-size: .95rem;
padding: 8px 0;
}
.search input::placeholder { color: var(--muted); }
.search button {
border: none;
cursor: pointer;
padding: 9px 22px;
border-radius: 999px;
font-size: .9rem;
font-weight: 600;
color: #06202e;
background: var(--accent);
flex-shrink: 0;
}
/* ===== 链接网格(多列) ===== */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 18px;
margin-top: 8px;
}
.col h2 {
font-size: .8rem;
font-weight: 600;
color: var(--muted);
letter-spacing: .1em;
margin: 0 0 12px 4px;
display: flex;
align-items: center;
gap: 8px;
}
ul.links { list-style: none; display: flex; flex-direction: column; gap: 10px; }
ul.links a {
display: flex;
align-items: center;
gap: 12px;
padding: 13px 15px;
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
text-decoration: none;
color: var(--text);
backdrop-filter: blur(var(--glass-blur));
-webkit-backdrop-filter: blur(var(--glass-blur));
box-shadow: var(--shadow);
transition: transform .16s ease, border-color .16s ease, background .16s ease;
}
ul.links a:hover {
transform: translateY(-3px);
background: var(--card-hover);
border-color: var(--accent);
}
.icon {
font-size: 1.3rem;
width: 38px; height: 38px;
display: flex; align-items: center; justify-content: center;
background: color-mix(in srgb, var(--accent) 16%, transparent);
border-radius: 10px;
flex-shrink: 0;
}
.meta { display: flex; flex-direction: column; min-width: 0; }
.meta .name { font-weight: 600; font-size: .95rem; }
.meta .desc { color: var(--muted); font-size: .78rem; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
footer {
margin-top: auto;
text-align: center;
color: var(--muted);
font-size: .8rem;
padding-top: 40px;
}
/* ===== 响应式 ===== */
@media (max-width: 820px) {
.grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
.grid { grid-template-columns: 1fr; }
header h1 { font-size: 2rem; }
.hero-box .value { font-size: 1.4rem; }
.hero-box#clock .value { font-size: 1.6rem; }
}
</style>
</head>
<body>
<canvas id="aurora"></canvas>
<div class="aurora-overlay"></div>
<main class="wrap">
<header>
<h1>hyonglin.top</h1>
<p class="sub">个人服务导航 · 随时可达</p>
<div class="hero-row">
<div class="hero-box" id="weather">
<div class="label">天气</div>
<div class="value"><span class="emoji">☀️</span><span class="wtxt">获取中…</span></div>
<div class="wcity" style="font-size:.72rem;color:var(--muted);margin-top:4px"></div>
</div>
<div class="hero-box" id="date">
<div class="label">日期</div>
<div class="value" id="dateval">----</div>
</div>
<div class="hero-box" id="clock">
<div class="label">时间</div>
<div class="value" id="clockval">--:--:--</div>
</div>
</div>
<form class="search" action="https://www.bing.com/search" method="get" target="_blank" rel="noopener">
<span class="sicon">🔍</span>
<input type="text" name="q" placeholder="Bing 搜索…" autocomplete="off">
<button type="submit">搜索</button>
</form>
</header>
<div class="grid">
<div class="col">
<h2>📡 自建服务</h2>
<ul class="links">
<li>
<a href="https://note.hyonglin.top">
<span class="icon">📝</span>
<span class="meta">
<span class="name">笔记</span>
<span class="desc">note.hyonglin.top · SilverBullet</span>
</span>
</a>
</li>
<li>
<a href="https://git.hyonglin.top">
<span class="icon">📦</span>
<span class="meta">
<span class="name">Git 服务</span>
<span class="desc">git.hyonglin.top · Gitea</span>
</span>
</a>
</li>
<li>
<a href="https://ssh.hyonglin.top">
<span class="icon">🖥️</span>
<span class="meta">
<span class="name">网页 SSH 终端</span>
<span class="desc">ssh.hyonglin.top</span>
</span>
</a>
</li>
<li>
<a href="https://frp.hyonglin.top">
<span class="icon">🔗</span>
<span class="meta">
<span class="name">frp 管理面板</span>
<span class="desc">frp.hyonglin.top</span>
</span>
</a>
</li>
</ul>
</div>
<div class="col">
<h2>🤖 AI 助手</h2>
<ul class="links">
<li>
<a href="https://chat.deepseek.com/" target="_blank" rel="noopener">
<span class="icon">🤖</span>
<span class="meta">
<span class="name">DeepSeek</span>
<span class="desc">chat.deepseek.com</span>
</span>
</a>
</li>
<li>
<a href="https://www.doubao.com/chat/" target="_blank" rel="noopener">
<span class="icon">💬</span>
<span class="meta">
<span class="name">豆包</span>
<span class="desc">doubao.com</span>
</span>
</a>
</li>
</ul>
</div>
<div class="col">
<h2>☁️ 云服务</h2>
<ul class="links">
<li>
<a href="https://dash.cloudflare.com/" target="_blank" rel="noopener">
<span class="icon">🌐</span>
<span class="meta">
<span class="name">Cloudflare</span>
<span class="desc">dash.cloudflare.com</span>
</span>
</a>
</li>
<li>
<a href="https://auth.huaweicloud.com/" target="_blank" rel="noopener">
<span class="icon">☁️</span>
<span class="meta">
<span class="name">华为云</span>
<span class="desc">auth.huaweicloud.com</span>
</span>
</a>
</li>
<li>
<a href="https://console.cloud.tencent.com/" target="_blank" rel="noopener">
<span class="icon">🐧</span>
<span class="meta">
<span class="name">腾讯云</span>
<span class="desc">console.cloud.tencent.com</span>
</span>
</a>
</li>
</ul>
</div>
<div class="col">
<h2>🔧 常用</h2>
<ul class="links">
<li>
<a href="https://wx.mail.qq.com/" target="_blank" rel="noopener">
<span class="icon">📧</span>
<span class="meta">
<span class="name">QQ 邮箱</span>
<span class="desc">wx.mail.qq.com</span>
</span>
</a>
</li>
<li>
<a href="https://www.bing.com/" target="_blank" rel="noopener">
<span class="icon">🔍</span>
<span class="meta">
<span class="name">Bing 搜索</span>
<span class="desc">bing.com</span>
</span>
</a>
</li>
<li>
<a href="https://www.bilibili.com/" target="_blank" rel="noopener">
<span class="icon">📺</span>
<span class="meta">
<span class="name">哔哩哔哩</span>
<span class="desc">bilibili.com</span>
</span>
</a>
</li>
</ul>
</div>
</div>
<footer>hyonglin.top</footer>
</main>
<script>
// 用 try/catch 包裹:老浏览器没 addEventListener 时报这一行也不影响后续
var mql = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)');
if (mql && mql.addEventListener) {
mql.addEventListener('change', function(){});
} else if (mql && mql.addListener) {
mql.addListener(function(){});
}
/* ===== 夜晚星空:闪烁星星 + 流星 + 鼠标视差,无模糊 ===== */
var aurora = document.getElementById('aurora');
var ctx = aurora.getContext('2d');
var W, H, stars = [], meteors = [];
var mouse = { x: -9999, y: -9999, px: -9999, py: -9999 };
var nextMeteor = 2; // 页面打开 2 秒后来第一颗流星
function resize() {
W = aurora.width = window.innerWidth;
H = aurora.height = window.innerHeight;
// 星星:锐利小像素点,明暗闪烁,少量亮星带十字芒
stars = [];
var scount = Math.min(240, Math.round(W * H / 8500));
for (var j = 0; j < scount; j++) {
stars.push({
x: Math.random() * W,
y: Math.random() * H * 0.9,
r: 0.6 + Math.random() * 1.3,
phase: Math.random() * Math.PI * 2,
tw: 0.6 + Math.random() * 2.2, // 闪烁速度
depth: 0.3 + Math.random() * 0.7, // 视差深度
cross: Math.random() < 0.08 // 少量亮星画十字芒
});
}
}
function spawnMeteor(t) {
var ang = Math.PI * (0.70 + Math.random() * 0.14); // 斜向左下
var sp = 9 + Math.random() * 6;
meteors.push({
x: W * (0.3 + Math.random() * 0.7),
y: Math.random() * H * 0.3,
vx: Math.cos(ang) * sp,
vy: -Math.sin(ang) * sp,
born: t,
life: 0.9 + Math.random() * 0.6
});
}
function frame() {
var t = Date.now() / 1000;
// --- 夜空底色 ---
ctx.globalCompositeOperation = 'source-over';
var sky = ctx.createLinearGradient(0, 0, 0, H);
sky.addColorStop(0, '#01030a');
sky.addColorStop(0.45, '#050d1d');
sky.addColorStop(1, '#0a1a2e');
ctx.fillStyle = sky;
ctx.fillRect(0, 0, W, H);
// --- 星星:闪烁 + 轻微视差(随鼠标反向漂移),fillRect 边缘锐利 ---
var px = (mouse.x - W / 2) * -0.012;
var py = (mouse.y - H / 2) * -0.012;
for (var j = 0; j < stars.length; j++) {
var s = stars[j];
var a = 0.35 + 0.65 * (0.5 + 0.5 * Math.sin(t * s.tw + s.phase));
var sx = s.x + px * s.depth * 8;
var sy = s.y + py * s.depth * 8;
ctx.fillStyle = 'rgba(255,255,255,' + a.toFixed(3) + ')';
ctx.fillRect(sx, sy, s.r, s.r);
if (s.cross) { // 亮星十字芒:细直线,无模糊
ctx.strokeStyle = 'rgba(255,255,255,' + (a * 0.6).toFixed(3) + ')';
ctx.lineWidth = 1;
var cl = s.r * 3.2;
ctx.beginPath();
ctx.moveTo(sx - cl, sy + s.r / 2); ctx.lineTo(sx + s.r + cl, sy + s.r / 2);
ctx.moveTo(sx + s.r / 2, sy - cl); ctx.lineTo(sx + s.r / 2, sy + s.r + cl);
ctx.stroke();
}
}
// --- 流星:每 3~9 秒一颗,锐利线段拖尾 ---
if (t > nextMeteor) { spawnMeteor(t); nextMeteor = t + 3 + Math.random() * 6; }
for (var m = meteors.length - 1; m >= 0; m--) {
var mt = meteors[m];
var age = t - mt.born;
if (age > mt.life) { meteors.splice(m, 1); continue; }
mt.x += mt.vx;
mt.y += mt.vy;
var fade = 1 - age / mt.life;
ctx.strokeStyle = 'rgba(200,240,255,' + (0.9 * fade).toFixed(3) + ')';
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(mt.x, mt.y);
ctx.lineTo(mt.x - mt.vx * 14, mt.y - mt.vy * 14);
ctx.stroke();
ctx.fillStyle = 'rgba(255,255,255,' + fade.toFixed(3) + ')';
ctx.fillRect(mt.x, mt.y, 2, 2);
}
requestAnimationFrame(frame);
}
window.addEventListener('resize', resize);
window.addEventListener('mousemove', function(e) { mouse.x = e.clientX; mouse.y = e.clientY; });
resize();
frame();
/* ===== 日期 + 时钟 ===== */
var week = ['日','一','二','三','四','五','六'];
function pad2(n) { return (n < 10 ? '0' : '') + n; }
function updateClock() {
var now = new Date();
var c = document.getElementById('clockval');
if (c) c.textContent = pad2(now.getHours()) + ':' + pad2(now.getMinutes()) + ':' + pad2(now.getSeconds());
var d = document.getElementById('dateval');
if (d) d.textContent = now.getFullYear() + '-' + pad2(now.getMonth()+1) + '-' + pad2(now.getDate()) + ' 周' + week[now.getDay()];
}
setInterval(updateClock, 1000);
updateClock();
/* ===== 天气:GPS 定位 + Open-Meteo(不依赖 IP) ===== */
var emojiBox = function(){ var e = document.querySelector('#weather .emoji'); return e; };
var tempBox = function(){ return document.querySelector('#weather .wtxt'); };
var cityBox = function(){ return document.querySelector('.wcity'); };
var WMO = {
0:'☀️',1:'🌤',2:'⛅',3:'☁️',45:'🌫',48:'🌫',
51:'🌦',53:'🌦',55:'🌦',56:'🌨',57:'🌨',
61:'🌧',63:'🌧',65:'🌧',66:'🌨',67:'🌨',
71:'🌨',73:'🌨',75:'🌨',77:'🌨',80:'🌦',81:'🌦',82:'🌧',
85:'🌨',86:'🌨',95:'⛈',96:'⛈',99:'⛈'
};
function wmo(w) { return WMO[w] || '🌡️'; }
function setWeather(temp, wcode, city) {
if (tempBox()) tempBox().textContent = temp + '°C';
if (emojiBox()) emojiBox().textContent = wmo(wcode);
if (city && cityBox()) cityBox().textContent = city;
}
function getWeather(lat, lon, city) {
var url = 'https://api.open-meteo.com/v1/forecast?latitude=' + lat + '&longitude=' + lon +
'&current_weather=true&timezone=auto';
fetch(url).then(function(r){ return r.json(); }).then(function(d){
var cw = d.current_weather || {};
setWeather(Math.round(cw.temperature), cw.weathercode, city);
}).catch(function(){ tempBox() && (tempBox().textContent = '天气获取失败'); });
}
// 优先浏览器 GPS 定位;失败则 ipwho.is 按 IP 定位;再失败显示默认城市(北京)
var DEFAULT_LOC = { lat: 39.9042, lon: 116.4074, city: '北京' };
function byIP() {
fetch('https://ipwho.is/').then(function(r){ return r.json(); }).then(function(d){
if (d && d.latitude && d.longitude) getWeather(d.latitude, d.longitude, d.city || '');
else { getWeather(DEFAULT_LOC.lat, DEFAULT_LOC.lon, DEFAULT_LOC.city); }
}).catch(function(){ getWeather(DEFAULT_LOC.lat, DEFAULT_LOC.lon, DEFAULT_LOC.city); });
}
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(pos){
getWeather(pos.coords.latitude, pos.coords.longitude, '');
}, byIP, { timeout: 8000 });
} else {
byIP();
}
</script>
</body>
</html>