Files
random-background-api/index.html
2026-03-29 10:58:33 +08:00

391 lines
11 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 name="viewport" content="width=device-width, initial-scale=1">
<title>随机背景 API</title>
<meta name="description" content="Cloudflare Pages 随机背景 API 文档与在线预览。">
<style>
:root {
--bg: #f6f7fb;
--panel: #fff;
--text: #172033;
--muted: #5b657a;
--line: #e3e7ef;
--soft: #f3f5f9;
--accent: #2563eb;
--code: #0b1220;
--shadow: 0 10px 28px rgba(15, 23, 42, 0.06);
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
background: linear-gradient(180deg, #fff 0%, var(--bg) 100%);
color: var(--text);
font: 14px/1.55 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
.wrap {
max-width: 1200px;
margin: 0 auto;
padding: 14px;
}
.top {
display: grid;
gap: 10px;
grid-template-columns: minmax(0, 1fr) 250px;
align-items: start;
}
.main {
display: grid;
gap: 10px;
grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
margin-top: 10px;
}
.card {
background: var(--panel);
border: 1px solid var(--line);
border-radius: 14px;
box-shadow: var(--shadow);
overflow: hidden;
}
.pad { padding: 12px; }
h1, h2, p { margin: 0; }
h1 {
font-size: clamp(24px, 3vw, 34px);
line-height: 1.1;
letter-spacing: -0.03em;
}
h2 {
font-size: 15px;
margin-bottom: 6px;
}
.lead {
margin-top: 6px;
color: var(--muted);
}
.meta {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 10px;
}
.tag {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 9px;
border-radius: 999px;
border: 1px solid var(--line);
background: var(--soft);
font-size: 11px;
color: var(--text);
}
.links {
display: grid;
gap: 6px;
font-size: 13px;
}
.section {
display: grid;
gap: 8px;
}
.grid-2 {
display: grid;
gap: 8px;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.endpoint {
padding: 10px;
border-radius: 12px;
border: 1px solid var(--line);
background: #fff;
}
.endpoint strong {
display: block;
margin-bottom: 4px;
font-size: 12px;
}
.code {
margin: 0;
padding: 9px 10px;
border-radius: 10px;
border: 1px solid var(--line);
background: var(--code);
color: #e7eefc;
overflow-x: auto;
font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
label {
display: block;
margin-bottom: 4px;
font-size: 11px;
color: var(--muted);
}
select, button, input {
width: 100%;
padding: 9px 10px;
border-radius: 10px;
border: 1px solid var(--line);
background: #fff;
color: var(--text);
font: inherit;
}
button {
cursor: pointer;
border-color: var(--accent);
background: var(--accent);
color: #fff;
}
.row {
display: grid;
gap: 8px;
grid-template-columns: repeat(3, minmax(0, 1fr)) 100px;
}
.preview-grid {
display: grid;
gap: 8px;
grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
align-items: start;
}
.preview-box {
aspect-ratio: 16 / 10;
border-radius: 12px;
overflow: hidden;
border: 1px solid var(--line);
background: #eef2f7;
}
.preview-box img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.small {
color: var(--muted);
font-size: 11px;
}
.footer {
margin-top: 10px;
color: var(--muted);
font-size: 11px;
}
@media (max-width: 900px) {
.top, .main, .grid-2, .preview-grid, .row {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<main class="wrap">
<section class="top">
<div class="card pad">
<h1>随机背景 API</h1>
<p class="lead">Cloudflare Pages 随机背景接口。上传图片后先用 `build.py` 生成序号 `.webp`,然后可通过 `/api/random` 和 `/api/list` 调用。</p>
<div class="meta">
<span class="tag">竖屏mobile-image</span>
<span class="tag">横屏desketop-image</span>
<span class="tag">返回302 / JSON</span>
<span class="tag">组件widget.js</span>
</div>
</div>
<div class="card pad">
<h2>快速链接</h2>
<div class="links">
<a href="/api/list" target="_blank" rel="noreferrer">/api/list</a>
<a href="/manifest.json" target="_blank" rel="noreferrer">manifest.json</a>
<a href="/mobile-image/1.webp" target="_blank" rel="noreferrer">mobile-image/1.webp</a>
</div>
</div>
</section>
<section class="main">
<div class="card pad section" id="api">
<h2>API 调用</h2>
<div class="grid-2">
<div class="endpoint">
<strong>随机图片</strong>
<div class="small">默认 302 跳转到随机图片。</div>
<pre class="code">GET /api/random</pre>
</div>
<div class="endpoint">
<strong>返回 JSON</strong>
<div class="small">适合前端自己处理图片地址。</div>
<pre class="code">GET /api/random?format=json</pre>
</div>
</div>
<div class="grid-2">
<div class="endpoint">
<strong>强制竖屏</strong>
<pre class="code">GET /api/random?mode=mobile</pre>
</div>
<div class="endpoint">
<strong>强制横屏</strong>
<pre class="code">GET /api/random?mode=desktop</pre>
</div>
</div>
<p class="small">支持参数:`mode=auto|mobile|desktop`、`orientation=portrait|landscape`、`device=mobile|desktop`、`format=redirect|json`。</p>
</div>
<div class="card pad section" id="preview">
<h2>在线预览</h2>
<div class="row">
<div>
<label for="modeSelect">mode</label>
<select id="modeSelect">
<option value="auto">auto</option>
<option value="mobile">mobile</option>
<option value="desktop">desktop</option>
</select>
</div>
<div>
<label for="orientationSelect">orientation</label>
<select id="orientationSelect">
<option value="">auto</option>
<option value="portrait">portrait</option>
<option value="landscape">landscape</option>
</select>
</div>
<div>
<label for="formatSelect">format</label>
<select id="formatSelect">
<option value="json">json</option>
<option value="redirect">redirect</option>
</select>
</div>
<button id="refreshBtn" type="button">刷新</button>
</div>
<div class="preview-grid">
<div>
<label for="apiUrl">当前地址</label>
<input id="apiUrl" readonly value="">
<div class="preview-box" style="margin-top:8px">
<img id="previewImg" alt="随机背景预览" src="">
</div>
</div>
<div>
<label>返回结果</label>
<pre class="code" id="jsonOutput">{}</pre>
</div>
</div>
</div>
</section>
<section class="card pad section" style="margin-top:10px">
<h2>嵌入组件</h2>
<div class="grid-2">
<div class="endpoint">
<strong>直接引用</strong>
<pre class="code">&lt;script src="https://random-background-api.pages.dev/widget.js"&gt;&lt;/script&gt;
&lt;random-background-widget
src="https://random-background-api.pages.dev"
blur="12"
radius="16px"
height="260px"&gt;
&lt;/random-background-widget&gt;</pre>
</div>
<div class="endpoint">
<strong>说明</strong>
<div class="small">组件只读静态 `manifest.json` 和图片,不消耗 Functions 额度;`blur` 控制高斯模糊像素值。</div>
</div>
</div>
</section>
<div class="footer">
构建命令:<code>python3 build.py</code>。这里保留 Pages Functions所以可以直接提供 HTTP 随机 API。
</div>
</main>
<script>
const modeSelect = document.getElementById('modeSelect');
const orientationSelect = document.getElementById('orientationSelect');
const formatSelect = document.getElementById('formatSelect');
const refreshBtn = document.getElementById('refreshBtn');
const apiUrl = document.getElementById('apiUrl');
const previewImg = document.getElementById('previewImg');
const jsonOutput = document.getElementById('jsonOutput');
function detectOrientation() {
return window.matchMedia('(orientation: portrait)').matches ? 'portrait' : 'landscape';
}
function detectDevice() {
if (navigator.userAgentData && typeof navigator.userAgentData.mobile === 'boolean') {
return navigator.userAgentData.mobile ? 'mobile' : 'desktop';
}
return /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent) ? 'mobile' : 'desktop';
}
function buildApiPath() {
const params = new URLSearchParams();
params.set('mode', modeSelect.value);
params.set('device', detectDevice());
params.set('format', formatSelect.value);
const orientation = orientationSelect.value || detectOrientation();
params.set('orientation', orientation);
return `/api/random?${params.toString()}`;
}
async function refresh() {
const url = buildApiPath();
apiUrl.value = url;
previewImg.style.opacity = '0.72';
if (formatSelect.value === 'json') {
const response = await fetch(url);
const data = await response.json();
jsonOutput.textContent = JSON.stringify(data, null, 2);
previewImg.src = data.url || '';
} else {
jsonOutput.textContent = '{ "format": "redirect" }';
previewImg.src = url;
}
}
previewImg.addEventListener('load', () => {
previewImg.style.opacity = '1';
});
refreshBtn.addEventListener('click', refresh);
modeSelect.addEventListener('change', refresh);
orientationSelect.addEventListener('change', refresh);
formatSelect.addEventListener('change', refresh);
refresh();
</script>
</body>
</html>