This commit is contained in:
yangyaoxiang666
2025-09-04 14:07:47 +08:00
180 changed files with 13653 additions and 26954 deletions

View File

@@ -17,8 +17,8 @@ class KFCGenerator {
// 加载API接口列表
async loadApiEndpoints() {
try {
const response = await fetch('./接口集合.json');
this.apiEndpoints = await response.json();
// 直接硬编码API端点避免CORS问题
this.apiEndpoints = ["https://60s.api.shumengya.top"];
} catch (error) {
console.error('加载API接口列表失败:', error);
this.showToast('加载接口配置失败', 'error');

View File

@@ -1 +1,8 @@
{"code":200,"message":"获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s反馈群 595941841","data":{"index":369,"kfc":"我不想你带着情绪过夜,这样会把你推的越来越远,我不想你离开我,我希望你可以多爱我一点,我不想因为一点点小问题就让我们两之间的感情产生隔阂。对我来说,双向奔赴的感情相处才有意义,看不到希望的事情我没办法坚持太久,我不想浪费时间在一些无意义无结果的事情上,时间应该用来做我该做的事情。很多人走不到最后,是因为理解不同步,但我跟你在一起,我知道你什样的人,我了解你甚至比了解自己还多,我和你在一起,不是因为其他,而是因为今天肯德基疯狂星期四,我希望你能请我吃!"}}
{
"code": 200,
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s反馈群 595941841",
"data": {
"index": 78,
"kfc": "我叫夯大力 立冬给我准备了糖炒栗子了没有 没准备的自动绝交 再 v 我 50 吃疯狂星期四 然后再给我点杯奶茶 再给我两万块钱 懂吗你们"
}
}

View File

@@ -4,11 +4,7 @@ class HitokotoApp {
constructor() {
// API接口列表
this.apiEndpoints = [
"https://60s-cf.viki.moe",
"https://60s.viki.moe",
"https://60s.b23.run",
"https://60s.114128.xyz",
"https://60s-cf.114128.xyz"
"https://60s.api.shumengya.top"
];
this.currentEndpointIndex = 0;
@@ -117,7 +113,8 @@ class HitokotoApp {
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时
try {
const response = await fetch(`${endpoint}/v2/hitokoto?encoding=text`, {
// 移除URL中的encoding=text参数确保返回JSON格式
const response = await fetch(`${endpoint}/v2/hitokoto`, {
method: 'GET',
headers: {
'Accept': 'application/json',

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>每日笑话</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="card">
<p id="joke">加载中...</p>
<button id="next">换一个</button>
</div>
<script src="script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,107 @@
/* background.css - 动态渐变背景 */
body {
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
background-size: 400% 400%;
animation: gradient 15s ease infinite;
}
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
:root {
--bg-yellow: #FFFDE7; /* 浅黄 */
--bg-blue: #E3F2FD; /* 淡蓝 */
}
body {
background: linear-gradient(180deg, var(--bg-yellow) 0%, var(--bg-blue) 100%);
background-attachment: fixed; /* 固定背景,滚动时不移动 */
}
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
overflow: hidden;
transition: background-color 0.5s ease;
}
/* Light Theme (Default) */
[data-theme="light"] {
background: linear-gradient(to bottom, #87CEEB, #B0E0E6);
}
/* Dark Theme */
[data-theme="dark"] {
background: linear-gradient(to bottom, #232526, #414345);
}
[data-theme="dark"] .snowflake {
color: #999;
}
/* Winter Theme */
[data-theme="winter"] {
background: linear-gradient(to bottom, #a1c4fd, #c2e9fb);
}
[data-theme="winter"] .background-bottom {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background: linear-gradient(to top, white, rgba(255, 255, 255, 0));
z-index: -1;
border-radius: 50% 50% 0 0 / 20px;
box-shadow: 0 -10px 20px rgba(255, 255, 255, 0.5);
}
#snowflake-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 0;
}
.snowflake {
position: absolute;
top: -10%;
color: white;
font-size: 20px;
user-select: none;
animation: fall linear infinite;
}
@keyframes fall {
to {
transform: translateY(105vh) rotate(360deg);
}
}
#frost-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('https://www.transparenttextures.com/patterns/ice-age.png') repeat;
opacity: 0;
pointer-events: none;
transition: opacity 0.5s ease-in-out;
z-index: 100;
}
#frost-overlay.is-frosted {
opacity: 0.3;
}

View File

@@ -0,0 +1,217 @@
:root {
--primary-color-light: #4A90E2;
--text-color-light: #333;
--card-bg-light: rgba(255, 255, 255, 0.85);
--primary-color-dark: #5271C4;
--text-color-dark: #E0E0E0;
--card-bg-dark: rgba(40, 40, 40, 0.85);
--primary-color-winter: #6A82FB;
--text-color-winter: #2c3e50;
--card-bg-winter: rgba(255, 255, 255, 0.7);
}
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 20px;
box-sizing: border-box;
text-align: center;
position: relative;
z-index: 1;
}
.top-nav {
position: absolute;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.3);
padding: 5px;
border-radius: 50px;
backdrop-filter: blur(5px);
}
.theme-switcher {
display: flex;
gap: 5px;
}
.theme-btn {
background: transparent;
border: 2px solid transparent;
border-radius: 50%;
width: 40px;
height: 40px;
font-size: 1.5em;
cursor: pointer;
transition: transform 0.2s, border-color 0.2s;
}
.theme-btn:hover {
transform: scale(1.1);
}
.theme-btn.active {
border-color: white;
}
.title {
font-family: 'ZCOOL KuaiLe', cursive;
font-size: 3em;
margin-bottom: 20px;
transition: color 0.5s ease;
}
.joke-stream {
width: 100%;
max-width: 500px;
display: flex;
flex-direction: column;
gap: 25px;
}
.joke-card {
border-radius: 20px;
padding: 30px 40px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
width: 100%;
max-width: 500px;
min-height: 150px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.2);
transition: background-color 0.5s ease, border-color 0.5s ease;
}
#joke-text {
font-size: 1.5em;
line-height: 1.6;
transition: opacity 0.3s, color 0.5s ease;
}
/* --- Theming --- */
/* Light Theme */
[data-theme="light"] .title { color: white; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2); }
[data-theme="light"] .joke-card { background-color: var(--card-bg-light); }
[data-theme="light"] #joke-text { color: var(--text-color-light); }
[data-theme="light"] #new-joke-btn { background-color: var(--primary-color-light); box-shadow: 0 4px 15px rgba(74, 144, 226, 0.4); }
[data-theme="light"] footer { color: rgba(255, 255, 255, 0.8); }
/* Dark Theme */
[data-theme="dark"] .title { color: #EAEAEA; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); }
[data-theme="dark"] .joke-card { background-color: var(--card-bg-dark); border-color: rgba(255, 255, 255, 0.1); }
[data-theme="dark"] #joke-text { color: var(--text-color-dark); }
[data-theme="dark"] #new-joke-btn { background-color: var(--primary-color-dark); box-shadow: 0 4px 15px rgba(82, 113, 196, 0.4); }
[data-theme="dark"] footer { color: rgba(200, 200, 200, 0.7); }
/* Winter Theme */
[data-theme="winter"] .title { color: #1e3a5f; text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.7); }
[data-theme="winter"] .joke-card {
background-color: var(--card-bg-winter);
border-color: rgba(255, 255, 255, 0.8);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1), inset 0 0 15px rgba(255, 255, 255, 0.5);
}
[data-theme="winter"] #joke-text { color: var(--text-color-winter); }
[data-theme="winter"] #new-joke-btn { background-color: var(--primary-color-winter); box-shadow: 0 4px 15px rgba(106, 130, 251, 0.4); }
[data-theme="winter"] footer { color: #1e3a5f; }
.controls {
margin-top: 30px;
}
#new-joke-btn {
color: white;
font-size: 1.2em;
font-weight: bold;
padding: 15px 35px;
border: none;
border-radius: 50px;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.5s ease;
}
#new-joke-btn:hover {
transform: translateY(-3px);
}
#new-joke-btn:active {
transform: translateY(1px);
}
.interactions {
margin-top: 25px;
display: flex;
gap: 20px;
}
.interaction-btn {
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.9);
border-radius: 50%;
width: 50px;
height: 50px;
font-size: 1.5em;
cursor: pointer;
transition: transform 0.2s, background-color 0.2s;
}
.interaction-btn:hover {
transform: scale(1.1);
background: white;
}
footer {
position: absolute;
bottom: 10px;
font-size: 0.9em;
transition: color 0.5s ease;
}
/* Loader */
#loader {
position: absolute;
transition: color 0.5s ease;
}
[data-theme="light"] #loader { color: var(--primary-color-light); }
[data-theme="dark"] #loader { color: var(--primary-color-dark); }
[data-theme="winter"] #loader { color: var(--primary-color-winter); }
.snowflake-loader {
font-size: 40px;
display: inline-block;
animation: spin 1.5s linear infinite;
}
.snowflake-loader::before {
content: '❄';
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.hidden {
display: none;
}
/* Responsive */
@media (max-width: 600px) {
.title {
font-size: 2.5em;
}
.joke-card {
padding: 25px;
}
#joke-text {
font-size: 1.2em;
}
.top-nav {
top: 10px;
right: 10px;
}
}

View File

@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>随机冷笑话</title>
<link href="https://fonts.googleapis.com/css2?family=ZCOOL+KuaiLe&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/background.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body data-theme="light">
<div id="snowflake-container"></div>
<div id="frost-overlay"></div>
<div class="background-bottom"></div>
<nav class="top-nav">
<div class="theme-switcher">
<button class="theme-btn" data-theme-target="light" title="清新风">☀️</button>
<button class="theme-btn" data-theme-target="dark" title="暗黑风">🌙</button>
<button class="theme-btn" data-theme-target="winter" title="冰雪风">❄️</button>
</div>
</nav>
<div class="container">
<header>
<h1 class="title">冷笑话生成器</h1>
</header>
<main class="joke-card">
<div id="loader" class="hidden">
<div class="snowflake-loader"></div>
<p>思考中...</p>
</div>
<p id="joke-text">点击下面的按钮,来点冷笑话吧!</p>
</main>
<div class="controls">
<button id="new-joke-btn">再来一个</button>
</div>
<div class="interactions">
<button class="interaction-btn" id="like-btn" title="好笑">👍</button>
<button class="interaction-btn" id="dislike-btn" title="不好笑">👎</button>
<button class="interaction-btn" id="collect-btn" title="收藏">⭐️</button>
<button class="interaction-btn" id="share-btn" title="分享">🔗</button>
</div>
</div>
<footer>
<p>&copy; 2024 冷笑话工坊</p>
</footer>
<audio id="wind-sound" src="https://www.soundjay.com/nature/sounds/wind-howl-01.mp3" preload="auto"></audio>
<audio id="snow-sound" src="https://www.soundjay.com/nature/sounds/walking-in-snow-01.mp3" preload="auto"></audio>
<script src="js/script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,117 @@
document.addEventListener('DOMContentLoaded', () => {
const jokeTextElem = document.getElementById('joke-text');
const newJokeBtn = document.getElementById('new-joke-btn');
const snowflakeContainer = document.getElementById('snowflake-container');
const frostOverlay = document.getElementById('frost-overlay');
const windSound = document.getElementById('wind-sound');
const snowSound = document.getElementById('snow-sound');
const loader = document.getElementById('loader');
const themeBtns = document.querySelectorAll('.theme-btn');
const apiEndpoints = [
'https://60s.api.shumengya.top/v2/dad-joke',
];
let currentApiIndex = 0;
async function fetchJoke() {
jokeTextElem.classList.add('hidden');
loader.classList.remove('hidden');
try {
const response = await fetch(apiEndpoints[currentApiIndex]);
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
if (data.code === 200 && data.data.content) {
updateJokeText(data.data.content);
if (document.body.dataset.theme === 'winter' && Math.random() < 0.3) {
triggerFrostEffect();
}
} else {
throw new Error('API returned invalid data');
}
} catch (error) {
console.error('Fetch error:', error);
currentApiIndex = (currentApiIndex + 1) % apiEndpoints.length;
if (currentApiIndex !== 0) {
fetchJoke();
} else {
jokeTextElem.textContent = '冰箱坏了,暂时没有冷笑话...';
}
} finally {
loader.classList.add('hidden');
jokeTextElem.classList.remove('hidden');
}
}
function updateJokeText(text) {
jokeTextElem.textContent = '';
let i = 0;
const typing = setInterval(() => {
if (i < text.length) {
jokeTextElem.textContent += text.charAt(i);
i++;
} else {
clearInterval(typing);
}
}, 50);
}
function createSnowflakes() {
const snowflakeCount = document.body.dataset.theme === 'dark' ? 50 : 30;
snowflakeContainer.innerHTML = '';
for (let i = 0; i < snowflakeCount; i++) {
const snowflake = document.createElement('div');
snowflake.className = 'snowflake';
snowflake.textContent = '❄️';
snowflake.style.left = `${Math.random() * 100}vw`;
snowflake.style.fontSize = `${Math.random() * 15 + 10}px`;
snowflake.style.opacity = Math.random() * 0.5 + 0.3;
const duration = Math.random() * 10 + 8;
const delay = Math.random() * 10;
snowflake.style.animation = `fall ${duration}s linear ${delay}s infinite`;
snowflakeContainer.appendChild(snowflake);
}
}
function triggerFrostEffect() {
frostOverlay.classList.add('is-frosted');
windSound.play().catch(e => console.error("Audio play failed:", e));
setTimeout(() => {
frostOverlay.classList.remove('is-frosted');
}, 2000);
}
function setTheme(theme) {
document.body.dataset.theme = theme;
localStorage.setItem('joke-theme', theme);
themeBtns.forEach(btn => {
btn.classList.toggle('active', btn.dataset.themeTarget === theme);
});
if (theme === 'winter') {
snowSound.play().catch(e => console.error("Audio play failed:", e));
}
// Recreate snowflakes for theme-specific density
createSnowflakes();
}
themeBtns.forEach(btn => {
btn.addEventListener('click', () => {
setTheme(btn.dataset.themeTarget);
});
});
newJokeBtn.addEventListener('click', fetchJoke);
// Initial setup
const savedTheme = localStorage.getItem('joke-theme') || 'light';
setTheme(savedTheme);
fetchJoke();
});

View File

@@ -0,0 +1,47 @@
{
"api_name": "60s-api",
"api_version": "2.22.1",
"api_docs": "https://docs.60s-api.viki.moe",
"author": "Viki <hi@viki.moe>",
"user_group": "595941841",
"github_repo": "https://github.com/vikiboss/60s",
"updated": "2025/09/01 11:12:08",
"updated_at": 1756696328000,
"endpoints": [
"/v2/60s",
"/v2/answer",
"/v2/baike",
"/v2/bili",
"/v2/bing",
"/v2/changya",
"/v2/chemical",
"/v2/douyin",
"/v2/duanzi",
"/v2/epic",
"/v2/exchange_rate",
"/v2/fabing",
"/v2/hitokoto",
"/v2/ip",
"/v2/kfc",
"/v2/luck",
"/v2/maoyan",
"/v2/today_in_history",
"/v2/toutiao",
"/v2/weibo",
"/v2/zhihu",
"/v2/lunar",
"/v2/ai-news",
"/v2/awesome-js",
"/v2/qrcode",
"/v2/dad-joke",
"/v2/hacker-news/:type",
"/v2/og",
"/v2/hash",
"/v2/fanyi",
"/v2/fanyi/langs",
"/v2/weather",
"/v2/weather/forecast",
"/v2/ncm-rank",
"/v2/ncm-rank/:id"
]
}

View File

@@ -0,0 +1,8 @@
{
"code": 200,
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s反馈群 595941841",
"data": {
"index": 121,
"content": "这个世界上谁最懂猪?蜘蛛(知猪)人。"
}
}

View File

@@ -0,0 +1,90 @@
body {
background-color: #1a1a1a;
color: #e0e0e0;
font-family: 'Courier New', Courier, monospace;
overflow: hidden;
margin: 0;
padding: 0;
}
#bg-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -2;
overflow: hidden;
transition: transform 0.2s ease-out;
}
.floating-emoji {
position: absolute;
user-select: none;
opacity: 0;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
@keyframes float-top-to-bottom {
0% { transform: translateY(-10vh) rotate(0deg); opacity: 0; }
10%, 90% { opacity: 0.7; }
100% { transform: translateY(110vh) rotate(360deg); opacity: 0; }
}
@keyframes float-bottom-to-top {
0% { transform: translateY(110vh) rotate(0deg); opacity: 0; }
10%, 90% { opacity: 0.7; }
100% { transform: translateY(-10vh) rotate(360deg); opacity: 0; }
}
@keyframes float-left-to-right {
0% { transform: translateX(-10vw) rotate(0deg); opacity: 0; }
10%, 90% { opacity: 0.7; }
100% { transform: translateX(110vw) rotate(360deg); opacity: 0; }
}
@keyframes float-right-to-left {
0% { transform: translateX(110vw) rotate(0deg); opacity: 0; }
10%, 90% { opacity: 0.7; }
100% { transform: translateX(-10vw) rotate(360deg); opacity: 0; }
}
.text-fragment {
position: absolute;
font-size: 24px;
color: rgba(255, 0, 255, 0.4);
opacity: 0;
animation: float-fragment 15s linear infinite, fade-in-out 15s linear infinite;
user-select: none;
}
@keyframes float-fragment {
0% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(20px, 40px) rotate(15deg); }
50% { transform: translate(-30px, -10px) rotate(-10deg); }
75% { transform: translate(10px, -30px) rotate(5deg); }
100% { transform: translate(0, 0) rotate(0deg); }
}
@keyframes fade-in-out {
0%, 100% { opacity: 0; }
10%, 90% { opacity: 0.4; }
}
.screen-crack {
position: absolute;
width: 200px;
height: 200px;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M 100 100 L 0 0 M 100 100 L 200 0 M 100 100 L 50 200 M 100 100 L 150 200" stroke="rgba(255,255,255,0.5)" stroke-width="1" fill="none"/></svg>');
opacity: 0;
animation: flicker-crack 25s steps(1, end) infinite;
}
@keyframes flicker-crack {
0%, 100% { opacity: 0; }
50% { opacity: 0.3; }
51% { opacity: 0; }
75% { opacity: 0.2; }
76% { opacity: 0; }
}

View File

@@ -0,0 +1,235 @@
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
box-sizing: border-box;
position: relative;
z-index: 1;
}
.content-card {
background: rgba(20, 20, 20, 0.7);
border: none;
padding: 40px;
max-width: 600px;
width: 100%;
text-align: center;
backdrop-filter: blur(5px);
position: relative;
clip-path: polygon(2% 5%, 97% 0%, 100% 95%, 0% 100%);
}
.body-animated .content-card {
animation: tremble 0.4s infinite, glitch-shadow 1.5s steps(1, end) infinite;
}
@keyframes tremble {
0% { clip-path: polygon(2% 5%, 97% 0%, 100% 95%, 0% 100%); }
25% { clip-path: polygon(2% 5%, 98% 2%, 99% 100%, 1% 98%); }
50% { clip-path: polygon(3% 4%, 96% 1%, 100% 96%, 2% 100%); }
75% { clip-path: polygon(1% 6%, 97% 3%, 98% 95%, 0% 99%); }
100% { clip-path: polygon(2% 5%, 97% 0%, 100% 95%, 0% 100%); }
}
@keyframes glitch-shadow {
0% {
box-shadow:
0 0 8px rgba(255, 0, 255, 0.5),
inset 0 0 8px rgba(255, 0, 255, 0.4);
}
33% {
box-shadow:
0 0 8px rgba(0, 255, 255, 0.5),
inset 0 0 8px rgba(0, 255, 255, 0.4);
}
66% {
box-shadow:
0 0 8px rgba(0, 255, 0, 0.5),
inset 0 0 8px rgba(0, 255, 0, 0.4);
}
100% {
box-shadow:
0 0 8px rgba(255, 0, 255, 0.5),
inset 0 0 8px rgba(255, 0, 255, 0.4);
}
}
#literature-text {
font-size: 1.2em;
line-height: 1.8;
min-height: 100px;
color: #e0e0e0;
text-shadow: 0 0 5px rgba(0, 255, 135, 0.5);
animation: text-flicker 15s linear infinite;
}
.body-animated #literature-text {
animation: text-flicker 15s linear infinite, text-shadow-glitch 2s steps(1, end) infinite;
}
@keyframes text-flicker {
0%, 100% { opacity: 1; }
50.0% { opacity: 0.95; }
50.5% { opacity: 1; }
}
@keyframes text-shadow-glitch {
0% {
text-shadow:
1px 0 0 rgba(255,0,255,0.5),
-1px 0 0 rgba(0,255,255,0.5);
}
10% {
text-shadow:
-1px 0 0 rgba(255,0,255,0.5),
1px 0 0 rgba(0,255,255,0.5);
}
11%, 100% {
text-shadow: none;
}
}
.controls {
margin-top: 30px;
display: flex;
align-items: center;
gap: 20px;
}
#new-literature-btn {
background-color: #ff00ff;
color: #fff;
border: none;
padding: 12px 25px;
font-size: 1em;
cursor: pointer;
border-radius: 5px;
text-transform: uppercase;
font-weight: bold;
transition: transform 0.2s, box-shadow 0.2s;
box-shadow: 0 0 10px #ff00ff, 0 0 20px #ff00ff;
}
#new-literature-btn:hover {
transform: scale(1.05);
box-shadow: 0 0 15px #ff00ff, 0 0 30px #ff00ff;
}
#new-literature-btn:active {
transform: scale(0.95);
}
/* Animation Toggle Switch */
.switch-container {
display: flex;
align-items: center;
gap: 10px;
color: #aaa;
}
.switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
}
input:checked + .slider {
background-color: #ff00ff;
}
input:checked + .slider:before {
transform: translateX(26px);
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
/* Glitch Overlay & Animations */
#glitch-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
pointer-events: none;
}
.body-animated #glitch-overlay {
animation: color-shift 15s steps(1, end) infinite;
}
@keyframes color-shift {
0%, 100% { background: transparent; }
10% { background: rgba(255, 0, 0, 0.05); }
10.1% { background: transparent; }
20% { background: rgba(0, 255, 0, 0.05); }
20.1% { background: transparent; }
30% { background: rgba(0, 0, 255, 0.05); }
30.1% { background: transparent; }
}
.flicker-block {
position: absolute;
background: rgba(255, 255, 255, 0.1);
opacity: 0;
}
.body-animated .flicker-block {
animation: flicker 3s infinite;
}
@keyframes flicker {
0%, 100% { opacity: 0; }
50% { opacity: 1; }
}
/* Responsive Design */
@media (max-width: 768px) {
.content-card {
padding: 20px;
}
#literature-text {
font-size: 1em;
}
.controls {
flex-direction: column;
}
}

View File

@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>随机发病文学</title>
<link rel="stylesheet" href="css/background.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="bg-container">
</div>
<div id="glitch-overlay"></div>
<div class="container">
<div class="content-card">
<p id="literature-text">正在加载发病文学...</p>
</div>
<div class="controls">
<button id="new-literature-btn">再疯一次</button>
<div class="switch-container">
<label for="animation-toggle">关闭动画</label>
<label class="switch">
<input type="checkbox" id="animation-toggle" checked>
<span class="slider round"></span>
</label>
</div>
</div>
</div>
<script src="js/script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,147 @@
document.addEventListener('DOMContentLoaded', () => {
const literatureTextElem = document.getElementById('literature-text');
const newLiteratureBtn = document.getElementById('new-literature-btn');
const animationToggle = document.getElementById('animation-toggle');
const bgContainer = document.getElementById('bg-container');
const body = document.body;
const apiEndpoints = [
'https://60s.api.shumengya.top/v2/fabing',
// Add fallback APIs here if available
];
let currentApiIndex = 0;
async function fetchLiterature() {
literatureTextElem.textContent = '正在卖力发疯中...';
literatureTextElem.style.opacity = '0.5';
try {
const response = await fetch(apiEndpoints[currentApiIndex]);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
if (data.code === 200) {
literatureTextElem.textContent = data.data.saying;
} else {
throw new Error('API returned an error');
}
} catch (error) {
console.error('Fetch error:', error);
currentApiIndex = (currentApiIndex + 1) % apiEndpoints.length;
if (currentApiIndex !== 0) {
fetchLiterature(); // Retry with the next API
} else {
literatureTextElem.textContent = '疯不起来了,请稍后再试。';
}
} finally {
literatureTextElem.style.opacity = '1';
}
}
function createFloatingEmojis() {
const existingEmojis = bgContainer.querySelectorAll('.floating-emoji');
existingEmojis.forEach(e => e.remove());
const emojis = ['🤯', '😵', '🤪', '🥴', '🤡', '👹', '👻', '💀', '💥', '🔥', '🌪️', '😵‍💫'];
const animationNames = ['float-top-to-bottom', 'float-bottom-to-top', 'float-left-to-right', 'float-right-to-left'];
const emojiCount = 25;
for (let i = 0; i < emojiCount; i++) {
const emojiEl = document.createElement('div');
emojiEl.className = 'floating-emoji';
emojiEl.textContent = emojis[Math.floor(Math.random() * emojis.length)];
const animationName = animationNames[Math.floor(Math.random() * animationNames.length)];
emojiEl.style.animationName = animationName;
emojiEl.style.animationDuration = `${Math.random() * 10 + 15}s`; // 15-25 seconds
emojiEl.style.animationDelay = `${Math.random() * 20}s`;
emojiEl.style.fontSize = `${Math.random() * 20 + 20}px`;
// Set initial position based on animation direction
if (animationName.includes('top') || animationName.includes('bottom')) { // Vertical movement
emojiEl.style.left = `${Math.random() * 100}vw`;
} else { // Horizontal movement
emojiEl.style.top = `${Math.random() * 100}vh`;
}
bgContainer.appendChild(emojiEl);
}
}
function createFragments() {
const existingFragments = bgContainer.querySelectorAll('.text-fragment');
existingFragments.forEach(f => f.remove());
const fragments = ['我', '疯', '了', '', '', '…', '救命', '为什么', '好烦', '啊啊啊'];
for (let i = 0; i < 20; i++) {
const frag = document.createElement('div');
frag.className = 'text-fragment';
frag.textContent = fragments[Math.floor(Math.random() * fragments.length)];
frag.style.top = `${Math.random() * 100}%`;
frag.style.left = `${Math.random() * 100}%`;
frag.style.animationDelay = `${Math.random() * 15}s`;
frag.style.fontSize = `${Math.random() * 12 + 12}px`;
bgContainer.appendChild(frag);
}
}
function createCracks() {
for (let i = 0; i < 2; i++) {
const crack = document.createElement('div');
crack.className = 'screen-crack';
crack.style.top = `${Math.random() * 80}%`;
crack.style.left = `${Math.random() * 80}%`;
crack.style.transform = `rotate(${Math.random() * 360}deg)`;
crack.style.animationDelay = `${Math.random() * 25}s`;
bgContainer.appendChild(crack);
}
}
function createFlickerBlocks() {
const glitchOverlay = document.getElementById('glitch-overlay');
for (let i = 0; i < 3; i++) {
const block = document.createElement('div');
block.className = 'flicker-block';
block.style.width = `${Math.random() * 100 + 50}px`;
block.style.height = `${Math.random() * 100 + 50}px`;
block.style.top = `${Math.random() * 90}%`;
block.style.left = `${Math.random() * 90}%`;
block.style.animationDuration = `${Math.random() * 2 + 2}s`;
block.style.animationDelay = `${Math.random() * 3}s`;
glitchOverlay.appendChild(block);
}
}
function toggleAnimations() {
if (animationToggle.checked) {
body.classList.add('body-animated');
} else {
body.classList.remove('body-animated');
}
}
document.addEventListener('mousemove', (e) => {
if (!animationToggle.checked) return;
const x = (window.innerWidth / 2) - e.pageX;
const y = (window.innerHeight / 2) - e.pageY;
bgContainer.style.transform = `translateX(${x / 50}px) translateY(${y / 50}px)`;
});
newLiteratureBtn.addEventListener('click', fetchLiterature);
animationToggle.addEventListener('change', toggleAnimations);
// Initial setup
createFloatingEmojis();
createFragments();
createCracks();
createFlickerBlocks();
toggleAnimations();
fetchLiterature();
window.addEventListener('resize', () => {
createFloatingEmojis();
createFragments();
});
});

View File

@@ -0,0 +1,47 @@
{
"api_name": "60s-api",
"api_version": "2.22.1",
"api_docs": "https://docs.60s-api.viki.moe",
"author": "Viki <hi@viki.moe>",
"user_group": "595941841",
"github_repo": "https://github.com/vikiboss/60s",
"updated": "2025/09/01 11:12:08",
"updated_at": 1756696328000,
"endpoints": [
"/v2/60s",
"/v2/answer",
"/v2/baike",
"/v2/bili",
"/v2/bing",
"/v2/changya",
"/v2/chemical",
"/v2/douyin",
"/v2/duanzi",
"/v2/epic",
"/v2/exchange_rate",
"/v2/fabing",
"/v2/hitokoto",
"/v2/ip",
"/v2/kfc",
"/v2/luck",
"/v2/maoyan",
"/v2/today_in_history",
"/v2/toutiao",
"/v2/weibo",
"/v2/zhihu",
"/v2/lunar",
"/v2/ai-news",
"/v2/awesome-js",
"/v2/qrcode",
"/v2/dad-joke",
"/v2/hacker-news/:type",
"/v2/og",
"/v2/hash",
"/v2/fanyi",
"/v2/fanyi/langs",
"/v2/weather",
"/v2/weather/forecast",
"/v2/ncm-rank",
"/v2/ncm-rank/:id"
]
}

View File

@@ -0,0 +1,8 @@
{
"code": 200,
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s反馈群 595941841",
"data": {
"index": 347,
"duanzi": "我不想读书,主要是因为家里牛啊,猪啊羊啊都没人喂。"
}
}

View File

@@ -0,0 +1,36 @@
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
margin: 0;
min-height: 100vh;
overflow-x: hidden;
transition: background 0.5s ease;
}
/* Hand-drawn Comic Theme Background - NEW VIBRANT VERSION */
body.theme-comic {
background: linear-gradient(-45deg, #ff7e5f, #feb47b, #ffcc80, #ffecb3);
background-size: 400% 400%;
animation: gradientBG 15s ease infinite;
}
@keyframes gradientBG {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
/* Placeholder for Emoji Theme Background */
body.theme-emoji {
background-color: #fffde7;
}
/* Placeholder for Retro TV Theme Background */
body.theme-retro {
background-color: #3d2b1f;
}

View File

@@ -0,0 +1,199 @@
@import url('https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap');
/* --- General & Theme Switcher --- */
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
.theme-switcher {
position: fixed;
top: 15px;
right: 15px;
display: flex;
gap: 5px;
background: rgba(255, 255, 255, 0.8);
padding: 5px;
border-radius: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
z-index: 100;
}
.theme-icon {
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: all 0.2s ease;
border: 2px solid transparent;
}
.theme-icon.active {
border-color: #ff7043;
transform: scale(1.1);
}
/* --- Comic Theme Styles --- */
.theme-comic header h1 {
font-family: 'Zhi Mang Xing', cursive;
font-size: 4em;
color: #d84315; /* Deep Orange */
text-shadow: 2px 2px 0 #fff;
margin: 0.2em 0;
}
.theme-comic .divider {
height: 3px;
background: linear-gradient(90deg, #ffca28, #ff7043, #29b6f6, #66bb6a);
border-radius: 3px;
margin: 20px auto;
width: 80%;
}
.theme-comic .joke-card {
background: rgba(255, 255, 255, 0.85); /* White with transparency */
backdrop-filter: blur(5px);
border-radius: 15px;
padding: 40px;
min-height: 200px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
display: flex;
justify-content: center;
align-items: center;
position: relative;
margin-bottom: 20px;
transform: rotate(-1deg);
transition: transform 0.2s ease;
}
.theme-comic .joke-card:hover {
transform: rotate(1deg) scale(1.02);
}
.theme-comic .joke-text {
font-family: 'Zhi Mang Xing', cursive;
font-size: 2em;
line-height: 1.6;
color: #5d4037;
}
.theme-comic .new-joke-btn {
background: #1e88e5; /* Vibrant Blue */
color: white;
font-family: 'Zhi Mang Xing', cursive;
font-size: 2.5em;
border: none;
border-radius: 50px;
padding: 10px 30px;
cursor: pointer;
box-shadow: 0 5px 0 #1565c0; /* Darker Blue */
transition: all 0.1s ease-in-out;
}
.theme-comic .new-joke-btn:active {
transform: translateY(5px);
box-shadow: none;
}
/* --- Loading Animation --- */
.loading-container { display: none; }
.loading-container.visible { display: block; }
.loading-anim {
height: 60px;
width: 80px;
margin: 0 auto 10px;
}
.book {
transform-style: preserve-3d;
transform: rotateY(-30deg);
animation: flip 3s infinite;
}
.book, .book-page {
width: 40px;
height: 55px;
position: absolute;
left: 50%;
top: 50%;
margin-left: -20px;
margin-top: -27.5px;
}
.book-page {
background: #ffca28;
border: 1px solid #ff7043;
border-radius: 3px;
transform-origin: left;
}
.book-page:nth-child(1) { animation: flip-page 3s infinite; }
.book-page:nth-child(2) { animation: flip-page 3s -1s infinite; }
.book-page:nth-child(3) { animation: flip-page 3s -2s infinite; }
@keyframes flip { 50% { transform: rotateY(30deg); } }
@keyframes flip-page { 30%, 100% { transform: rotateY(180deg); } }
/* --- Feedback Buttons & Animations --- */
.feedback-buttons {
display: flex;
justify-content: center;
gap: 15px;
margin-bottom: 30px;
}
.feedback-btn {
background: none;
border: none;
font-size: 2em;
cursor: pointer;
transition: transform 0.2s ease;
}
.feedback-btn:hover { transform: scale(1.2); }
#animation-container {
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
pointer-events: none; z-index: 999;
}
.confetti, .snowflake {
position: absolute;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.confetti {
width: 10px; height: 10px;
animation-name: fall;
}
.snowflake {
font-size: 20px; color: #fff;
animation-name: fall;
}
@keyframes fall {
from { transform: translateY(-10vh) rotate(0deg); }
to { transform: translateY(110vh) rotate(360deg); }
}
.joke-card.absurd {
animation: absurd-flash 0.5s 2;
}
@keyframes absurd-flash {
0%, 100% { border: 2px solid transparent; }
50% { border: 5px solid red; }
}
/* --- General Joke Text Visibility --- */
.joke-text {
opacity: 0;
transform: scale(0.9);
transition: opacity 0.4s ease, transform 0.4s ease;
}
.joke-text.visible {
opacity: 1;
transform: scale(1);
}
/* --- Responsive --- */
@media (max-width: 600px) {
.theme-comic header h1 { font-size: 3em; }
.theme-comic .joke-card { padding: 25px; transform: rotate(0); }
.theme-comic .joke-card:hover { transform: rotate(0); }
.theme-comic .joke-text { font-size: 1.5em; }
}

View File

@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>段子游乐场</title>
<link rel="stylesheet" href="css/background.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body class="theme-comic"> <!-- Default Theme -->
<div class="theme-switcher">
<div class="theme-icon" data-theme="theme-comic" title="手绘漫画">✏️</div>
<div class="theme-icon" data-theme="theme-emoji" title="表情包狂欢">😂</div>
<div class="theme-icon" data-theme="theme-retro" title="复古电视">📺</div>
</div>
<div class="container">
<header>
<h1>段子游乐场</h1>
<div class="divider"></div>
</header>
<main>
<div id="joke-card" class="joke-card">
<div class="loading-container">
<div class="loading-anim">
<div class="book">
<div class="book-page"></div>
<div class="book-page"></div>
<div class="book-page"></div>
</div>
</div>
<p>段子菌正在翻笑话库...</p>
</div>
<p id="joke-text" class="joke-text"></p>
</div>
<div class="feedback-buttons">
<button id="btn-lol" class="feedback-btn" title="笑到拍桌">🤣</button>
<button id="btn-cold" class="feedback-btn" title="有点冷">🥶</button>
<button id="btn-seen" class="feedback-btn" title="似曾相识">🤔</button>
<button id="btn-absurd" class="feedback-btn" title="离谱但好笑">🤯</button>
</div>
<button id="new-joke-btn" class="new-joke-btn">
<span class="btn-text">再来一个!</span>
</button>
</main>
</div>
<!-- Animation & Sound Containers -->
<div id="animation-container"></div>
<audio id="sound-lol" src="https://www.myinstants.com/media/sounds/yay-6326.mp3" preload="auto"></audio>
<audio id="sound-cold" src="https://www.myinstants.com/media/sounds/zapsplat_cartoon_whoosh_fast_swoosh_001_76761.mp3" preload="auto"></audio>
<script src="js/script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,122 @@
document.addEventListener('DOMContentLoaded', () => {
// Elements
const body = document.body;
const jokeTextElem = document.getElementById('joke-text');
const newJokeBtn = document.getElementById('new-joke-btn');
const loadingContainer = document.querySelector('.loading-container');
const animationContainer = document.getElementById('animation-container');
const jokeCard = document.getElementById('joke-card');
// API
const apiBaseUrls = ["https://60s.api.shumengya.top", "https://60s-cf.viki.moe", "https://60s.viki.moe"];
const apiPath = "/v2/duanzi";
let currentApiIndex = 0;
// --- Core Functions ---
const showLoading = (isLoading) => {
loadingContainer.classList.toggle('visible', isLoading);
if (isLoading) jokeTextElem.classList.remove('visible');
};
const displayJoke = (joke) => {
jokeTextElem.textContent = joke;
showLoading(false);
setTimeout(() => jokeTextElem.classList.add('visible'), 50);
};
const fetchJoke = async () => {
showLoading(true);
try {
const url = apiBaseUrls[currentApiIndex] + apiPath;
const response = await fetch(url, { timeout: 5000 });
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
if (data.code === 200 && data.data && data.data.duanzi) {
displayJoke(data.data.duanzi);
} else {
throw new Error('Invalid data format');
}
} catch (error) {
console.error(`API error with ${apiBaseUrls[currentApiIndex]}:`, error);
currentApiIndex = (currentApiIndex + 1) % apiBaseUrls.length;
if (currentApiIndex !== 0) {
fetchJoke(); // Try next API
} else {
displayJoke('段子菌迷路了!点击‘再来一个’让它重新找路~');
}
}
};
// --- Theme Switcher ---
const themeSwitcher = document.querySelector('.theme-switcher');
themeSwitcher.addEventListener('click', (e) => {
if (e.target.classList.contains('theme-icon')) {
const theme = e.target.dataset.theme;
body.className = theme; // Set body class to the selected theme
// Update active icon
themeSwitcher.querySelectorAll('.theme-icon').forEach(icon => icon.classList.remove('active'));
e.target.classList.add('active');
alert(`主题已切换!部分主题(如表情包、复古电视)将在后续阶段实现。`);
}
});
// Set initial active theme icon
themeSwitcher.querySelector(`[data-theme="${body.className}"]`).classList.add('active');
// --- Feedback Buttons & Animations ---
const btnLol = document.getElementById('btn-lol');
const btnCold = document.getElementById('btn-cold');
const btnSeen = document.getElementById('btn-seen');
const btnAbsurd = document.getElementById('btn-absurd');
const soundLol = document.getElementById('sound-lol');
const soundCold = document.getElementById('sound-cold');
btnLol.addEventListener('click', () => {
soundLol.play();
createParticles(20, 'confetti');
});
btnCold.addEventListener('click', () => {
soundCold.play();
createParticles(15, 'snowflake');
});
btnSeen.addEventListener('click', () => {
displayJoke("原来你也听过!那再给你换个新鲜的~");
setTimeout(fetchJoke, 1500);
});
btnAbsurd.addEventListener('click', () => {
jokeCard.classList.add('absurd');
setTimeout(() => jokeCard.classList.remove('absurd'), 1000);
});
function createParticles(count, type) {
animationContainer.innerHTML = ''; // Clear previous
const colors = ['#ffca28', '#ff7043', '#29b6f6', '#66bb6a'];
for (let i = 0; i < count; i++) {
const particle = document.createElement('div');
particle.classList.add(type);
if (type === 'confetti') {
particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
} else {
particle.textContent = '❄️';
}
particle.style.left = `${Math.random() * 100}vw`;
const duration = Math.random() * 3 + 2; // 2-5 seconds
const delay = Math.random() * -duration; // Start at different times
particle.style.animationDuration = `${duration}s`;
particle.style.animationDelay = `${delay}s`;
animationContainer.appendChild(particle);
}
}
// --- Event Listeners ---
newJokeBtn.addEventListener('click', fetchJoke);
// --- Initial Load ---
fetchJoke();
});

View File

@@ -0,0 +1,47 @@
{
"api_name": "60s-api",
"api_version": "2.22.1",
"api_docs": "https://docs.60s-api.viki.moe",
"author": "Viki <hi@viki.moe>",
"user_group": "595941841",
"github_repo": "https://github.com/vikiboss/60s",
"updated": "2025/09/01 11:12:08",
"updated_at": 1756696328000,
"endpoints": [
"/v2/60s",
"/v2/answer",
"/v2/baike",
"/v2/bili",
"/v2/bing",
"/v2/changya",
"/v2/chemical",
"/v2/douyin",
"/v2/duanzi",
"/v2/epic",
"/v2/exchange_rate",
"/v2/fabing",
"/v2/hitokoto",
"/v2/ip",
"/v2/kfc",
"/v2/luck",
"/v2/maoyan",
"/v2/today_in_history",
"/v2/toutiao",
"/v2/weibo",
"/v2/zhihu",
"/v2/lunar",
"/v2/ai-news",
"/v2/awesome-js",
"/v2/qrcode",
"/v2/dad-joke",
"/v2/hacker-news/:type",
"/v2/og",
"/v2/hash",
"/v2/fanyi",
"/v2/fanyi/langs",
"/v2/weather",
"/v2/weather/forecast",
"/v2/ncm-rank",
"/v2/ncm-rank/:id"
]
}

View File

@@ -0,0 +1,8 @@
{
"code": 200,
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s反馈群 595941841",
"data": {
"index": 347,
"duanzi": "我不想读书,主要是因为家里牛啊,猪啊羊啊都没人喂。"
}
}

View File

@@ -0,0 +1,26 @@
body {
background: linear-gradient(-45deg, #0a021a, #2a0d3f, #4a1a6c, #7b2f8f);
background-size: 400% 400%;
animation: gradientBG 20s ease infinite;
color: #ffffff;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow-x: hidden;
}
@keyframes gradientBG {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}

View File

@@ -0,0 +1,342 @@
.container {
text-align: center;
padding: 20px;
max-width: 600px;
width: 100%;
box-sizing: border-box;
}
header h1 {
font-size: 2.8em;
color: #f0e6ff;
text-shadow: 0 0 10px #d1a9ff, 0 0 20px #d1a9ff;
margin-bottom: 0.2em;
}
header p {
font-size: 1.2em;
color: #e0c8ff;
margin-bottom: 40px;
}
.crystal-ball-container {
perspective: 1000px;
margin-bottom: 40px;
}
.crystal-ball {
width: 200px;
height: 200px;
background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.6), rgba(200, 180, 255, 0.1));
border-radius: 50%;
margin: 0 auto;
position: relative;
box-shadow: 0 0 30px #c390ff, 0 0 60px #a060e0, inset 0 0 20px rgba(255, 220, 255, 0.3);
animation: float 6s ease-in-out infinite;
transform-style: preserve-3d;
}
.reflection {
width: 80px;
height: 40px;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
position: absolute;
top: 20px;
left: 40px;
transform: rotate(-30deg);
filter: blur(5px);
}
.swirl {
position: absolute;
top: 50%;
left: 50%;
width: 120%;
height: 120%;
background: linear-gradient(45deg, rgba(255, 192, 203, 0.1), rgba(128, 0, 128, 0.2));
border-radius: 50%;
animation: swirl 10s linear infinite;
transform: translate(-50%, -50%);
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
@keyframes swirl {
from { transform: translate(-50%, -50%) rotate(0deg); }
to { transform: translate(-50%, -50%) rotate(360deg); }
}
.fortune-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
padding: 30px;
margin-bottom: 30px;
min-height: 120px;
display: flex;
justify-content: center;
align-items: center;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
transition: opacity 0.5s ease-in-out;
}
.fortune-content {
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
.fortune-content.visible {
opacity: 1;
}
#luck-desc {
font-size: 2em;
color: #ffc0cb;
margin: 0 0 10px;
}
#luck-tip {
font-size: 1.1em;
color: #e0e0e0;
margin: 0;
padding-bottom: 20px; /* Add some space before the new details */
}
.fortune-details {
display: flex;
justify-content: space-around;
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.detail-item {
text-align: center;
}
.detail-item h3 {
font-size: 0.9em;
color: #ffc0cb;
margin: 0 0 5px;
font-weight: normal;
}
.detail-item p {
font-size: 1.2em;
margin: 0;
font-weight: bold;
}
#lucky-color {
display: inline-block;
width: 24px;
height: 24px;
border-radius: 50%;
border: 2px solid white;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
/* Remove the text content */
font-size: 0;
}
/* Tarot Card Styles */
.tarot-container {
margin-top: 40px;
margin-bottom: 40px;
}
.tarot-container h2 {
font-size: 1.5em;
color: #f0e6ff;
text-shadow: 0 0 8px #d1a9ff;
margin-bottom: 20px;
}
.tarot-card-container {
width: 180px;
height: 280px;
perspective: 1000px;
margin: 0 auto;
cursor: pointer;
}
.tarot-card-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform 0.8s;
transform-style: preserve-3d;
}
.tarot-card-container.flipped .tarot-card-inner {
transform: rotateY(180deg);
}
.tarot-card-front,
.tarot-card-back {
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
}
.tarot-card-back {
background: linear-gradient(135deg, #4a1a6c, #2a0d3f);
border: 2px solid #d1a9ff;
display: flex;
justify-content: center;
align-items: center;
font-size: 3em;
color: #d1a9ff;
}
.tarot-card-back::after {
content: '✧'; /* A simple star symbol */
text-shadow: 0 0 10px #f0e6ff;
}
.tarot-card-front {
background: linear-gradient(135deg, #3e165b, #592883);
border: 2px solid #d1a9ff;
color: white;
transform: rotateY(180deg);
padding: 20px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
#tarot-name {
font-size: 1.4em;
color: #ffc0cb;
margin: 0 0 10px;
}
#tarot-interpretation {
font-size: 0.9em;
text-align: center;
margin: 0;
}
/* Side Decorations */
.side-decor {
position: fixed;
top: 0;
bottom: 0;
width: 15vw;
height: 100vh;
pointer-events: none;
z-index: 0;
}
.left-decor {
left: 0;
}
.right-decor {
right: 0;
}
.decor-symbol {
position: absolute;
color: rgba(209, 169, 255, 0.5);
text-shadow: 0 0 10px rgba(240, 230, 255, 0.7);
animation: floatSymbol 20s infinite ease-in-out;
}
@keyframes floatSymbol {
0%, 100% {
transform: translateY(0) rotate(0deg);
opacity: 0;
}
25%, 75% {
opacity: 0.8;
}
50% {
transform: translateY(-20vh) rotate(180deg);
opacity: 0.3;
}
}
#get-fortune-btn {
background: linear-gradient(45deg, #da70d6, #8a2be2);
color: white;
border: none;
border-radius: 50px;
padding: 15px 30px;
font-size: 1.1em;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
box-shadow: 0 0 15px #c390ff;
}
#get-fortune-btn:hover {
transform: scale(1.05);
box-shadow: 0 0 25px #d1a9ff;
}
#get-fortune-btn:active {
transform: scale(0.98);
}
.loading-spinner {
border: 4px solid rgba(255, 255, 255, 0.2);
border-left-color: #ffc0cb;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
display: none; /* Hidden by default */
}
.loading-spinner.visible {
display: block;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
footer {
margin-top: 40px;
color: rgba(255, 255, 255, 0.6);
}
/* Responsive Design */
@media (max-width: 768px) {
header h1 {
font-size: 2.2em;
}
.crystal-ball {
width: 150px;
height: 150px;
}
.fortune-card {
padding: 20px;
}
.fortune-details {
flex-direction: column;
gap: 15px;
}
.tarot-card-container {
width: 150px;
height: 233px;
}
}
/* Hide side decor on smaller screens */
@media (max-width: 1200px) {
.side-decor {
display: none;
}
}

View File

@@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>水晶球占卜</title>
<link rel="stylesheet" href="css/background.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="side-decor left-decor"></div>
<div class="container">
<header>
<h1>水晶球占卜</h1>
<p>洞察你今日的运势</p>
</header>
<main>
<div class="crystal-ball-container">
<div class="crystal-ball">
<div class="reflection"></div>
<div class="swirl"></div>
</div>
</div>
<div id="fortune-card" class="fortune-card">
<div class="loading-spinner"></div>
<div class="fortune-content">
<h2 id="luck-desc"></h2>
<p id="luck-tip"></p>
<div class="fortune-details">
<div class="detail-item">
<h3>今日咒语</h3>
<p id="fortune-summary"></p>
</div>
<div class="detail-item">
<h3>幸运色</h3>
<p id="lucky-color"></p>
</div>
<div class="detail-item">
<h3>幸运数字</h3>
<p id="lucky-number"></p>
</div>
</div>
</div>
</div>
<!-- New Tarot Card Section -->
<div class="tarot-container">
<h2>每日塔罗指引</h2>
<div id="tarot-card" class="tarot-card-container">
<div class="tarot-card-inner">
<div class="tarot-card-back">
<!-- Back of the card design -->
</div>
<div class="tarot-card-front">
<h3 id="tarot-name"></h3>
<p id="tarot-interpretation"></p>
</div>
</div>
</div>
</div>
<button id="get-fortune-btn">再次占卜</button>
</main>
<footer>
<p>仅供娱乐,祝您好运</p>
</footer>
</div>
<div class="side-decor right-decor"></div>
<script src="js/script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,175 @@
document.addEventListener('DOMContentLoaded', () => {
const getFortuneBtn = document.getElementById('get-fortune-btn');
const fortuneCard = document.getElementById('fortune-card');
const fortuneContent = fortuneCard.querySelector('.fortune-content');
const luckDescElem = document.getElementById('luck-desc');
const luckTipElem = document.getElementById('luck-tip');
const fortuneSummaryElem = document.getElementById('fortune-summary');
const luckyColorElem = document.getElementById('lucky-color');
const luckyNumberElem = document.getElementById('lucky-number');
const loadingSpinner = fortuneCard.querySelector('.loading-spinner');
const tarotCardContainer = document.getElementById('tarot-card');
const tarotNameElem = document.getElementById('tarot-name');
const tarotInterpretationElem = document.getElementById('tarot-interpretation');
const apiBaseUrls = [
"https://60s.api.shumengya.top",
"https://60s-cf.viki.moe",
"https://60s.viki.moe",
"https://60s.b23.run",
"https://60s.114128.xyz",
"https://60s-cf.114128.xyz"
];
const apiPath = "/v2/luck";
const mantras = [
"顺其自然,皆是美好。",
"相信直觉,它知道方向。",
"每一次呼吸都是新的开始。",
"心怀感恩,好运自来。",
"拥抱变化,发现惊喜。",
"你的能量,超乎想象。",
"保持微笑,宇宙会回应你。"
];
const tarotDeck = [
{ name: "愚者", interpretation: "新的开始,无限的潜力,天真和自由。勇敢地迈出第一步。" },
{ name: "魔术师", interpretation: "创造力,意志力,显化。你拥有实现目标所需的一切资源。" },
{ name: "女祭司", interpretation: "直觉,潜意识,神秘。倾听你内心的声音,智慧在你之内。" },
{ name: "皇后", interpretation: "丰饶,母性,创造。享受生活的美好,与自然和谐相处。" },
{ name: "皇帝", interpretation: "权威,结构,控制。建立秩序和纪律,掌控你的生活。" },
{ name: "教皇", interpretation: "传统,信仰,灵性指导。寻求智慧和知识,遵循传统。" },
{ name: "恋人", interpretation: "爱,和谐,选择。做出与你内心价值观一致的决定。" },
{ name: "战车", interpretation: "胜利,决心,控制。以坚定的意志力克服障碍,勇往直前。" },
{ name: "力量", interpretation: "勇气,内在力量,同情。用温柔和耐心驯服内心的野兽。" },
{ name: "隐士", interpretation: "内省,孤独,寻求真理。花时间独处,向内寻求答案。" },
{ name: "命运之轮", interpretation: "变化,命运,转折点。生活总在变化,顺应潮流。" },
{ name: "正义", interpretation: "公平,真理,因果。为你的行为负责,寻求平衡。" },
{ name: "倒吊人", interpretation: "新的视角,顺从,牺牲。放手,从不同的角度看问题。" },
{ name: "死神", interpretation: "结束,转变,新生。一个周期的结束是另一个周期的开始。" },
{ name: "节制", interpretation: "平衡,和谐,耐心。融合对立的力量,找到中间道路。" },
{ name: "恶魔", interpretation: "束缚,物质主义,诱惑。认识到你的束缚,并寻求解放。" },
{ name: "塔", interpretation: "突变,启示,解放。旧的结构正在崩塌,为新的结构让路。" },
{ name: "星星", interpretation: "希望,灵感,平静。在黑暗之后,总有希望的曙光。" },
{ name: "月亮", interpretation: "幻觉,恐惧,潜意识。面对你的恐惧,相信你的直觉。" },
{ name: "太阳", interpretation: "成功,喜悦,活力。拥抱光明,享受生活的乐趣。" },
{ name: "审判", interpretation: "觉醒,重生,评估。一个反思和更新的时刻。" },
{ name: "世界", interpretation: "完成,整合,成就。一个旅程的成功结束,庆祝你的成就。" }
];
let currentApiIndex = 0;
const showLoading = (isLoading) => {
if (isLoading) {
fortuneContent.classList.remove('visible');
loadingSpinner.classList.add('visible');
} else {
loadingSpinner.classList.remove('visible');
setTimeout(() => {
fortuneContent.classList.add('visible');
}, 100);
}
};
const fetchFortune = async () => {
showLoading(true);
tarotCardContainer.classList.remove('flipped'); // Reset card on new fetch
try {
const url = apiBaseUrls[currentApiIndex] + apiPath;
const response = await fetch(url, { timeout: 5000 });
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
if (data.code === 200 && data.data) {
updateFortune(data.data);
drawTarotCard(); // Draw a tarot card on success
} else {
throw new Error('Invalid data format');
}
} catch (error) {
console.error(`API error with ${apiBaseUrls[currentApiIndex]}:`, error);
currentApiIndex = (currentApiIndex + 1) % apiBaseUrls.length;
if (currentApiIndex !== 0) {
fetchFortune(); // Try next API
} else {
displayError();
}
}
};
const updateFortune = (data) => {
luckDescElem.textContent = data.luck_desc || '运势';
luckTipElem.textContent = data.luck_tip || '今日运势平平,保持好心情。';
// Generate and display additional details
fortuneSummaryElem.textContent = mantras[Math.floor(Math.random() * mantras.length)];
luckyColorElem.style.backgroundColor = `#${Math.floor(Math.random()*16777215).toString(16).padStart(6, '0')}`;
luckyNumberElem.textContent = Math.floor(Math.random() * 100);
showLoading(false);
};
const displayError = () => {
luckDescElem.textContent = '占卜失败';
luckTipElem.textContent = '无法连接到星辰之力,请稍后再试。';
fortuneSummaryElem.textContent = '---';
luckyColorElem.style.backgroundColor = 'transparent';
luckyNumberElem.textContent = '-';
showLoading(false);
tarotNameElem.textContent = '指引中断';
tarotInterpretationElem.textContent = '星辰之力暂时无法连接。';
tarotCardContainer.classList.add('flipped'); // Show error on card
};
const drawTarotCard = () => {
const card = tarotDeck[Math.floor(Math.random() * tarotDeck.length)];
tarotNameElem.textContent = card.name;
tarotInterpretationElem.textContent = card.interpretation;
// Flip the card after a short delay to allow the main content to appear
setTimeout(() => {
tarotCardContainer.classList.add('flipped');
}, 500);
};
const createSideDecorations = () => {
const leftContainer = document.querySelector('.left-decor');
const rightContainer = document.querySelector('.right-decor');
if (!leftContainer || !rightContainer) return;
const symbols = ['✧', '✦', '☾', '✶', '✵', '✩', '✨'];
const symbolCount = 15; // Number of symbols per side
const createSymbols = (container) => {
for (let i = 0; i < symbolCount; i++) {
const symbol = document.createElement('span');
symbol.classList.add('decor-symbol');
symbol.textContent = symbols[Math.floor(Math.random() * symbols.length)];
// Randomize properties for a more natural look
symbol.style.top = `${Math.random() * 90}vh`;
symbol.style.left = `${Math.random() * 80}%`;
symbol.style.fontSize = `${Math.random() * 20 + 10}px`;
symbol.style.animationDelay = `${Math.random() * 20}s`;
symbol.style.animationDuration = `${Math.random() * 20 + 15}s`; // Duration between 15s and 35s
container.appendChild(symbol);
}
};
createSymbols(leftContainer);
createSymbols(rightContainer);
};
getFortuneBtn.addEventListener('click', fetchFortune);
tarotCardContainer.addEventListener('click', () => {
tarotCardContainer.classList.toggle('flipped');
});
// Initial actions on page load
fetchFortune();
createSideDecorations();
});

View File

@@ -0,0 +1,7 @@
[
"https://60s-cf.viki.moe",
"https://60s.viki.moe",
"https://60s.b23.run",
"https://60s.114128.xyz",
"https://60s-cf.114128.xyz"
]

View File

@@ -0,0 +1,10 @@
{
"code": 200,
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s反馈群 595941841",
"data": {
"luck_desc": "恋愛運",
"luck_rank": 21,
"luck_tip": "闪亮的邂逅之日!顺其自然吧",
"luck_tip_index": 19
}
}

View File

@@ -0,0 +1,233 @@
/* 动态背景样式 */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
z-index: -2;
}
body::after {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
z-index: -1;
animation: backgroundMove 20s ease-in-out infinite;
}
@keyframes backgroundMove {
0%, 100% {
background:
radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
}
25% {
background:
radial-gradient(circle at 60% 30%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
radial-gradient(circle at 30% 70%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
radial-gradient(circle at 80% 80%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
}
50% {
background:
radial-gradient(circle at 80% 60%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
radial-gradient(circle at 20% 30%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
radial-gradient(circle at 60% 70%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
}
75% {
background:
radial-gradient(circle at 40% 90%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
radial-gradient(circle at 70% 10%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
radial-gradient(circle at 20% 60%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
}
}
/* 浮动粒子效果 */
.particles {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: -1;
}
.particle {
position: absolute;
width: 4px;
height: 4px;
background: rgba(255, 255, 255, 0.5);
border-radius: 50%;
animation: float 15s infinite linear;
}
.particle:nth-child(1) {
left: 10%;
animation-delay: 0s;
animation-duration: 12s;
}
.particle:nth-child(2) {
left: 20%;
animation-delay: 2s;
animation-duration: 18s;
}
.particle:nth-child(3) {
left: 30%;
animation-delay: 4s;
animation-duration: 15s;
}
.particle:nth-child(4) {
left: 40%;
animation-delay: 6s;
animation-duration: 20s;
}
.particle:nth-child(5) {
left: 50%;
animation-delay: 8s;
animation-duration: 14s;
}
.particle:nth-child(6) {
left: 60%;
animation-delay: 10s;
animation-duration: 16s;
}
.particle:nth-child(7) {
left: 70%;
animation-delay: 12s;
animation-duration: 22s;
}
.particle:nth-child(8) {
left: 80%;
animation-delay: 14s;
animation-duration: 13s;
}
.particle:nth-child(9) {
left: 90%;
animation-delay: 16s;
animation-duration: 19s;
}
.particle:nth-child(10) {
left: 15%;
animation-delay: 18s;
animation-duration: 17s;
}
@keyframes float {
0% {
transform: translateY(100vh) rotate(0deg);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(-100px) rotate(360deg);
opacity: 0;
}
}
/* 网格背景效果 */
.grid-background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(rgba(255, 255, 255, 0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.1) 1px, transparent 1px);
background-size: 50px 50px;
z-index: -1;
opacity: 0.3;
animation: gridMove 30s linear infinite;
}
@keyframes gridMove {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(50px, 50px);
}
}
/* 光晕效果 */
.glow-effect {
position: fixed;
top: 50%;
left: 50%;
width: 300px;
height: 300px;
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
border-radius: 50%;
transform: translate(-50%, -50%);
z-index: -1;
animation: pulse 4s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% {
transform: translate(-50%, -50%) scale(1);
opacity: 0.5;
}
50% {
transform: translate(-50%, -50%) scale(1.2);
opacity: 0.8;
}
}
/* 响应式背景调整 */
@media (max-width: 768px) {
.grid-background {
background-size: 30px 30px;
}
.glow-effect {
width: 200px;
height: 200px;
}
.particle {
width: 3px;
height: 3px;
}
}
@media (max-width: 480px) {
.grid-background {
background-size: 20px 20px;
}
.glow-effect {
width: 150px;
height: 150px;
}
.particle {
width: 2px;
height: 2px;
}
}

View File

@@ -0,0 +1,445 @@
/* 全局样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
min-height: 100vh;
overflow-x: hidden;
}
/* 容器样式 */
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
position: relative;
z-index: 1;
}
/* 头部样式 */
.header {
text-align: center;
padding: 3rem 2rem 2rem;
background: linear-gradient(135deg, rgba(74, 144, 226, 0.1), rgba(80, 200, 120, 0.1));
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
.header h1 {
font-size: 2.5rem;
font-weight: 700;
background: linear-gradient(135deg, #4a90e2, #50c878);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 0.5rem;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.header h1 i {
margin-right: 0.5rem;
background: linear-gradient(135deg, #4a90e2, #50c878);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle {
font-size: 1.1rem;
color: #666;
font-weight: 300;
}
/* 主要内容区域 */
.main-content {
flex: 1;
padding: 2rem;
max-width: 800px;
margin: 0 auto;
width: 100%;
}
/* 查询按钮区域 */
.query-section {
text-align: center;
margin-bottom: 2rem;
}
.query-btn {
background: linear-gradient(135deg, #4a90e2, #50c878);
color: white;
border: none;
padding: 1rem 2rem;
font-size: 1.1rem;
font-weight: 600;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(74, 144, 226, 0.3);
display: inline-flex;
align-items: center;
gap: 0.5rem;
min-width: 200px;
justify-content: center;
}
.query-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(74, 144, 226, 0.4);
background: linear-gradient(135deg, #3a7bc8, #40a868);
}
.query-btn:active {
transform: translateY(0);
}
.query-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
/* 加载动画 */
.loading {
text-align: center;
padding: 2rem;
}
.spinner {
width: 50px;
height: 50px;
border: 4px solid #f3f3f3;
border-top: 4px solid #4a90e2;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 1rem;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading p {
color: #666;
font-size: 1rem;
}
/* IP信息卡片 */
.ip-info {
animation: fadeInUp 0.6s ease;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.ip-card {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 2rem;
margin-bottom: 2rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.ip-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 2px solid #f0f0f0;
}
.ip-header i {
font-size: 1.5rem;
color: #4a90e2;
}
.ip-header h2 {
font-size: 1.5rem;
font-weight: 600;
color: #333;
}
.ip-display {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
margin-bottom: 1.5rem;
padding: 1.5rem;
background: linear-gradient(135deg, rgba(74, 144, 226, 0.1), rgba(80, 200, 120, 0.1));
border-radius: 15px;
border: 2px solid rgba(74, 144, 226, 0.2);
}
.ip-address {
font-size: 2rem;
font-weight: 700;
font-family: 'Courier New', monospace;
color: #2c3e50;
background: linear-gradient(135deg, #4a90e2, #50c878);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.copy-btn {
background: #4a90e2;
color: white;
border: none;
padding: 0.5rem;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 1rem;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.copy-btn:hover {
background: #3a7bc8;
transform: scale(1.1);
}
.ip-details {
display: grid;
gap: 1rem;
}
.detail-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
background: rgba(248, 249, 250, 0.8);
border-radius: 10px;
transition: all 0.3s ease;
}
.detail-item:hover {
background: rgba(74, 144, 226, 0.1);
transform: translateX(5px);
}
.detail-item i {
color: #4a90e2;
width: 20px;
text-align: center;
}
.detail-item .label {
font-weight: 600;
color: #555;
min-width: 80px;
}
.detail-item .value {
color: #333;
font-weight: 500;
}
/* IP地址说明区域 */
.ip-explanation {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.ip-explanation h3 {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
font-size: 1.3rem;
color: #333;
}
.ip-explanation h3 i {
color: #4a90e2;
}
.ip-explanation p {
color: #666;
line-height: 1.8;
margin-bottom: 1.5rem;
}
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
}
.feature-item {
display: flex;
align-items: flex-start;
gap: 1rem;
padding: 1rem;
background: rgba(248, 249, 250, 0.8);
border-radius: 12px;
transition: all 0.3s ease;
}
.feature-item:hover {
background: rgba(74, 144, 226, 0.1);
transform: translateY(-2px);
}
.feature-item i {
color: #4a90e2;
font-size: 1.5rem;
margin-top: 0.2rem;
}
.feature-item h4 {
font-size: 1rem;
font-weight: 600;
color: #333;
margin-bottom: 0.3rem;
}
.feature-item p {
font-size: 0.9rem;
color: #666;
line-height: 1.5;
margin: 0;
}
/* 错误信息 */
.error-message {
text-align: center;
padding: 2rem;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
border-radius: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 99, 99, 0.3);
animation: fadeInUp 0.6s ease;
}
.error-message i {
font-size: 3rem;
color: #ff6b6b;
margin-bottom: 1rem;
}
.error-message p {
color: #666;
font-size: 1.1rem;
margin-bottom: 1.5rem;
}
.retry-btn {
background: #ff6b6b;
color: white;
border: none;
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: 600;
border-radius: 25px;
cursor: pointer;
transition: all 0.3s ease;
}
.retry-btn:hover {
background: #ff5252;
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
}
/* 页脚 */
.footer {
text-align: center;
padding: 2rem;
background: rgba(248, 249, 250, 0.8);
backdrop-filter: blur(10px);
border-top: 1px solid rgba(255, 255, 255, 0.2);
color: #666;
font-size: 0.9rem;
}
/* 响应式设计 */
@media (max-width: 768px) {
.header {
padding: 2rem 1rem 1.5rem;
}
.header h1 {
font-size: 2rem;
}
.main-content {
padding: 1rem;
}
.ip-card, .ip-explanation {
padding: 1.5rem;
}
.ip-address {
font-size: 1.5rem;
}
.ip-display {
flex-direction: column;
gap: 1rem;
}
.features {
grid-template-columns: 1fr;
}
.detail-item {
flex-direction: column;
align-items: flex-start;
gap: 0.3rem;
}
.detail-item .label {
min-width: auto;
}
}
@media (max-width: 480px) {
.header h1 {
font-size: 1.8rem;
}
.query-btn {
padding: 0.875rem 1.5rem;
font-size: 1rem;
min-width: 180px;
}
.ip-address {
font-size: 1.3rem;
}
.ip-card, .ip-explanation {
padding: 1rem;
}
}

View File

@@ -0,0 +1,139 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>公网IP地址查询</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/background.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
</head>
<body>
<div class="container">
<!-- 头部 -->
<header class="header">
<h1><i class="fas fa-globe"></i> 公网IP地址查询</h1>
<p class="subtitle">快速获取您的公网IP地址信息</p>
</header>
<!-- 主要内容区域 -->
<main class="main-content">
<!-- 查询按钮区域 -->
<div class="query-section">
<button id="queryBtn" class="query-btn">
<i class="fas fa-search"></i>
<span>查询我的IP地址</span>
</button>
</div>
<!-- 加载动画 -->
<div id="loading" class="loading" style="display: none;">
<div class="spinner"></div>
<p>正在获取IP地址信息...</p>
</div>
<!-- IP信息展示区域 -->
<div id="ip-info" class="ip-info" style="display: none;">
<div class="ip-card">
<div class="ip-header">
<i class="fas fa-network-wired"></i>
<h2>您的公网IP地址</h2>
</div>
<div class="ip-display">
<span id="ip-address" class="ip-address">---.---.---.---</span>
<button id="copyBtn" class="copy-btn" title="复制IP地址">
<i class="fas fa-copy"></i>
</button>
</div>
<div class="ip-details">
<div class="detail-item">
<i class="fas fa-clock"></i>
<span class="label">查询时间:</span>
<span id="query-time" class="value">--</span>
</div>
<div class="detail-item">
<i class="fas fa-server"></i>
<span class="label">数据来源:</span>
<span class="value">60s.viki.moe</span>
</div>
<div class="detail-item">
<i class="fas fa-map-marker-alt"></i>
<span class="label">位置信息:</span>
<span id="location" class="value">--</span>
</div>
<div class="detail-item">
<i class="fas fa-building"></i>
<span class="label">网络服务商:</span>
<span id="isp" class="value">--</span>
</div>
<div class="detail-item">
<i class="fas fa-flag"></i>
<span class="label">国家:</span>
<span id="country" class="value">--</span>
</div>
<div class="detail-item">
<i class="fas fa-map"></i>
<span class="label">地区:</span>
<span id="region" class="value">--</span>
</div>
<div class="detail-item">
<i class="fas fa-city"></i>
<span class="label">城市:</span>
<span id="city" class="value">--</span>
</div>
<div class="detail-item">
<i class="fas fa-clock"></i>
<span class="label">时区:</span>
<span id="timezone" class="value">--</span>
</div>
</div>
</div>
<!-- IP地址信息说明 -->
<div class="ip-explanation">
<h3><i class="fas fa-info-circle"></i> 什么是公网IP地址</h3>
<p>公网IP地址是您的设备在互联网上的唯一标识符由您的网络服务提供商(ISP)分配。通过这个地址,互联网上的其他设备可以找到并与您的设备通信。</p>
<div class="features">
<div class="feature-item">
<i class="fas fa-shield-alt"></i>
<div>
<h4>隐私保护</h4>
<p>了解您的IP地址有助于保护网络隐私</p>
</div>
</div>
<div class="feature-item">
<i class="fas fa-map-marker-alt"></i>
<div>
<h4>地理位置</h4>
<p>IP地址可以大致确定您的地理位置</p>
</div>
</div>
<div class="feature-item">
<i class="fas fa-cogs"></i>
<div>
<h4>网络配置</h4>
<p>用于网络故障排除和配置</p>
</div>
</div>
</div>
</div>
</div>
<!-- 错误信息 -->
<div id="error-message" class="error-message" style="display: none;">
<i class="fas fa-exclamation-triangle"></i>
<p>获取IP地址失败请检查网络连接或稍后重试</p>
<button id="retryBtn" class="retry-btn">重试</button>
</div>
</main>
<!-- 页脚 -->
<footer class="footer">
<p>&copy; 2024 公网IP地址查询工具 | 数据来源: 60s.viki.moe</p>
</footer>
</div>
<script src="js/script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,315 @@
// 公网IP地址查询应用
class IPQueryApp {
constructor() {
this.apiEndpoint = 'https://60s.viki.moe/v2/ip';
this.init();
}
// 初始化应用
init() {
this.bindEvents();
this.createParticles();
this.createBackgroundElements();
console.log('IP查询应用初始化完成');
}
// 绑定事件
bindEvents() {
const queryBtn = document.getElementById('queryBtn');
const retryBtn = document.getElementById('retryBtn');
const copyBtn = document.getElementById('copyBtn');
if (queryBtn) {
queryBtn.addEventListener('click', () => this.queryIP());
}
if (retryBtn) {
retryBtn.addEventListener('click', () => this.queryIP());
}
if (copyBtn) {
copyBtn.addEventListener('click', () => this.copyIP());
}
// 页面加载完成后自动查询一次
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => this.queryIP(), 500);
});
}
// 创建浮动粒子
createParticles() {
const particlesContainer = document.createElement('div');
particlesContainer.className = 'particles';
document.body.appendChild(particlesContainer);
for (let i = 0; i < 10; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particlesContainer.appendChild(particle);
}
}
// 创建背景元素
createBackgroundElements() {
// 创建网格背景
const gridBackground = document.createElement('div');
gridBackground.className = 'grid-background';
document.body.appendChild(gridBackground);
// 创建光晕效果
const glowEffect = document.createElement('div');
glowEffect.className = 'glow-effect';
document.body.appendChild(glowEffect);
}
// 显示加载状态
showLoading() {
const loading = document.getElementById('loading');
const ipInfo = document.getElementById('ipInfo');
const errorMessage = document.getElementById('errorMessage');
const queryBtn = document.getElementById('queryBtn');
if (loading) loading.style.display = 'block';
if (ipInfo) ipInfo.style.display = 'none';
if (errorMessage) errorMessage.style.display = 'none';
if (queryBtn) {
queryBtn.disabled = true;
queryBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 查询中...';
}
}
// 隐藏加载状态
hideLoading() {
const loading = document.getElementById('loading');
const queryBtn = document.getElementById('queryBtn');
if (loading) loading.style.display = 'none';
if (queryBtn) {
queryBtn.disabled = false;
queryBtn.innerHTML = '<i class="fas fa-search"></i> 查询我的IP';
}
}
// 显示错误信息
showError(message) {
const errorMessage = document.getElementById('error-message');
const ipInfo = document.getElementById('ip-info');
if (errorMessage) {
errorMessage.style.display = 'block';
const errorText = errorMessage.querySelector('p');
if (errorText) errorText.textContent = message || '获取IP信息失败请稍后重试';
}
if (ipInfo) ipInfo.style.display = 'none';
this.hideLoading();
}
// 查询IP地址
async queryIP() {
try {
this.showLoading();
console.log('开始查询IP地址...');
const response = await fetch(this.apiEndpoint, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
console.log('API响应状态:', response.status);
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`);
}
const data = await response.json();
console.log('API返回数据:', data);
if (data.code === 200 && data.data) {
this.displayIPInfo(data.data);
} else {
throw new Error(data.message || '获取IP信息失败');
}
} catch (error) {
console.error('查询IP失败:', error);
this.showError(error.message);
}
}
// 显示IP信息
displayIPInfo(data) {
const ipInfo = document.getElementById('ip-info');
const errorMessage = document.getElementById('error-message');
// 更新IP地址显示
const ipAddressElement = document.getElementById('ip-address');
if (ipAddressElement && data.ip) {
ipAddressElement.textContent = data.ip;
}
// 更新查询时间
const queryTimeElement = document.getElementById('query-time');
if (queryTimeElement) {
const now = new Date();
queryTimeElement.textContent = now.toLocaleString('zh-CN');
}
// 更新详细信息
this.updateDetailItem('location', data.location || '未知');
this.updateDetailItem('isp', data.isp || '未知');
this.updateDetailItem('country', data.country || '未知');
this.updateDetailItem('region', data.region || '未知');
this.updateDetailItem('city', data.city || '未知');
this.updateDetailItem('timezone', data.timezone || '未知');
// 显示IP信息隐藏错误信息
if (ipInfo) ipInfo.style.display = 'block';
if (errorMessage) errorMessage.style.display = 'none';
this.hideLoading();
console.log('IP信息显示完成');
}
// 更新详细信息项
updateDetailItem(id, value) {
const element = document.getElementById(id);
if (element) {
element.textContent = value;
}
}
// 复制IP地址
async copyIP() {
const ipAddressElement = document.getElementById('ip-address');
const copyBtn = document.getElementById('copyBtn');
if (!ipAddressElement || !ipAddressElement.textContent) {
this.showToast('没有可复制的IP地址', 'error');
return;
}
try {
await navigator.clipboard.writeText(ipAddressElement.textContent);
// 更新按钮状态
if (copyBtn) {
const originalHTML = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="fas fa-check"></i>';
copyBtn.style.background = '#50c878';
setTimeout(() => {
copyBtn.innerHTML = originalHTML;
copyBtn.style.background = '#4a90e2';
}, 1500);
}
this.showToast('IP地址已复制到剪贴板', 'success');
console.log('IP地址已复制:', ipAddressElement.textContent);
} catch (error) {
console.error('复制失败:', error);
this.showToast('复制失败,请手动选择复制', 'error');
}
}
// 显示提示消息
showToast(message, type = 'info') {
// 移除已存在的toast
const existingToast = document.querySelector('.toast');
if (existingToast) {
existingToast.remove();
}
// 创建新的toast
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.innerHTML = `
<i class="fas fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'}"></i>
<span>${message}</span>
`;
// 添加toast样式
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${type === 'success' ? '#50c878' : type === 'error' ? '#ff6b6b' : '#4a90e2'};
color: white;
padding: 1rem 1.5rem;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
z-index: 1000;
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 500;
animation: slideInRight 0.3s ease;
max-width: 300px;
`;
// 添加动画样式
const style = document.createElement('style');
style.textContent = `
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOutRight {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
`;
document.head.appendChild(style);
document.body.appendChild(toast);
// 3秒后自动移除
setTimeout(() => {
toast.style.animation = 'slideOutRight 0.3s ease';
setTimeout(() => {
if (toast.parentNode) {
toast.remove();
}
if (style.parentNode) {
style.remove();
}
}, 300);
}, 3000);
}
// 格式化时间
formatTime(timestamp) {
const date = new Date(timestamp);
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
}
// 初始化应用
const app = new IPQueryApp();
// 导出到全局作用域(用于调试)
window.IPQueryApp = app;

View File

@@ -0,0 +1,3 @@
[
"https://60s.viki.moe/v2/ip"
]

View File

@@ -0,0 +1,561 @@
/* Reset and Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: #333;
overflow-x: hidden;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
position: relative;
}
/* Header Styles */
.header {
text-align: center;
margin-bottom: 40px;
position: relative;
z-index: 2;
}
.header-content {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border-radius: 24px;
padding: 40px;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
}
.header-content::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(45deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%, rgba(255, 255, 255, 0.1) 100%);
animation: shimmer 3s ease-in-out infinite;
}
@keyframes shimmer {
0%, 100% { transform: translateX(-100%); }
50% { transform: translateX(100%); }
}
.logo {
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
margin-bottom: 15px;
}
.logo i {
font-size: 48px;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.logo h1 {
font-size: 42px;
font-weight: 700;
color: white;
text-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
.subtitle {
font-size: 18px;
color: rgba(255, 255, 255, 0.9);
font-weight: 400;
letter-spacing: 0.5px;
}
/* Floating Shapes */
.header-decoration {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
z-index: 1;
}
.floating-shapes {
position: relative;
width: 100%;
height: 100%;
}
.shape {
position: absolute;
border-radius: 50%;
background: linear-gradient(45deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
animation: float 6s ease-in-out infinite;
}
.shape-1 {
width: 80px;
height: 80px;
top: 10%;
left: 10%;
animation-delay: 0s;
}
.shape-2 {
width: 60px;
height: 60px;
top: 20%;
right: 15%;
animation-delay: 2s;
}
.shape-3 {
width: 100px;
height: 100px;
bottom: 15%;
left: 20%;
animation-delay: 4s;
}
@keyframes float {
0%, 100% { transform: translateY(0px) rotate(0deg); }
33% { transform: translateY(-20px) rotate(120deg); }
66% { transform: translateY(10px) rotate(240deg); }
}
/* Input Section */
.input-section {
margin-bottom: 40px;
}
.input-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
border-radius: 20px;
padding: 30px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.3);
transition: all 0.3s ease;
}
.input-card:hover {
transform: translateY(-5px);
box-shadow: 0 30px 60px rgba(0, 0, 0, 0.15);
}
.card-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
}
.card-header i {
font-size: 24px;
color: #667eea;
}
.card-header h2 {
font-size: 24px;
font-weight: 600;
color: #333;
}
.input-wrapper {
position: relative;
}
#inputText {
width: 100%;
padding: 20px;
border: 2px solid #e1e5e9;
border-radius: 12px;
font-size: 16px;
font-family: inherit;
resize: vertical;
transition: all 0.3s ease;
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px);
}
#inputText:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
background: rgba(255, 255, 255, 0.95);
}
.input-actions {
display: flex;
gap: 15px;
margin-top: 20px;
justify-content: flex-end;
}
/* Button Styles */
.btn {
padding: 12px 24px;
border: none;
border-radius: 10px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
text-decoration: none;
position: relative;
overflow: hidden;
}
.btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.btn:hover::before {
left: 100%;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: rgba(255, 255, 255, 0.8);
color: #666;
border: 1px solid #e1e5e9;
}
.btn-secondary:hover {
background: rgba(255, 255, 255, 0.95);
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
/* Results Section */
.results-section {
opacity: 0;
transform: translateY(30px);
transition: all 0.5s ease;
}
.results-section.show {
opacity: 1;
transform: translateY(0);
}
.results-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 30px;
}
.result-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
border-radius: 20px;
padding: 30px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.3);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.result-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4);
}
.result-card:hover {
transform: translateY(-5px);
box-shadow: 0 30px 60px rgba(0, 0, 0, 0.15);
}
.result-card .card-header h3 {
font-size: 20px;
font-weight: 600;
color: #333;
}
.result-items {
display: flex;
flex-direction: column;
gap: 20px;
}
.result-item {
position: relative;
}
.result-item label {
display: block;
font-size: 14px;
font-weight: 500;
color: #666;
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.result-value {
display: flex;
align-items: center;
background: rgba(248, 250, 252, 0.8);
border: 1px solid #e1e5e9;
border-radius: 8px;
padding: 12px 16px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 14px;
word-break: break-all;
position: relative;
transition: all 0.3s ease;
}
.result-value:hover {
background: rgba(248, 250, 252, 0.95);
border-color: #667eea;
}
.result-value .placeholder {
color: #999;
font-style: italic;
}
.copy-btn {
background: none;
border: none;
color: #667eea;
cursor: pointer;
padding: 8px;
border-radius: 6px;
transition: all 0.3s ease;
margin-left: auto;
flex-shrink: 0;
}
.copy-btn:hover {
background: rgba(102, 126, 234, 0.1);
color: #5a67d8;
}
/* Loading Overlay */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.loading-overlay.show {
opacity: 1;
visibility: visible;
}
.loading-spinner {
text-align: center;
color: white;
}
.spinner {
width: 50px;
height: 50px;
border: 4px solid rgba(255, 255, 255, 0.3);
border-top: 4px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Toast Notification */
.toast {
position: fixed;
bottom: 30px;
right: 30px;
background: linear-gradient(135deg, #4ecdc4, #44a08d);
color: white;
padding: 16px 24px;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
gap: 10px;
transform: translateX(400px);
transition: all 0.3s ease;
z-index: 1001;
}
.toast.show {
transform: translateX(0);
}
.toast i {
font-size: 18px;
}
/* Responsive Design */
@media (max-width: 768px) {
.container {
padding: 15px;
}
.header-content {
padding: 30px 20px;
}
.logo h1 {
font-size: 32px;
}
.logo i {
font-size: 36px;
}
.results-grid {
grid-template-columns: 1fr;
gap: 20px;
}
.input-actions {
flex-direction: column;
}
.btn {
justify-content: center;
}
.toast {
bottom: 20px;
right: 20px;
left: 20px;
transform: translateY(100px);
}
.toast.show {
transform: translateY(0);
}
}
@media (max-width: 480px) {
.input-card,
.result-card {
padding: 20px;
}
.card-header h2,
.card-header h3 {
font-size: 18px;
}
.result-value {
font-size: 12px;
padding: 10px 12px;
}
}
/* Animation Classes */
.fade-in {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.slide-in {
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from {
transform: translateX(-20px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}

View File

@@ -0,0 +1,221 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多功能哈希工具 - Hash Toolkit</title>
<link rel="stylesheet" href="css/style.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<div class="container">
<!-- Header Section -->
<header class="header">
<div class="header-content">
<div class="logo">
<i class="fas fa-fingerprint"></i>
<h1>Hash Toolkit</h1>
</div>
<p class="subtitle">多功能哈希、编码与压缩工具</p>
</div>
<div class="header-decoration">
<div class="floating-shapes">
<div class="shape shape-1"></div>
<div class="shape shape-2"></div>
<div class="shape shape-3"></div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="main-content">
<!-- Input Section -->
<section class="input-section">
<div class="input-card">
<div class="card-header">
<i class="fas fa-edit"></i>
<h2>输入内容</h2>
</div>
<div class="input-wrapper">
<textarea
id="inputText"
placeholder="请输入要处理的文本内容...\n支持中文、英文、特殊字符等"
rows="6"
></textarea>
<div class="input-actions">
<button id="clearBtn" class="btn btn-secondary">
<i class="fas fa-trash"></i>
清空
</button>
<button id="processBtn" class="btn btn-primary">
<i class="fas fa-cogs"></i>
开始处理
</button>
</div>
</div>
</div>
</section>
<!-- Results Section -->
<section class="results-section" id="resultsSection">
<div class="results-grid">
<!-- Hash Results -->
<div class="result-card hash-card">
<div class="card-header">
<i class="fas fa-hashtag"></i>
<h3>哈希算法</h3>
</div>
<div class="result-items">
<div class="result-item">
<label>MD5</label>
<div class="result-value" id="md5Result">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="md5Result">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="result-item">
<label>SHA1</label>
<div class="result-value" id="sha1Result">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="sha1Result">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="result-item">
<label>SHA256</label>
<div class="result-value" id="sha256Result">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="sha256Result">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="result-item">
<label>SHA512</label>
<div class="result-value" id="sha512Result">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="sha512Result">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Encoding Results -->
<div class="result-card encoding-card">
<div class="card-header">
<i class="fas fa-code"></i>
<h3>编码转换</h3>
</div>
<div class="result-items">
<div class="result-item">
<label>Base64 编码</label>
<div class="result-value" id="base64EncodeResult">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="base64EncodeResult">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="result-item">
<label>Base64 解码</label>
<div class="result-value" id="base64DecodeResult">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="base64DecodeResult">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="result-item">
<label>URL 编码</label>
<div class="result-value" id="urlEncodeResult">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="urlEncodeResult">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="result-item">
<label>URL 解码</label>
<div class="result-value" id="urlDecodeResult">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="urlDecodeResult">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Compression Results -->
<div class="result-card compression-card">
<div class="card-header">
<i class="fas fa-compress-alt"></i>
<h3>压缩算法</h3>
</div>
<div class="result-items">
<div class="result-item">
<label>Gzip 压缩</label>
<div class="result-value" id="gzipCompressResult">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="gzipCompressResult">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="result-item">
<label>Gzip 解压</label>
<div class="result-value" id="gzipDecompressResult">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="gzipDecompressResult">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="result-item">
<label>Deflate 压缩</label>
<div class="result-value" id="deflateCompressResult">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="deflateCompressResult">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="result-item">
<label>Brotli 压缩</label>
<div class="result-value" id="brotliCompressResult">
<span class="placeholder">等待处理...</span>
<button class="copy-btn" data-target="brotliCompressResult">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</section>
</main>
<!-- Loading Overlay -->
<div class="loading-overlay" id="loadingOverlay">
<div class="loading-spinner">
<div class="spinner"></div>
<p>正在处理中...</p>
</div>
</div>
<!-- Toast Notification -->
<div class="toast" id="toast">
<i class="fas fa-check-circle"></i>
<span id="toastMessage">复制成功!</span>
</div>
</div>
<script src="js/script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,380 @@
// API配置
const API_BASE_URL = 'https://60s.viki.moe/v2/hash';
// DOM元素
const elements = {
inputText: document.getElementById('inputText'),
processBtn: document.getElementById('processBtn'),
clearBtn: document.getElementById('clearBtn'),
resultsSection: document.getElementById('resultsSection'),
loadingOverlay: document.getElementById('loadingOverlay'),
toast: document.getElementById('toast'),
toastMessage: document.getElementById('toastMessage')
};
// 结果元素映射
const resultElements = {
md5: document.getElementById('md5Result'),
sha1: document.getElementById('sha1Result'),
sha256: document.getElementById('sha256Result'),
sha512: document.getElementById('sha512Result'),
base64Encode: document.getElementById('base64EncodeResult'),
base64Decode: document.getElementById('base64DecodeResult'),
urlEncode: document.getElementById('urlEncodeResult'),
urlDecode: document.getElementById('urlDecodeResult'),
gzipCompress: document.getElementById('gzipCompressResult'),
gzipDecompress: document.getElementById('gzipDecompressResult'),
deflateCompress: document.getElementById('deflateCompressResult'),
brotliCompress: document.getElementById('brotliCompressResult')
};
// 初始化
document.addEventListener('DOMContentLoaded', function() {
initializeEventListeners();
addInputAnimation();
});
// 事件监听器初始化
function initializeEventListeners() {
// 处理按钮点击
elements.processBtn.addEventListener('click', handleProcess);
// 清空按钮点击
elements.clearBtn.addEventListener('click', handleClear);
// 输入框回车键
elements.inputText.addEventListener('keydown', function(e) {
if (e.ctrlKey && e.key === 'Enter') {
handleProcess();
}
});
// 复制按钮事件委托
document.addEventListener('click', function(e) {
if (e.target.closest('.copy-btn')) {
const copyBtn = e.target.closest('.copy-btn');
const targetId = copyBtn.getAttribute('data-target');
const targetElement = document.getElementById(targetId);
const textContent = targetElement.textContent.trim();
if (textContent && textContent !== '等待处理...' && textContent !== '处理失败') {
copyToClipboard(textContent);
}
}
});
// 输入框实时验证
elements.inputText.addEventListener('input', function() {
const hasContent = this.value.trim().length > 0;
elements.processBtn.disabled = !hasContent;
if (hasContent) {
elements.processBtn.classList.remove('disabled');
} else {
elements.processBtn.classList.add('disabled');
}
});
}
// 添加输入动画效果
function addInputAnimation() {
elements.inputText.addEventListener('focus', function() {
this.parentElement.classList.add('focused');
});
elements.inputText.addEventListener('blur', function() {
this.parentElement.classList.remove('focused');
});
}
// 处理主要功能
async function handleProcess() {
const inputValue = elements.inputText.value.trim();
if (!inputValue) {
showToast('请输入要处理的内容', 'error');
return;
}
// 显示加载状态
showLoading(true);
resetResults();
try {
// 调用API
const response = await fetch(`${API_BASE_URL}?content=${encodeURIComponent(inputValue)}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.code === 200 && data.data) {
displayResults(data.data);
showResultsSection();
showToast('处理完成!', 'success');
} else {
throw new Error(data.message || '处理失败');
}
} catch (error) {
console.error('处理错误:', error);
showToast(`处理失败: ${error.message}`, 'error');
displayError();
} finally {
showLoading(false);
}
}
// 显示结果
function displayResults(data) {
try {
// 哈希结果
updateResultElement('md5', data.md5 || '不可用');
// SHA系列
if (data.sha) {
updateResultElement('sha1', data.sha.sha1 || '不可用');
updateResultElement('sha256', data.sha.sha256 || '不可用');
updateResultElement('sha512', data.sha.sha512 || '不可用');
}
// Base64编码
if (data.base64) {
updateResultElement('base64Encode', data.base64.encode || '不可用');
updateResultElement('base64Decode', data.base64.decode || '不可用');
}
// URL编码
if (data.url) {
updateResultElement('urlEncode', data.url.encode || '不可用');
updateResultElement('urlDecode', data.url.decode || '不可用');
}
// 压缩结果
if (data.gzip) {
updateResultElement('gzipCompress', data.gzip.compress || '不可用');
updateResultElement('gzipDecompress', data.gzip.decompress || '不可用');
}
if (data.deflate) {
updateResultElement('deflateCompress', data.deflate.compress || '不可用');
}
if (data.brotli) {
updateResultElement('brotliCompress', data.brotli.compress || '不可用');
}
} catch (error) {
console.error('显示结果时出错:', error);
showToast('显示结果时出错', 'error');
}
}
// 更新单个结果元素
function updateResultElement(key, value) {
const element = resultElements[key];
if (element) {
const textSpan = element.querySelector('span') || element;
textSpan.textContent = value;
textSpan.classList.remove('placeholder');
// 添加动画效果
element.classList.add('slide-in');
setTimeout(() => {
element.classList.remove('slide-in');
}, 300);
}
}
// 重置结果
function resetResults() {
Object.values(resultElements).forEach(element => {
if (element) {
const textSpan = element.querySelector('span') || element;
textSpan.textContent = '等待处理...';
textSpan.classList.add('placeholder');
}
});
}
// 显示错误状态
function displayError() {
Object.values(resultElements).forEach(element => {
if (element) {
const textSpan = element.querySelector('span') || element;
textSpan.textContent = '处理失败';
textSpan.classList.add('placeholder');
}
});
}
// 显示结果区域
function showResultsSection() {
elements.resultsSection.classList.add('show');
// 平滑滚动到结果区域
setTimeout(() => {
elements.resultsSection.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}, 100);
}
// 清空功能
function handleClear() {
elements.inputText.value = '';
elements.inputText.focus();
elements.resultsSection.classList.remove('show');
resetResults();
elements.processBtn.disabled = true;
elements.processBtn.classList.add('disabled');
showToast('内容已清空', 'info');
}
// 复制到剪贴板
async function copyToClipboard(text) {
try {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(text);
} else {
// 降级方案
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
document.execCommand('copy');
textArea.remove();
}
showToast('复制成功!', 'success');
} catch (error) {
console.error('复制失败:', error);
showToast('复制失败,请手动复制', 'error');
}
}
// 显示/隐藏加载状态
function showLoading(show) {
if (show) {
elements.loadingOverlay.classList.add('show');
elements.processBtn.disabled = true;
elements.processBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 处理中...';
} else {
elements.loadingOverlay.classList.remove('show');
elements.processBtn.disabled = false;
elements.processBtn.innerHTML = '<i class="fas fa-cogs"></i> 开始处理';
}
}
// 显示提示消息
function showToast(message, type = 'success') {
elements.toastMessage.textContent = message;
// 设置图标和样式
const icon = elements.toast.querySelector('i');
icon.className = getToastIcon(type);
elements.toast.className = `toast ${type}`;
elements.toast.classList.add('show');
// 自动隐藏
setTimeout(() => {
elements.toast.classList.remove('show');
}, 3000);
}
// 获取提示图标
function getToastIcon(type) {
const icons = {
success: 'fas fa-check-circle',
error: 'fas fa-exclamation-circle',
info: 'fas fa-info-circle',
warning: 'fas fa-exclamation-triangle'
};
return icons[type] || icons.success;
}
// 工具函数:防抖
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 工具函数:节流
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
}
}
// 添加键盘快捷键支持
document.addEventListener('keydown', function(e) {
// Ctrl+Enter 处理
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
if (!elements.processBtn.disabled) {
handleProcess();
}
}
// Escape 清空
if (e.key === 'Escape') {
handleClear();
}
});
// 页面可见性变化处理
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
// 页面隐藏时的处理
console.log('页面已隐藏');
} else {
// 页面显示时的处理
console.log('页面已显示');
}
});
// 错误处理
window.addEventListener('error', function(e) {
console.error('全局错误:', e.error);
showToast('发生未知错误,请刷新页面重试', 'error');
});
// 未处理的Promise拒绝
window.addEventListener('unhandledrejection', function(e) {
console.error('未处理的Promise拒绝:', e.reason);
showToast('网络请求失败,请检查网络连接', 'error');
});
// 导出函数供测试使用
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
handleProcess,
copyToClipboard,
showToast,
debounce,
throttle
};
}

View File

@@ -0,0 +1,3 @@
[
"https://60s.api.shumengya.top"
]

View File

@@ -0,0 +1,33 @@
{
"code": 200,
"message": "处理成功",
"data": {
"source": "你好👋",
"md5": "a1b2c3d4e5f6789012345678901234567",
"sha": {
"sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"sha512": "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
},
"base64": {
"encode": "5L2g5aW9",
"decode": "你好"
},
"url": {
"encode": "%E4%BD%A0%E5%A5%BD%F0%9F%91%8B",
"decode": "你好👋"
},
"gzip": {
"compress": "H4sIAAAAAAAAA...(压缩后的数据)",
"decompress": "你好👋"
},
"deflate": {
"compress": "eJwrz8kvTUlMy...(压缩后的数据)",
"decompress": "你好👋"
},
"brotli": {
"compress": "CwWAaGVsbG8g...(压缩后的数据)",
"decompress": "你好👋"
}
}
}

View File

@@ -0,0 +1,8 @@
1.生成为静态网页jscsshtml分离出来不要混合在一起放入html里难以阅读
2.网页要适配手机端电脑端和平板端三个设备分别做不同的css格式优先优化手机端用户体验
3.网页默认风格以淡绿色清新风格为主,除非用户要求
4.尽量不要引用外部cssjs实在要引用就使用中国国内的cdn否则用户可能加载不出来
5.返回接口.json储存了网页api返回的数据格式
6.严格按照用户要求执行,不得随意添加什么注解,如“以下数据来自...”
7.接口集合.json保存了所有已知的后端API接口一个访问不了尝试自动切换另一个
8.在css中有关背景的css单独一个css文件方便我直接迁移

View File

@@ -0,0 +1,232 @@
/* 高维度背景特效样式 - 神秘高级风格 */
/* 背景容器 */
.background-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
overflow: hidden;
pointer-events: none;
background: radial-gradient(ellipse at center,
rgba(15, 0, 30, 0.95) 0%,
rgba(5, 0, 15, 0.98) 50%,
rgba(0, 0, 0, 1) 100%);
}
/* 几何网格层 */
.geometric-grid {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(rgba(138, 43, 226, 0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(138, 43, 226, 0.1) 1px, transparent 1px),
linear-gradient(rgba(75, 0, 130, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(75, 0, 130, 0.05) 1px, transparent 1px);
background-size: 100px 100px, 100px 100px, 20px 20px, 20px 20px;
animation: gridPulse 8s ease-in-out infinite;
}
@keyframes gridPulse {
0%, 100% { opacity: 0.3; transform: scale(1); }
50% { opacity: 0.6; transform: scale(1.02); }
}
/* 神经网络效果 */
.neural-network {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 30%, rgba(138, 43, 226, 0.15) 2px, transparent 2px),
radial-gradient(circle at 80% 20%, rgba(75, 0, 130, 0.12) 1px, transparent 1px),
radial-gradient(circle at 40% 70%, rgba(147, 0, 211, 0.1) 1.5px, transparent 1.5px),
radial-gradient(circle at 90% 80%, rgba(138, 43, 226, 0.08) 1px, transparent 1px),
radial-gradient(circle at 10% 90%, rgba(75, 0, 130, 0.1) 2px, transparent 2px);
background-size: 200px 200px, 150px 150px, 300px 300px, 180px 180px, 250px 250px;
animation: neuralFlow 15s linear infinite;
}
@keyframes neuralFlow {
0% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(-10px, -5px) rotate(90deg); }
50% { transform: translate(-5px, -10px) rotate(180deg); }
75% { transform: translate(5px, -5px) rotate(270deg); }
100% { transform: translate(0, 0) rotate(360deg); }
}
/* 粒子系统 */
.particle-system {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
radial-gradient(circle, rgba(138, 43, 226, 0.4) 1px, transparent 1px),
radial-gradient(circle, rgba(75, 0, 130, 0.3) 0.5px, transparent 0.5px),
radial-gradient(circle, rgba(147, 0, 211, 0.2) 0.8px, transparent 0.8px);
background-size: 80px 80px, 120px 120px, 160px 160px;
background-position: 0 0, 40px 40px, 80px 80px;
animation: particleFloat 20s ease-in-out infinite;
}
@keyframes particleFloat {
0%, 100% { transform: translateY(0px) translateX(0px); }
25% { transform: translateY(-20px) translateX(10px); }
50% { transform: translateY(-10px) translateX(-15px); }
75% { transform: translateY(-30px) translateX(5px); }
}
/* 扫描线效果 */
.scan-lines {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: repeating-linear-gradient(
0deg,
transparent 0px,
transparent 2px,
rgba(138, 43, 226, 0.03) 2px,
rgba(138, 43, 226, 0.03) 4px
);
animation: scanMove 3s linear infinite;
}
@keyframes scanMove {
0% { transform: translateY(-100%); }
100% { transform: translateY(100%); }
}
/* 全息投影效果 */
.holographic-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
linear-gradient(45deg,
transparent 30%,
rgba(138, 43, 226, 0.05) 50%,
transparent 70%),
linear-gradient(-45deg,
transparent 30%,
rgba(75, 0, 130, 0.03) 50%,
transparent 70%);
background-size: 200px 200px, 150px 150px;
animation: holographicShift 12s ease-in-out infinite;
}
@keyframes holographicShift {
0%, 100% {
background-position: 0% 0%, 100% 100%;
opacity: 0.7;
}
50% {
background-position: 100% 100%, 0% 0%;
opacity: 1;
}
}
/* 数据流效果 */
.data-stream {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
linear-gradient(90deg,
transparent 0%,
rgba(138, 43, 226, 0.1) 50%,
transparent 100%);
background-size: 300px 100%;
animation: dataFlow 8s linear infinite;
}
@keyframes dataFlow {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
/* 量子波动效果 */
.quantum-waves {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(ellipse 200px 100px at 50% 0%,
rgba(138, 43, 226, 0.1) 0%,
transparent 50%),
radial-gradient(ellipse 300px 150px at 50% 100%,
rgba(75, 0, 130, 0.08) 0%,
transparent 50%);
animation: quantumPulse 10s ease-in-out infinite;
}
@keyframes quantumPulse {
0%, 100% {
transform: scale(1) rotate(0deg);
opacity: 0.5;
}
50% {
transform: scale(1.1) rotate(180deg);
opacity: 0.8;
}
}
/* 响应式优化 */
@media (max-width: 768px) {
.geometric-grid {
background-size: 50px 50px, 50px 50px, 10px 10px, 10px 10px;
}
.neural-network {
background-size: 100px 100px, 75px 75px, 150px 150px, 90px 90px, 125px 125px;
}
.particle-system {
background-size: 40px 40px, 60px 60px, 80px 80px;
}
}
/* 减少动画偏好 */
@media (prefers-reduced-motion: reduce) {
.geometric-grid,
.neural-network,
.particle-system,
.scan-lines,
.holographic-overlay,
.data-stream,
.quantum-waves {
animation: none;
}
}
/* 高对比度模式 */
@media (prefers-contrast: high) {
.background-container {
background: radial-gradient(ellipse at center,
rgba(25, 0, 50, 0.95) 0%,
rgba(10, 0, 25, 0.98) 50%,
rgba(0, 0, 0, 1) 100%);
}
.geometric-grid {
background-image:
linear-gradient(rgba(200, 100, 255, 0.2) 1px, transparent 1px),
linear-gradient(90deg, rgba(200, 100, 255, 0.2) 1px, transparent 1px);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,223 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>链接OG信息查询 - 神秘解析器</title>
<meta name="description" content="高级链接OG信息查询工具解析网页元数据">
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/background.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
</head>
<body>
<!-- 背景特效容器 -->
<div class="background-container">
<div class="matrix-rain"></div>
<div class="geometric-shapes"></div>
<div class="neural-network"></div>
</div>
<!-- 主容器 -->
<div class="main-container">
<!-- 头部区域 -->
<header class="header">
<div class="header-content">
<div class="logo-section">
<i class="fas fa-link logo-icon"></i>
<h1 class="title">OG 解析器</h1>
<span class="subtitle">链接元数据神秘解析</span>
</div>
<div class="status-indicator">
<div class="pulse-dot"></div>
<span class="status-text">系统就绪</span>
</div>
</div>
</header>
<!-- 查询区域 -->
<section class="query-section">
<div class="input-container">
<div class="input-wrapper">
<i class="fas fa-globe input-icon"></i>
<input type="url" id="url-input" placeholder="输入链接地址进行深度解析..." class="url-input">
<div class="input-border"></div>
</div>
<button id="analyze-btn" class="analyze-btn">
<span class="btn-text">开始解析</span>
<div class="btn-effects">
<div class="ripple"></div>
<div class="glow"></div>
</div>
</button>
</div>
</section>
<!-- 加载状态 -->
<div id="loading" class="loading-container" style="display: none;">
<div class="loading-content">
<div class="scanner">
<div class="scanner-line"></div>
<div class="scanner-grid">
<div class="grid-line"></div>
<div class="grid-line"></div>
<div class="grid-line"></div>
<div class="grid-line"></div>
</div>
</div>
<div class="loading-text">
<span class="loading-title">正在解析链接</span>
<span class="loading-subtitle">深度扫描元数据中...</span>
</div>
</div>
</div>
<!-- 结果展示区域 -->
<section id="results" class="results-section" style="display: none;">
<div class="results-header">
<h2 class="results-title">
<i class="fas fa-chart-network"></i>
解析结果
</h2>
<div class="results-actions">
<button id="copy-btn" class="action-btn">
<i class="fas fa-copy"></i>
<span>复制数据</span>
</button>
<button id="clear-btn" class="action-btn">
<i class="fas fa-trash"></i>
<span>清除结果</span>
</button>
</div>
</div>
<div class="og-card">
<!-- 基础信息 -->
<div class="info-section basic-info">
<div class="section-header">
<i class="fas fa-info-circle"></i>
<span>基础信息</span>
</div>
<div class="info-grid">
<div class="info-item">
<label>标题</label>
<div id="og-title" class="info-value">-</div>
</div>
<div class="info-item">
<label>描述</label>
<div id="og-description" class="info-value">-</div>
</div>
<div class="info-item">
<label>网站名称</label>
<div id="og-site-name" class="info-value">-</div>
</div>
<div class="info-item">
<label>类型</label>
<div id="og-type" class="info-value">-</div>
</div>
</div>
</div>
<!-- 媒体信息 -->
<div class="info-section media-info">
<div class="section-header">
<i class="fas fa-image"></i>
<span>媒体信息</span>
</div>
<div class="media-preview" id="media-preview">
<div class="no-media">
<i class="fas fa-image-slash"></i>
<span>暂无媒体内容</span>
</div>
</div>
<div class="media-details">
<div class="info-item">
<label>图片URL</label>
<div id="og-image" class="info-value url-value">-</div>
</div>
<div class="info-item">
<label>图片尺寸</label>
<div id="og-image-size" class="info-value">-</div>
</div>
</div>
</div>
<!-- 技术信息 -->
<div class="info-section tech-info">
<div class="section-header">
<i class="fas fa-code"></i>
<span>技术信息</span>
</div>
<div class="info-grid">
<div class="info-item">
<label>URL</label>
<div id="og-url" class="info-value url-value">-</div>
</div>
<div class="info-item">
<label>域名</label>
<div id="og-domain" class="info-value">-</div>
</div>
<div class="info-item">
<label>语言</label>
<div id="og-locale" class="info-value">-</div>
</div>
<div class="info-item">
<label>字符编码</label>
<div id="og-charset" class="info-value">-</div>
</div>
</div>
</div>
</div>
</section>
<!-- 错误信息 -->
<div id="error" class="error-container" style="display: none;">
<div class="error-content">
<div class="error-icon">
<i class="fas fa-exclamation-triangle"></i>
</div>
<div class="error-text">
<h3 class="error-title">解析失败</h3>
<p id="error-message" class="error-message">未知错误</p>
</div>
<button id="retryBtn" class="retry-btn">
<i class="fas fa-redo"></i>
<span>重新尝试</span>
</button>
</div>
</div>
</div>
<!-- 提示消息 -->
<div id="tip-message" class="tip-container">
<div class="tip-content">
<i class="fas fa-lightbulb tip-icon"></i>
<span class="tip-text"></span>
</div>
</div>
<!-- Toast消息 -->
<div id="toast" class="toast-container">
<div class="toast-content">
<i class="toast-icon"></i>
<span class="toast-message"></span>
</div>
</div>
<!-- 页脚 -->
<footer class="footer">
<div class="footer-content">
<p class="footer-text">
<i class="fas fa-shield-alt"></i>
高级链接解析系统 | 神秘数据挖掘
</p>
<div class="footer-links">
<span class="footer-link">隐私保护</span>
<span class="footer-divider">|</span>
<span class="footer-link">安全解析</span>
</div>
</div>
</footer>
<script src="js/script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,555 @@
// 链接OG信息查询 - JavaScript功能代码
// 神秘高级风格的交互体验
class OGAnalyzer {
constructor() {
this.apiUrl = 'https://60s.viki.moe/v2/og';
this.isAnalyzing = false;
this.currentUrl = '';
this.animationFrameId = null;
this.init();
}
init() {
this.bindEvents();
this.createBackgroundEffects();
this.initializeAnimations();
this.showWelcomeMessage();
this.initPageAnimations();
}
// 初始化页面动画
initPageAnimations() {
// 延迟添加动画类确保CSS已加载
setTimeout(() => {
const header = document.querySelector('.header');
const querySection = document.querySelector('.query-section');
if (header) header.classList.add('animate-in');
if (querySection) querySection.classList.add('animate-in');
}, 100);
}
bindEvents() {
const urlInput = document.getElementById('url-input');
const analyzeBtn = document.getElementById('analyze-btn');
const copyBtn = document.getElementById('copy-btn');
const clearBtn = document.getElementById('clear-btn');
// 输入框事件
urlInput.addEventListener('input', (e) => this.handleUrlInput(e));
urlInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter' && !this.isAnalyzing) {
this.analyzeUrl();
}
});
urlInput.addEventListener('focus', () => this.handleInputFocus());
urlInput.addEventListener('blur', () => this.handleInputBlur());
// 按钮事件
analyzeBtn.addEventListener('click', () => this.analyzeUrl());
copyBtn.addEventListener('click', () => this.copyResults());
clearBtn.addEventListener('click', () => this.clearResults());
// 键盘快捷键
document.addEventListener('keydown', (e) => this.handleKeyboard(e));
}
handleUrlInput(e) {
const url = e.target.value.trim();
const analyzeBtn = document.getElementById('analyze-btn');
if (this.isValidUrl(url)) {
analyzeBtn.classList.add('ready');
e.target.classList.remove('error');
} else {
analyzeBtn.classList.remove('ready');
if (url.length > 0) {
e.target.classList.add('error');
} else {
e.target.classList.remove('error');
}
}
}
handleInputFocus() {
const inputContainer = document.querySelector('.input-container');
inputContainer.classList.add('focused');
this.createInputGlow();
}
handleInputBlur() {
const inputContainer = document.querySelector('.input-container');
inputContainer.classList.remove('focused');
}
handleKeyboard(e) {
// Ctrl/Cmd + Enter 快速分析
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
e.preventDefault();
if (!this.isAnalyzing) {
this.analyzeUrl();
}
}
// Escape 清除结果
if (e.key === 'Escape') {
this.clearResults();
}
}
isValidUrl(string) {
try {
const url = new URL(string);
return url.protocol === 'http:' || url.protocol === 'https:';
} catch (_) {
return false;
}
}
async analyzeUrl() {
const urlInput = document.getElementById('url-input');
const url = urlInput.value.trim();
if (!this.isValidUrl(url)) {
this.showError('请输入有效的URL地址');
this.shakeInput();
return;
}
if (this.isAnalyzing) {
return;
}
this.currentUrl = url;
this.isAnalyzing = true;
this.showLoading();
this.hideError();
this.hideResults();
try {
const response = await fetch(`${this.apiUrl}?url=${encodeURIComponent(url)}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (data.code === 200 && data.data) {
await this.displayResults(data.data);
this.showSuccessMessage('分析完成!');
// 添加按钮闪烁效果
const analyzeBtn = document.getElementById('analyze-btn');
analyzeBtn.classList.add('flash');
setTimeout(() => {
analyzeBtn.classList.remove('flash');
}, 300);
} else {
throw new Error(data.message || '获取OG信息失败');
}
} catch (error) {
console.error('分析失败:', error);
this.showError(`分析失败: ${error.message}`);
} finally {
this.isAnalyzing = false;
this.hideLoading();
}
}
showLoading() {
const loadingElement = document.getElementById('loading');
const analyzeBtn = document.getElementById('analyze-btn');
loadingElement.classList.add('active');
analyzeBtn.disabled = true;
analyzeBtn.textContent = '分析中...';
this.startScannerAnimation();
}
hideLoading() {
const loadingElement = document.getElementById('loading');
const analyzeBtn = document.getElementById('analyze-btn');
loadingElement.classList.remove('active');
analyzeBtn.disabled = false;
analyzeBtn.textContent = '开始分析';
this.stopScannerAnimation();
}
async displayResults(data) {
const resultsElement = document.getElementById('results');
const ogCard = document.getElementById('og-card');
// 基础信息
this.updateElement('og-title', data.title || '未获取到标题');
this.updateElement('og-description', data.description || '未获取到描述');
this.updateElement('og-url', data.url || this.currentUrl);
this.updateElement('og-site-name', data.site_name || '未知站点');
this.updateElement('og-type', data.type || 'website');
// 媒体信息
this.updateImageElement('og-image', data.image);
this.updateElement('og-image-alt', data.image_alt || '图片描述不可用');
// 技术信息
this.updateElement('og-locale', data.locale || '未指定');
this.updateElement('og-updated-time', this.formatDate(data.updated_time));
this.updateElement('response-time', `${Date.now() - this.startTime}ms`);
// 显示结果
resultsElement.classList.add('active');
// 添加动画效果
await this.animateResults();
// 启用操作按钮
document.getElementById('copy-btn').disabled = false;
document.getElementById('clear-btn').disabled = false;
}
updateElement(id, content) {
const element = document.getElementById(id);
if (element) {
element.textContent = content;
}
}
updateImageElement(id, imageSrc) {
const element = document.getElementById(id);
if (element && imageSrc) {
element.src = imageSrc;
element.style.display = 'block';
element.onerror = () => {
element.style.display = 'none';
const placeholder = element.nextElementSibling;
if (placeholder && placeholder.classList.contains('image-placeholder')) {
placeholder.style.display = 'flex';
}
};
} else if (element) {
element.style.display = 'none';
const placeholder = element.nextElementSibling;
if (placeholder && placeholder.classList.contains('image-placeholder')) {
placeholder.style.display = 'flex';
}
}
}
formatDate(timestamp) {
if (!timestamp) return '未知';
try {
const date = new Date(timestamp);
return date.toLocaleString('zh-CN');
} catch (e) {
return '格式错误';
}
}
async animateResults() {
const cards = document.querySelectorAll('.info-card');
for (let i = 0; i < cards.length; i++) {
setTimeout(() => {
cards[i].classList.add('animate-in');
}, i * 100);
}
// 等待动画完成
await new Promise(resolve => setTimeout(resolve, cards.length * 100 + 300));
}
showError(message) {
const errorElement = document.getElementById('error-message');
const errorText = errorElement.querySelector('.error-text');
const inputContainer = document.querySelector('.input-container');
errorText.textContent = message;
errorElement.classList.add('active');
// 添加震动效果
if (inputContainer) {
inputContainer.classList.add('shake');
setTimeout(() => {
inputContainer.classList.remove('shake');
}, 600);
}
// 自动隐藏错误信息
setTimeout(() => {
this.hideError();
}, 5000);
}
hideError() {
const errorElement = document.getElementById('error-message');
errorElement.classList.remove('active');
}
hideResults() {
const resultsElement = document.getElementById('results');
resultsElement.classList.remove('active');
// 重置动画状态
const cards = document.querySelectorAll('.info-card');
cards.forEach(card => card.classList.remove('animate-in'));
}
showSuccessMessage(message) {
const tipElement = document.getElementById('tip-message');
const tipText = tipElement.querySelector('.tip-text');
tipText.textContent = message;
tipElement.classList.add('active');
setTimeout(() => {
tipElement.classList.remove('active');
}, 3000);
}
shakeInput() {
const inputContainer = document.querySelector('.input-container');
inputContainer.classList.add('shake');
setTimeout(() => {
inputContainer.classList.remove('shake');
}, 600);
}
copyResults() {
const ogData = {
title: document.getElementById('og-title').textContent,
description: document.getElementById('og-description').textContent,
url: document.getElementById('og-url').textContent,
site_name: document.getElementById('og-site-name').textContent,
type: document.getElementById('og-type').textContent,
image: document.getElementById('og-image').src,
locale: document.getElementById('og-locale').textContent
};
const jsonString = JSON.stringify(ogData, null, 2);
navigator.clipboard.writeText(jsonString).then(() => {
this.showSuccessMessage('结果已复制到剪贴板!');
this.flashCopyButton();
}).catch(err => {
console.error('复制失败:', err);
this.showError('复制失败,请手动选择内容');
});
}
flashCopyButton() {
const copyBtn = document.getElementById('copy-btn');
copyBtn.classList.add('flash');
setTimeout(() => {
copyBtn.classList.remove('flash');
}, 300);
}
clearResults() {
const urlInput = document.getElementById('url-input');
const resultsElement = document.getElementById('results');
const errorElement = document.getElementById('error-message');
urlInput.value = '';
urlInput.classList.remove('error');
resultsElement.classList.remove('active');
errorElement.classList.remove('active');
document.getElementById('analyze-btn').classList.remove('ready');
document.getElementById('copy-btn').disabled = true;
document.getElementById('clear-btn').disabled = true;
this.currentUrl = '';
// 重置动画状态
const cards = document.querySelectorAll('.info-card');
cards.forEach(card => card.classList.remove('animate-in'));
this.showSuccessMessage('已清除所有内容');
}
createBackgroundEffects() {
const container = document.querySelector('.background-container');
// 创建各种背景效果层
const effects = [
'geometric-grid',
'neural-network',
'particle-system',
'scan-lines',
'holographic-overlay',
'data-stream',
'quantum-waves'
];
effects.forEach(effectClass => {
const layer = document.createElement('div');
layer.className = effectClass;
container.appendChild(layer);
});
}
createInputGlow() {
const inputContainer = document.querySelector('.input-container');
// 创建光晕效果
const glow = document.createElement('div');
glow.className = 'input-glow';
inputContainer.appendChild(glow);
setTimeout(() => {
if (glow.parentNode) {
glow.remove();
}
}, 2000);
}
startScannerAnimation() {
const scanner = document.querySelector('.scanner');
if (scanner) {
scanner.classList.add('active');
}
}
stopScannerAnimation() {
const scanner = document.querySelector('.scanner');
if (scanner) {
scanner.classList.remove('active');
}
}
initializeAnimations() {
// 初始化页面动画
const header = document.querySelector('.header');
const querySection = document.querySelector('.query-section');
setTimeout(() => {
header.classList.add('animate-in');
}, 100);
setTimeout(() => {
querySection.classList.add('animate-in');
}, 300);
}
showWelcomeMessage() {
const tips = [
'支持分析网页的标题、描述、图片等元信息',
'可以预览社交媒体分享时的显示效果',
'检测网页的SEO优化情况',
'分析Open Graph协议标签'
];
setTimeout(() => {
this.showSuccessMessage('欢迎使用链接OG信息分析器');
}, 1000);
// 显示提示信息
this.showTips(tips);
}
// 显示提示信息
showTips(tips) {
const tipElement = document.getElementById('tip-message');
const tipText = tipElement.querySelector('.tip-text');
let currentTip = 0;
const showNextTip = () => {
tipText.textContent = tips[currentTip];
tipElement.classList.add('active');
tipElement.style.animation = 'fadeInUp 0.5s ease-out';
setTimeout(() => {
tipElement.style.animation = 'fadeOutDown 0.5s ease-in';
setTimeout(() => {
tipElement.classList.remove('active');
currentTip = (currentTip + 1) % tips.length;
}, 500);
}, 3000);
};
// 首次显示
setTimeout(showNextTip, 2000);
// 每8秒显示一次
setInterval(showNextTip, 8000);
}
}
// 工具函数
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
}
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
// 检查必要的DOM元素
const requiredElements = [
'url-input', 'analyze-btn', 'copy-btn', 'clear-btn',
'loading', 'results', 'error-message', 'tip-message'
];
const missingElements = requiredElements.filter(id => !document.getElementById(id));
if (missingElements.length > 0) {
console.error('缺少必要的DOM元素:', missingElements);
return;
}
// 初始化应用
window.ogAnalyzer = new OGAnalyzer();
// 添加全局错误处理
window.addEventListener('error', (e) => {
console.error('全局错误:', e.error);
if (window.ogAnalyzer) {
window.ogAnalyzer.showError('发生未知错误,请刷新页面重试');
}
});
// 添加网络状态监听
window.addEventListener('online', () => {
if (window.ogAnalyzer) {
window.ogAnalyzer.showSuccessMessage('网络连接已恢复');
}
});
window.addEventListener('offline', () => {
if (window.ogAnalyzer) {
window.ogAnalyzer.showError('网络连接已断开');
}
});
});
// 导出给其他模块使用
if (typeof module !== 'undefined' && module.exports) {
module.exports = { OGAnalyzer, debounce, throttle };
}

View File

@@ -0,0 +1,3 @@
[
"https://60s.api.shumengya.top"
]

View File

@@ -0,0 +1,66 @@
{
"code": 200,
"message": "success",
"data": {
"url": "https://example.com",
"title": "示例网站标题",
"description": "这是一个示例网站的描述信息用于展示OG标签解析功能。",
"image": "https://example.com/og-image.jpg",
"site_name": "示例网站",
"type": "website",
"locale": "zh_CN",
"author": "网站作者",
"keywords": "示例,网站,OG标签,元数据",
"favicon": "https://example.com/favicon.ico",
"canonical_url": "https://example.com",
"robots": "index,follow",
"viewport": "width=device-width, initial-scale=1.0",
"charset": "UTF-8",
"language": "zh-CN",
"published_time": "2024-01-01T00:00:00Z",
"modified_time": "2024-01-15T12:30:00Z",
"section": "技术",
"tags": ["前端", "元数据", "SEO"],
"twitter": {
"card": "summary_large_image",
"site": "@example",
"creator": "@author",
"title": "Twitter标题",
"description": "Twitter描述",
"image": "https://example.com/twitter-image.jpg"
},
"facebook": {
"app_id": "123456789",
"admins": "987654321"
},
"structured_data": {
"@context": "https://schema.org",
"@type": "WebPage",
"name": "示例网页",
"description": "示例网页描述",
"url": "https://example.com"
},
"meta_tags": {
"generator": "WordPress 6.0",
"theme-color": "#000000",
"msapplication-TileColor": "#ffffff",
"apple-mobile-web-app-capable": "yes",
"apple-mobile-web-app-status-bar-style": "default"
},
"performance": {
"load_time": 1.25,
"page_size": "2.3MB",
"requests_count": 45
},
"seo_score": {
"overall": 85,
"title_score": 90,
"description_score": 80,
"image_score": 85,
"structure_score": 88
}
},
"timestamp": "2024-01-15T12:30:45Z",
"request_id": "req_123456789",
"processing_time": 0.85
}