Files
whoami/homepage/hud/index.html
T

412 lines
24 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 64 64'%3E%3Crect width='64' height='64' fill='%23050a14'/%3E%3Cpath d='M8 22V8h14M56 42v14H42' stroke='%2300f0ff' stroke-width='4' fill='none'/%3E%3Ctext x='22' y='44' font-family='monospace' font-size='22' fill='%23ff2d95'%3E%3E_%3C/text%3E%3C/svg%3E">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="dark">
<title>hyonglin.top</title>
<style>
:root {
--bg: #04070f;
--cyan: #00f0ff;
--mag: #ff2d95;
--text: #d6f3ff;
--muted: #5f8299;
--fill-a: rgba(7,15,30,.92);
--fill-b: rgba(4,8,18,.95);
--edge-soft: rgba(0,240,255,.35);
--mono: ui-monospace, "SF Mono", "Cascadia Code", "JetBrains Mono", Menlo, Consolas, monospace;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
font-family: var(--mono);
background:
radial-gradient(56% 38% at 50% -6%, rgba(0,240,255,.10), transparent 70%),
radial-gradient(50% 34% at 88% 108%, rgba(255,45,149,.09), transparent 70%),
var(--bg);
color: var(--text);
min-height: 100vh;
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
}
::selection { background: var(--cyan); color: #03121a; }
/* ===== 背景:细网格 + 扫描线 + 巡扫光带 ===== */
body::before {
content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none;
background-image:
linear-gradient(rgba(0,240,255,.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(0,240,255,.05) 1px, transparent 1px);
background-size: 44px 44px;
}
body::after {
content: ""; position: fixed; inset: 0; z-index: 7; pointer-events: none;
background: repeating-linear-gradient(0deg, rgba(0,240,255,.03) 0 1px, transparent 1px 3px);
}
.scanline {
position: fixed; left: 0; right: 0; top: -18%; height: 16%; z-index: 1; pointer-events: none;
background: linear-gradient(180deg, transparent, rgba(0,240,255,.07), transparent);
animation: sweepband 9s linear infinite;
}
@keyframes sweepband { from { top: -18%; } to { top: 118%; } }
/* ===== 四角坐标装饰文字 ===== */
.coords span { position: fixed; z-index: 2; font-size: .62rem; letter-spacing: .14em; color: rgba(0,240,255,.4); pointer-events: none; }
.coords .tl { top: 10px; left: 14px; }
.coords .tr { top: 10px; right: 14px; color: rgba(255,45,149,.45); }
.coords .bl { bottom: 10px; left: 14px; color: rgba(255,45,149,.4); }
.coords .br { bottom: 10px; right: 14px; }
.wrap { position: relative; z-index: 3; max-width: 1080px; margin: 0 auto; padding: 42px 20px 46px; }
.grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; }
/* ===== HUD 面板:clip-path 切角 + 霓虹描边(外框 1px 渐变) ===== */
.card {
position: relative; padding: 1px;
background: linear-gradient(135deg, rgba(0,240,255,.75), rgba(0,240,255,.16) 32%, rgba(255,45,149,.28) 72%, rgba(255,45,149,.6));
clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px));
animation: rise .55s cubic-bezier(.2,.7,.3,1) both;
animation-delay: var(--d, 0s);
}
.card > .in {
clip-path: polygon(0 0, calc(100% - 15px) 0, 100% 15px, 100% 100%, 15px 100%, 0 calc(100% - 15px));
background: linear-gradient(160deg, var(--fill-a), var(--fill-b));
padding: 16px 18px; position: relative; height: 100%;
}
/* 四角 L 形角标(单伪元素绘四个角) */
.card > .in::before {
content: ""; position: absolute; inset: 5px; pointer-events: none;
background:
linear-gradient(var(--cyan), var(--cyan)) top left / 12px 2px,
linear-gradient(var(--cyan), var(--cyan)) top left / 2px 12px,
linear-gradient(var(--cyan), var(--cyan)) top right / 12px 2px,
linear-gradient(var(--cyan), var(--cyan)) top right / 2px 12px,
linear-gradient(rgba(0,240,255,.6), rgba(0,240,255,.6)) bottom left / 12px 2px,
linear-gradient(rgba(0,240,255,.6), rgba(0,240,255,.6)) bottom left / 2px 12px,
linear-gradient(var(--mag), var(--mag)) bottom right / 12px 2px,
linear-gradient(var(--mag), var(--mag)) bottom right / 2px 12px;
background-repeat: no-repeat; opacity: .85;
}
@keyframes rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
/* ===== 头部:状态标签 + glitch 标题 ===== */
.head .in { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 26px 18px 22px; }
.tags { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; margin-bottom: 14px; }
.tag { display: inline-flex; align-items: center; gap: 7px; font-size: .68rem; letter-spacing: .18em; color: var(--cyan); border: 1px solid var(--edge-soft); padding: 4px 12px; }
.tag.dim { color: var(--muted); border-color: rgba(95,130,153,.3); }
.dot { width: 6px; height: 6px; background: var(--cyan); box-shadow: 0 0 8px var(--cyan); animation: blink 1.4s steps(2) infinite; }
@keyframes blink { 50% { opacity: .15; } }
h1.glitch {
position: relative; font-size: clamp(2rem, 5vw, 3rem); font-weight: 700;
letter-spacing: .04em; color: #eaffff;
text-shadow: 0 0 16px rgba(0,240,255,.4);
}
h1.glitch::before, h1.glitch::after { content: attr(data-text); position: absolute; left: 0; top: 0; width: 100%; pointer-events: none; }
h1.glitch::before { color: var(--cyan); opacity: .75; transform: translate(-2px, -1.5px); animation: gl1 3.4s steps(1) infinite; }
h1.glitch::after { color: var(--mag); opacity: .6; transform: translate(2px, 1.5px); animation: gl2 2.9s steps(1) infinite; }
@keyframes gl1 {
0%, 91% { clip-path: inset(0 0 0 0); transform: translate(-2px, -1.5px); }
92% { clip-path: inset(18% 0 58% 0); transform: translate(-7px, 0); }
94% { clip-path: inset(62% 0 8% 0); transform: translate(5px, -2px); }
96% { clip-path: inset(38% 0 40% 0); transform: translate(-5px, 1px); }
97%, 100% { clip-path: inset(0 0 0 0); transform: translate(-2px, -1.5px); }
}
@keyframes gl2 {
0%, 88% { clip-path: inset(0 0 0 0); transform: translate(2px, 1.5px); }
89% { clip-path: inset(56% 0 24% 0); transform: translate(7px, 1px); }
91% { clip-path: inset(8% 0 74% 0); transform: translate(-5px, -1px); }
93%, 100% { clip-path: inset(0 0 0 0); transform: translate(2px, 1.5px); }
}
.head p.sub { color: var(--muted); margin-top: 10px; font-size: .92rem; letter-spacing: .12em; }
/* ===== HUD 数据读数:天气 / 日期 / 时间 ===== */
.sky, .datecard, .clockcard { grid-column: span 2; }
.card .label { display: flex; justify-content: space-between; align-items: baseline; font-size: .7rem; letter-spacing: .18em; color: var(--muted); margin-bottom: 10px; text-transform: uppercase; }
.card .label .unit { font-size: .62rem; color: rgba(255,45,149,.75); letter-spacing: .1em; }
.sky .in { display: flex; flex-direction: column; justify-content: center; gap: 6px; align-items: center; text-align: center; }
.sky .emoji { font-size: 2.4rem; line-height: 1; filter: drop-shadow(0 0 10px rgba(0,240,255,.35)); }
.sky .temp { font-size: clamp(1.9rem, 3.6vw, 2.4rem); font-weight: 700; font-variant-numeric: tabular-nums; color: var(--cyan); text-shadow: 0 0 14px rgba(0,240,255,.45); }
.wcity { font-size: .76rem; color: var(--muted); min-height: 1.2em; letter-spacing: .08em; }
.datecard .in, .clockcard .in { display: flex; flex-direction: column; justify-content: center; text-align: center; }
.datecard .value { font-size: clamp(1.25rem, 2.6vw, 1.6rem); font-weight: 650; font-variant-numeric: tabular-nums; letter-spacing: .06em; color: var(--text); }
.clockcard .value { font-size: clamp(1.7rem, 3.4vw, 2.2rem); font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: .08em; color: #eaffff; text-shadow: 0 0 16px rgba(0,240,255,.5); }
/* ===== 搜索:命令输入行 ===== */
form.search { grid-column: span 6; }
form.search .in { display: flex; align-items: center; gap: 10px; padding: 8px 14px; transition: background .15s ease; }
form.search:focus-within .in { background: rgba(0,240,255,.05); }
form.search .ps { font-size: .82rem; color: var(--cyan); letter-spacing: .1em; white-space: nowrap; flex-shrink: 0; text-shadow: 0 0 8px rgba(0,240,255,.5); }
form.search select {
flex-shrink: 0; cursor: pointer;
font-family: var(--mono); font-size: .8rem; color: var(--cyan);
background: transparent; border: 1px solid var(--edge-soft);
padding: 7px 9px; outline: none; max-width: 128px;
}
form.search select:focus-visible { border-color: var(--cyan); }
form.search input { flex: 1; min-width: 0; background: none; border: none; outline: none; color: var(--text); font-family: var(--mono); font-size: .92rem; padding: 8px 0; caret-color: var(--cyan); }
form.search input::placeholder { color: #3d5a6e; }
form.search button {
border: none; cursor: pointer; flex-shrink: 0;
font-family: var(--mono); font-size: .82rem; font-weight: 700; letter-spacing: .14em;
color: #03121a; background: linear-gradient(120deg, var(--cyan), #6cf5ff);
padding: 9px 20px; clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px));
transition: filter .14s ease;
}
form.search button:hover { filter: brightness(1.15) drop-shadow(0 0 10px rgba(0,240,255,.6)); }
/* ===== 服务分组面板 ===== */
.panel { grid-column: span 3; }
.panel > .in > h2 { display: flex; align-items: center; gap: 9px; font-size: .88rem; font-weight: 700; letter-spacing: .1em; margin-bottom: 12px; color: #eaffff; }
.panel > .in > h2::before { content: "▞"; color: var(--cyan); text-shadow: 0 0 8px rgba(0,240,255,.7); }
.panel > .in > h2 .count { margin-left: auto; font-size: .68rem; color: var(--mag); letter-spacing: .1em; }
ul.links { list-style: none; display: flex; flex-direction: column; gap: 7px; }
ul.links a {
position: relative; overflow: hidden;
display: flex; align-items: center; gap: 10px;
padding: 9px 12px; text-decoration: none; color: var(--text);
border: 1px solid rgba(0,240,255,.16); background: rgba(3,9,18,.5);
transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
ul.links a::after {
content: ""; position: absolute; inset: 0; pointer-events: none;
background: linear-gradient(105deg, transparent 42%, rgba(0,240,255,.16) 50%, transparent 58%);
transform: translateX(-105%);
}
ul.links a:hover {
border-color: var(--cyan);
box-shadow: 0 0 14px rgba(0,240,255,.22), inset 0 0 12px rgba(0,240,255,.07);
transform: translateX(3px);
}
ul.links a:hover::after { animation: sweep .65s ease; }
@keyframes sweep { to { transform: translateX(105%); } }
ul.links .idx { font-size: .68rem; color: var(--muted); letter-spacing: .08em; flex-shrink: 0; transition: color .15s ease, text-shadow .15s ease; }
ul.links a:hover .idx { color: var(--cyan); text-shadow: 0 0 8px rgba(0,240,255,.7); }
ul.links .icon { font-size: 1rem; width: 30px; height: 30px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; border: 1px solid rgba(0,240,255,.2); background: rgba(0,240,255,.06); }
ul.links .meta { display: flex; flex-direction: column; min-width: 0; }
ul.links .name { font-weight: 650; font-size: .88rem; letter-spacing: .03em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
ul.links .desc { color: var(--muted); font-size: .72rem; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
ul.links .arr { margin-left: auto; flex-shrink: 0; color: var(--mag); font-size: .85rem; transition: transform .15s ease, text-shadow .15s ease; }
ul.links a:hover .arr { transform: translate(2px, -2px); text-shadow: 0 0 8px rgba(255,45,149,.8); }
/* ===== 页脚:版本切换 ===== */
footer.foot { grid-column: span 6; }
footer.foot .in { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; padding: 14px 18px; font-size: .74rem; color: var(--muted); letter-spacing: .06em; }
footer.foot .brand { color: #eaffff; text-shadow: 0 0 10px rgba(0,240,255,.35); }
footer.foot nav { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
footer.foot a { color: var(--muted); text-decoration: none; border-bottom: 1px solid transparent; transition: color .14s ease, border-color .14s ease; }
footer.foot a:hover { color: var(--cyan); border-color: var(--cyan); }
footer.foot i { font-style: normal; color: rgba(0,240,255,.35); }
footer.foot .cur { color: var(--mag); text-shadow: 0 0 8px rgba(255,45,149,.6); }
/* ===== 响应式:窄屏单列 ===== */
@media (max-width: 760px) {
.grid { grid-template-columns: 1fr; gap: 12px; }
.sky, .datecard, .clockcard, form.search, .panel, footer.foot { grid-column: auto; }
.wrap { padding: 30px 14px 36px; }
.coords span { display: none; }
}
/* ===== 减少动态效果:全部降级为静态 ===== */
@media (prefers-reduced-motion: reduce) {
.scanline { display: none; }
.card { animation: none; }
h1.glitch::before, h1.glitch::after { display: none; }
.dot { animation: none; }
ul.links a:hover::after { animation: none; opacity: 0; }
ul.links a, ul.links .arr, form.search button { transition: none; }
}
</style>
</head>
<body>
<div class="scanline" aria-hidden="true"></div>
<div class="coords" aria-hidden="true">
<span class="tl">39.9042°N / 116.4074°E</span>
<span class="tr">SYS v3.1.7 // UPLINK STABLE</span>
<span class="bl">GRID::0x5F_A3 · CH-04</span>
<span class="br">NODE:HYONGLIN · LOCK ▮</span>
</div>
<main class="wrap">
<div class="grid">
<!-- 头部:状态 + 标题 -->
<section class="card head" style="--d:.02s"><div class="in">
<div class="tags">
<span class="tag"><i class="dot"></i>SYSTEM ONLINE</span>
<span class="tag dim">NODE 39.90N · 116.40E // SECURE</span>
</div>
<h1 class="glitch" data-text="hyonglin.top">hyonglin.top</h1>
<p class="sub">个人服务导航 · 随时可达</p>
</div></section>
<!-- 天气 -->
<section class="card sky" style="--d:.08s"><div class="in">
<div class="label">天气<span class="unit">WX-TLM</span></div>
<div class="emoji">☀️</div>
<div class="temp wtxt">获取中…</div>
<div class="wcity"></div>
</div></section>
<!-- 日期 -->
<section class="card datecard" style="--d:.14s"><div class="in">
<div class="label">日期<span class="unit">DATE</span></div>
<div class="value" id="dateval">----</div>
</div></section>
<!-- 时间 -->
<section class="card clockcard" style="--d:.2s"><div class="in">
<div class="label">时间<span class="unit">LOCAL</span></div>
<div class="value" id="clockval">--:--:--</div>
</div></section>
<!-- 搜索:命令输入行 -->
<form class="card search" action="https://www.bing.com/search" method="get" target="_blank" rel="noopener"><div class="in">
<span class="ps">&gt; QUERY</span>
<select name="engine" id="engine" aria-label="搜索引擎">
<option value="bing" selected>Bing</option>
<option value="google">Google</option>
<option value="baidu">百度</option>
<option value="duckduckgo">DuckDuckGo</option>
</select>
<input type="text" name="q" placeholder="输入关键词,回车搜索…" autocomplete="off" aria-label="搜索">
<button type="submit">搜索</button>
</div></form>
<!-- 自建服务 -->
<section class="card panel s-self" style="--d:.3s"><div class="in">
<h2>📡 自建服务<span class="count">[4]</span></h2>
<ul class="links">
<li><a href="https://note.hyonglin.top"><span class="idx">01</span><span class="icon">📝</span><span class="meta"><span class="name">笔记</span><span class="desc">note.hyonglin.top · SilverBullet</span></span><span class="arr">↗</span></a></li>
<li><a href="https://git.hyonglin.top"><span class="idx">02</span><span class="icon">📦</span><span class="meta"><span class="name">Git 服务</span><span class="desc">git.hyonglin.top · Gitea</span></span><span class="arr">↗</span></a></li>
<li><a href="https://ssh.hyonglin.top"><span class="idx">03</span><span class="icon">🖥️</span><span class="meta"><span class="name">网页 SSH 终端</span><span class="desc">ssh.hyonglin.top</span></span><span class="arr">↗</span></a></li>
<li><a href="https://frp.hyonglin.top"><span class="idx">04</span><span class="icon">🔗</span><span class="meta"><span class="name">frp 管理面板</span><span class="desc">frp.hyonglin.top</span></span><span class="arr">↗</span></a></li>
</ul>
</div></section>
<!-- AI 助手 -->
<section class="card panel s-ai" style="--d:.36s"><div class="in">
<h2>🤖 AI 助手<span class="count">[2]</span></h2>
<ul class="links">
<li><a href="https://chat.deepseek.com/" target="_blank" rel="noopener"><span class="idx">01</span><span class="icon">🤖</span><span class="meta"><span class="name">DeepSeek</span><span class="desc">chat.deepseek.com</span></span><span class="arr">↗</span></a></li>
<li><a href="https://www.doubao.com/chat/" target="_blank" rel="noopener"><span class="idx">02</span><span class="icon">💬</span><span class="meta"><span class="name">豆包</span><span class="desc">doubao.com</span></span><span class="arr">↗</span></a></li>
</ul>
</div></section>
<!-- 云服务 -->
<section class="card panel s-cloud" style="--d:.42s"><div class="in">
<h2>☁️ 云服务<span class="count">[3]</span></h2>
<ul class="links">
<li><a href="https://dash.cloudflare.com/" target="_blank" rel="noopener"><span class="idx">01</span><span class="icon">🌐</span><span class="meta"><span class="name">Cloudflare</span><span class="desc">dash.cloudflare.com</span></span><span class="arr">↗</span></a></li>
<li><a href="https://auth.huaweicloud.com/" target="_blank" rel="noopener"><span class="idx">02</span><span class="icon">☁️</span><span class="meta"><span class="name">华为云</span><span class="desc">auth.huaweicloud.com</span></span><span class="arr">↗</span></a></li>
<li><a href="https://console.cloud.tencent.com/" target="_blank" rel="noopener"><span class="idx">03</span><span class="icon">🐧</span><span class="meta"><span class="name">腾讯云</span><span class="desc">console.cloud.tencent.com</span></span><span class="arr">↗</span></a></li>
</ul>
</div></section>
<!-- 常用 -->
<section class="card panel s-util" style="--d:.48s"><div class="in">
<h2>🔧 常用<span class="count">[3]</span></h2>
<ul class="links">
<li><a href="https://wx.mail.qq.com/" target="_blank" rel="noopener"><span class="idx">01</span><span class="icon">📧</span><span class="meta"><span class="name">QQ 邮箱</span><span class="desc">wx.mail.qq.com</span></span><span class="arr">↗</span></a></li>
<li><a href="https://www.bing.com/" target="_blank" rel="noopener"><span class="idx">02</span><span class="icon">🔍</span><span class="meta"><span class="name">Bing 搜索</span><span class="desc">bing.com</span></span><span class="arr">↗</span></a></li>
<li><a href="https://www.bilibili.com/" target="_blank" rel="noopener"><span class="idx">03</span><span class="icon">📺</span><span class="meta"><span class="name">哔哩哔哩</span><span class="desc">bilibili.com</span></span><span class="arr">↗</span></a></li>
</ul>
</div></section>
<!-- 页脚:版本切换 -->
<footer class="foot card" style="--d:.54s"><div class="in">
<span class="brand">hyonglin.top</span>
<nav aria-label="版本切换">
<a href="/terminal/">终端版</a><i>·</i>
<a href="/">Bento 版</a><i>·</i>
<a href="/brutal/">Brutal 版</a><i>·</i>
<a href="/swiss/">Swiss 版</a><i>·</i>
<span class="cur">◉ HUD 版</span>
</nav>
</div></footer>
</div>
</main>
<script>
/* ===== 搜索引擎选择 ===== */
(function(){
var sel = document.getElementById('engine');
if (!sel) return;
var ENGINES = {
bing: 'https://www.bing.com/search?q=',
google: 'https://www.google.com/search?q=',
baidu: 'https://www.baidu.com/s?wd=',
duckduckgo: 'https://duckduckgo.com/?q='
};
var form = sel.closest('form');
function apply() { form.action = ENGINES[sel.value] || ENGINES.bing; }
sel.addEventListener('change', apply);
apply();
})();
/* ===== 日期 + 时钟 ===== */
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(){ return document.querySelector('.sky .emoji'); };
var tempBox = function(){ return document.querySelector('.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>