释放双眼,带上耳机,听听看~!
名言卡片 – 每日智慧
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#6366f1',
secondary: '#8b5cf6',
accent: '#ec4899',
dark: '#1e293b',
light: '#f8fafc'
},
fontFamily: {
serif: ['Georgia', 'serif'],
sans: ['Inter', 'sans-serif']
}
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.text-shadow {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
.card-shadow {
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}
.gradient-overlay {
background: linear-gradient(135deg, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.3) 100%);
}
}
</style>
<!-- 主要内容容器 -->
<div class="relative min-h-screen flex items-center justify-center p-4">
<div class="w-full max-w-4xl">
<!-- 名言卡片 -->
<div id="quote-card" class="relative bg-white/10 backdrop-blur-lg rounded-3xl p-8 md:p-12 card-shadow border border-white/20 transform transition-all duration-500 hover:scale-105">
<!-- 装饰元素 -->
<div class="absolute -top-6 -right-6 w-24 h-24 bg-gradient-to-br from-primary to-secondary rounded-full opacity-20 blur-xl"></div>
<div class="absolute -bottom-6 -left-6 w-24 h-24 bg-gradient-to-br from-accent to-pink-500 rounded-full opacity-20 blur-xl"></div>
<!-- 名言内容 -->
<div class="relative z-10">
<div class="flex items-center mb-6">
<i class="fa fa-quote-left text-primary text-3xl mr-4"></i>
<h2 class="text-2xl md:text-3xl font-serif font-bold text-white text-shadow">每日名言</h2>
</div>
<div class="mb-8">
<!-- 英文名言 -->
<p id="quote-text-en" class="text-lg md:text-xl font-serif text-white/90 italic mb-4 text-shadow">
Loading...
</p>
<!-- 中文翻译 -->
<p id="quote-text-cn" class="text-xl md:text-2xl font-serif text-white leading-relaxed text-shadow mb-6">
正在加载名言...
</p>
<!-- 作者/来源 -->
<p id="quote-author" class="text-right text-lg text-white/80 italic">
—— 加载中
</p>
</div>
<!-- 控制按钮 -->
<div class="flex justify-between items-center">
<div class="flex space-x-3">
<button id="refresh-btn" class="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-full transition-all duration-300">
<i class="fa fa-refresh mr-2"></i>刷新
</button>
<button id="copy-btn" class="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-full transition-all duration-300">
<i class="fa fa-copy mr-2"></i>复制
</button>
</div>
<div class="text-white/70 text-sm">
<span id="refresh-status">5秒后自动刷新</span>
</div>
</div>
</div>
</div>
<!-- 底部信息 -->
<div class="mt-8 text-center text-white/60 text-sm">
<p>数据来源:多API智能切换 + 本地缓存</p>
</div>
</div>
</div>
<script>
// 背景图片列表
const backgrounds = [
"https://aka.doubaocdn.com/s/4fFv1xdoFn",
"https://aka.doubaocdn.com/s/f0CQ1xdoFn",
"https://aka.doubaocdn.com/s/fiCx1xdoFn",
"https://aka.doubaocdn.com/s/7ROy1xdoFn",
"https://aka.doubaocdn.com/s/8tmA1xdoFm",
"https://aka.doubaocdn.com/s/yrxV1xdoFm"
];
// 本地缓存的名言库(作为API失败时的后备)
const localQuotes = [
{ en: "The journey of a thousand miles begins with one step.", cn: "千里之行,始于足下。", author: "老子" },
{ en: "Where there's a will, there's a way.", cn: "有志者事竟成。", author: "谚语" },
{ en: "Knowledge is power.", cn: "知识就是力量。", author: "培根" },
{ en: "Practice makes perfect.", cn: "熟能生巧。", author: "谚语" },
{ en: "Time and tide wait for no man.", cn: "时不我待。", author: "谚语" },
{ en: "A journey of a thousand miles begins with a single step.", cn: "千里之行,始于足下。", author: "老子" },
{ en: "All that glitters is not gold.", cn: "闪光的不一定都是金子。", author: "莎士比亚" },
{ en: "Better late than never.", cn: "迟到总比不到好。", author: "谚语" },
{ en: "Birds of a feather flock together.", cn: "物以类聚,人以群分。", author: "谚语" },
{ en: "Constant dripping wears away the stone.", cn: "滴水穿石。", author: "谚语" },
{ en: "Actions speak louder than words.", cn: "行动胜于言语。", author: "谚语" },
{ en: "An apple a day keeps the doctor away.", cn: "一天一苹果,医生远离我。", author: "谚语" },
{ en: "Beauty is in the eye of the beholder.", cn: "情人眼里出西施。", author: "谚语" },
{ en: "Do as you would be done by.", cn: "己所不欲,勿施于人。", author: "孔子" },
{ en: "Every cloud has a silver lining.", cn: "黑暗中总有一线光明。", author: "谚语" }
];
let refreshInterval;
let refreshTime = 5; // 5秒刷新一次
let countdownInterval;
let currentQuote = null;
let apiIndex = 0;
// API配置列表(多个API源确保稳定性)
const API_LIST = [
{
url: 'https://api.lovelive.tools/api/SweetNothings',
parser: (data) => {
if (data.returnObj && data.returnObj.content) {
return {
en: '',
cn: data.returnObj.content,
author: data.returnObj.source || '未知'
};
}
throw new Error('API响应格式不正确');
}
},
{
url: 'http://open.iciba.com/dsapi/',
parser: (data) => {
if (data.content && data.note) {
return {
en: data.content,
cn: data.note,
author: data.translation ? data.translation.replace('小编的话:', '') : '金山词霸'
};
}
throw new Error('API响应格式不正确');
}
},
{
url: 'https://v1.hitokoto.cn/',
parser: (data) => {
if (data.hitokoto && data.from_who) {
return {
en: '',
cn: data.hitokoto,
author: data.from_who
};
}
throw new Error('API响应格式不正确');
}
}
];
// DOM元素
const quoteTextEn = document.getElementById('quote-text-en');
const quoteTextCn = document.getElementById('quote-text-cn');
const quoteAuthor = document.getElementById('quote-author');
const refreshBtn = document.getElementById('refresh-btn');
const copyBtn = document.getElementById('copy-btn');
const refreshStatus = document.getElementById('refresh-status');
const backgroundContainer = document.getElementById('background-container');
const quoteCard = document.getElementById('quote-card');
// 初始化
function init() {
// 首先尝试从本地缓存显示一条名言
showRandomLocalQuote();
// 然后尝试加载API数据
setTimeout(loadQuoteFromAPI, 1000);
// 设置自动刷新
startRefreshTimer();
// 设置按钮事件
refreshBtn.addEventListener('click', loadQuote);
copyBtn.addEventListener('click', copyQuote);
// 键盘快捷键
document.addEventListener('keydown', handleKeyPress);
// 触摸滑动支持
let touchStartX = 0;
let touchEndX = 0;
quoteCard.addEventListener('touchstart', (e) => {
touchStartX = e.changedTouches[0].screenX;
});
quoteCard.addEventListener('touchend', (e) => {
touchEndX = e.changedTouches[0].screenX;
handleSwipe();
});
function handleSwipe() {
const swipeThreshold = 50;
if (Math.abs(touchStartX - touchEndX) > swipeThreshold) {
loadQuote();
}
}
}
// 加载名言(先尝试API,失败则使用本地缓存)
function loadQuote() {
loadQuoteFromAPI().catch(() => {
showRandomLocalQuote();
});
}
// 从API加载名言
async function loadQuoteFromAPI() {
try {
// 显示加载状态
showLoadingState();
// 循环尝试所有API
for (let i = 0; i < API_LIST.length; i++) {
const api = API_LIST[(apiIndex + i) % API_LIST.length];
try {
console.log(`尝试调用API: ${api.url}`);
// 添加超时控制(5秒)
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
const response = await fetch(api.url, {
signal: controller.signal,
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`);
}
const data = await response.json();
console.log('API响应:', data);
// 使用API特定的解析器
currentQuote = api.parser(data);
// 更新显示
updateQuoteDisplay();
changeBackground();
resetRefreshTimer();
// API调用成功,更新下一次使用的API索引
apiIndex = (apiIndex + i) % API_LIST.length;
return;
} catch (error) {
console.error(`API调用失败: ${error.message}`);
// 继续尝试下一个API
}
}
// 所有API都失败了
throw new Error('所有API调用都失败了');
} catch (error) {
console.error('API加载失败:', error);
throw error;
}
}
// 显示本地缓存的随机名言
function showRandomLocalQuote() {
const randomIndex = Math.floor(Math.random() * localQuotes.length);
currentQuote = localQuotes[randomIndex];
updateQuoteDisplay();
changeBackground();
resetRefreshTimer();
}
// 显示加载状态
function showLoadingState() {
quoteTextEn.textContent = 'Loading...';
quoteTextCn.textContent = '正在加载名言...';
quoteAuthor.textContent = '—— 加载中';
quoteTextEn.style.opacity = '0.7';
quoteTextCn.style.opacity = '0.7';
quoteAuthor.style.opacity = '0.7';
}
// 更新名言显示
function updateQuoteDisplay() {
if (!currentQuote) return;
// 添加淡入淡出动画
quoteTextEn.style.opacity = '0';
quoteTextCn.style.opacity = '0';
quoteAuthor.style.opacity = '0';
setTimeout(() => {
quoteTextEn.textContent = currentQuote.en || '';
quoteTextCn.textContent = currentQuote.cn;
quoteAuthor.textContent = `—— ${currentQuote.author}`;
// 只在有英文内容时显示英文部分
if (currentQuote.en) {
quoteTextEn.style.display = 'block';
} else {
quoteTextEn.style.display = 'none';
}
quoteTextEn.style.opacity = '1';
quoteTextCn.style.opacity = '1';
quoteAuthor.style.opacity = '1';
// 添加文字动画
if (currentQuote.en) animateText(quoteTextEn);
animateText(quoteTextCn);
}, 300);
}
// 文字动画效果
function animateText(element) {
const text = element.textContent;
element.textContent = '';
let index = 0;
const interval = setInterval(() => {
if (index < text.length) {
element.textContent += text[index];
index++;
} else {
clearInterval(interval);
}
}, 50);
}
// 更换背景图片
function changeBackground() {
const randomBg = backgrounds[Math.floor(Math.random() * backgrounds.length)];
// 创建临时图片加载
const tempImg = new Image();
tempImg.src = randomBg;
tempImg.onload = () => {
backgroundContainer.style.opacity = '0';
setTimeout(() => {
backgroundContainer.style.backgroundImage = `url('${randomBg}')`;
backgroundContainer.style.opacity = '0.3';
}, 500);
};
// 图片加载失败时的处理
tempImg.onerror = () => {
console.error('背景图片加载失败');
// 使用默认背景色
backgroundContainer.style.backgroundImage = 'none';
backgroundContainer.style.opacity = '1';
};
}
// 复制名言到剪贴板
function copyQuote() {
if (!currentQuote) return;
let copyText = currentQuote.cn;
if (currentQuote.en) {
copyText = `${currentQuote.en}\n${currentQuote.cn}\n${currentQuote.author}`;
}
navigator.clipboard.writeText(copyText).then(() => {
// 显示复制成功提示
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="fa fa-check mr-2"></i>已复制';
setTimeout(() => {
copyBtn.innerHTML = originalText;
}, 2000);
}).catch(err => {
console.error('复制失败:', err);
// 降级处理:使用提示框显示内容
alert(`无法复制到剪贴板:\n\n${copyText}`);
});
}
// 开始刷新计时器
function startRefreshTimer() {
refreshInterval = setInterval(loadQuote, refreshTime * 1000);
startCountdown();
}
// 重置刷新计时器
function resetRefreshTimer() {
clearInterval(refreshInterval);
clearInterval(countdownInterval);
startRefreshTimer();
}
// 倒计时显示
function startCountdown() {
let timeLeft = refreshTime;
updateCountdownDisplay(timeLeft);
countdownInterval = setInterval(() => {
timeLeft--;
updateCountdownDisplay(timeLeft);
if (timeLeft <= 0) {
clearInterval(countdownInterval);
}
}, 1000);
}
// 更新倒计时显示
function updateCountdownDisplay(seconds) {
refreshStatus.textContent = `${seconds}秒后自动刷新`;
}
// 键盘快捷键处理
function handleKeyPress(e) {
switch(e.key) {
case ' ':
e.preventDefault();
loadQuote();
break;
case 'c':
case 'C':
if (e.ctrlKey || e.metaKey) {
e.preventDefault();
copyQuote();
}
break;
}
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', init);
</script>
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。











