Skip to content

Commit a27fabf

Browse files
committed
feat: support quotes page logic
1 parent b6db9a6 commit a27fabf

File tree

1 file changed

+270
-0
lines changed

1 file changed

+270
-0
lines changed

assets/js/quotes.js

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
// 读取本地 JSON 文件
2+
fetch('/quotes/proverbs.json')
3+
.then(response => {
4+
if (!response.ok) {
5+
throw new Error('Network response was not ok');
6+
}
7+
return response.json();
8+
})
9+
.then(data => {
10+
// 将 JSON 数据分配给一个常量
11+
const quotes = data;
12+
13+
// 在 JavaScript 中以常量方式使用 JSON 数据
14+
// console.log("JSON Data:", quotes);
15+
16+
let currentIndex = 0;
17+
let isAnimating = false;
18+
let interpretationVisible = false;
19+
20+
const quoteCard = document.getElementById('quoteCard');
21+
const interpretationContainer = document.getElementById('interpretationContainer');
22+
const quoteInterpretation = document.getElementById('quoteInterpretation');
23+
const svgContainer = document.getElementById('svgContainer');
24+
const prevButton = document.getElementById('prevButton');
25+
const nextButton = document.getElementById('nextButton');
26+
const backToTopButton = document.getElementById('backToTop');
27+
const canvas = document.getElementById('canvas');
28+
const ctx = canvas.getContext('2d');
29+
30+
function createQuoteCard(quote, direction = 'next') {
31+
if (isAnimating) return;
32+
isAnimating = true;
33+
34+
const quoteCard = document.getElementById('quoteCard');
35+
const newQuoteCard = document.createElement('div');
36+
newQuoteCard.className = 'quote-card';
37+
newQuoteCard.innerHTML = `
38+
<div class="quote-chinese">${quote.chinese}</div>
39+
<div class="quote-english">${quote.english}</div>
40+
${quote.author ? `<div class="quote-author">— ${quote.author}</div>` : ''}
41+
${quote.source ? `<div class="quote-source">出处 / Source: ${formatSource(quote.source)}</div>` : ''}
42+
`;
43+
44+
// 设置新卡片的初始位置
45+
newQuoteCard.classList.add(direction === 'next' ? 'slide-right' : 'slide-left');
46+
47+
// 添加新卡片到容器
48+
const container = document.getElementById('quoteCardContainer');
49+
container.appendChild(newQuoteCard);
50+
51+
// 触发重排
52+
void newQuoteCard.offsetWidth;
53+
54+
// 开始动画
55+
quoteCard.classList.add('fade-out');
56+
quoteCard.classList.add(direction === 'next' ? 'slide-left' : 'slide-right');
57+
newQuoteCard.classList.add('fade-in');
58+
newQuoteCard.classList.remove('slide-right', 'slide-left');
59+
60+
// 动画结束后移除旧卡片
61+
setTimeout(() => {
62+
if (container.contains(quoteCard)) {
63+
container.removeChild(quoteCard);
64+
}
65+
newQuoteCard.id = 'quoteCard';
66+
isAnimating = false;
67+
}, 300);
68+
69+
// 更新解释和SVG
70+
quoteInterpretation.innerHTML = `
71+
<div class="interpretation-title">箴言新解 / Proverbs Insights:</div>
72+
<p class="interpretation-chinese">${quote.interpretation.zh || 'TODO'}</p>
73+
<p class="interpretation-english">${quote.interpretation.en || 'TODO'}</p>
74+
`;
75+
svgContainer.innerHTML = quote.svg || '';
76+
}
77+
78+
function formatSource(source) {
79+
// 匹配文本中括号内的URL
80+
const urlRegex = /(.*?)\((https?:\/\/[^\s)]+)\)/g;
81+
82+
return source.replace(urlRegex, (match, text, url) => {
83+
return `<a href="${url}" target="_blank" rel="noopener noreferrer">${text}</a>`;
84+
});
85+
}
86+
87+
// function drawQuoteCard(quote) {
88+
// // 设置背景
89+
// ctx.fillStyle = '#f8f4e5';
90+
// ctx.fillRect(0, 0, canvas.width, canvas.height);
91+
92+
// // 绘制背景图案
93+
// drawBackgroundPattern();
94+
95+
// // 绘制半透明白色背景
96+
// ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
97+
// ctx.fillRect(20, 20, canvas.width - 40, canvas.height - 40);
98+
99+
// // 设置字体和颜色
100+
// ctx.fillStyle = '#2d3748';
101+
102+
// // 绘制中文引用
103+
// ctx.font = '24px "Ma Shan Zheng"';
104+
// let yPos = wrapText(ctx, quote.chinese, 40, 60, canvas.width - 80, 36);
105+
106+
// // 绘制英文引用
107+
// ctx.font = '18px "Noto Serif"';
108+
// yPos = wrapText(ctx, quote.english, 40, yPos + 20, canvas.width - 80, 28);
109+
110+
// // 绘制作者
111+
// if (quote.author) {
112+
// ctx.font = 'italic 14px "Noto Serif"';
113+
// ctx.textAlign = 'right';
114+
// ctx.fillText(`— ${quote.author}`, canvas.width - 40, yPos + 30);
115+
// yPos += 30;
116+
// }
117+
118+
// // 绘制出处
119+
// if (quote.source) {
120+
// ctx.font = '14px "Noto Serif"';
121+
// ctx.fillStyle = '#6b7280';
122+
// ctx.textAlign = 'right';
123+
// // 对于图片,我们总是显示完整的URL
124+
// const sourceText = `出处 / Source: ${quote.source}`;
125+
// ctx.fillText(sourceText, canvas.width - 40, yPos + 20);
126+
// }
127+
128+
// // 绘制标题和分类
129+
// ctx.font = '16px "Ma Shan Zheng"';
130+
// ctx.fillStyle = '#2d3748'; // 使用与引用相同的颜色
131+
// ctx.textAlign = 'left';
132+
// ctx.fillText('Go 语言箴言新解', 40, canvas.height - 20);
133+
// ctx.textAlign = 'right';
134+
// ctx.fillText(quote.category, canvas.width - 40, canvas.height - 20);
135+
136+
// // 绘制新解
137+
// yPos += 40;
138+
// ctx.font = '16px "Noto Serif"';
139+
// ctx.fillStyle = '#4a5568';
140+
// ctx.textAlign = 'left';
141+
// yPos = wrapText(ctx, '箴言新解:', 40, yPos, canvas.width - 80, 24);
142+
// yPos = wrapText(ctx, quote.interpretation.zh || 'TODO', 40, yPos + 10, canvas.width - 80, 24);
143+
144+
// // 绘制SVG(如果有)
145+
// if (quote.svg) {
146+
// yPos += 20;
147+
// drawSVG(ctx, quote.svg, 40, yPos);
148+
// }
149+
// }
150+
151+
// function drawSVG(ctx, svgString, x, y) {
152+
// // 这个函数需要实现SVG到Canvas的转换
153+
// // 由于复杂性,这里只是一个占位符
154+
// ctx.font = '14px Arial';
155+
// ctx.fillText('SVG图表位置', x, y + 50);
156+
// }
157+
158+
// function drawBackgroundPattern() {
159+
// const patternCanvas = document.createElement('canvas');
160+
// const patternContext = patternCanvas.getContext('2d');
161+
// patternCanvas.width = 100;
162+
// patternCanvas.height = 100;
163+
164+
// patternContext.fillStyle = '#e6dfcc';
165+
// patternContext.globalAlpha = 0.4;
166+
167+
// // 绘制简化版的背景图案
168+
// patternContext.beginPath();
169+
// patternContext.arc(11, 18, 7, 0, 2 * Math.PI);
170+
// patternContext.arc(59, 43, 7, 0, 2 * Math.PI);
171+
// patternContext.arc(34, 90, 3, 0, 2 * Math.PI);
172+
// patternContext.fill();
173+
174+
// const pattern = ctx.createPattern(patternCanvas, 'repeat');
175+
// ctx.fillStyle = pattern;
176+
// ctx.fillRect(0, 0, canvas.width, canvas.height);
177+
// }
178+
179+
// function wrapText(context, text, x, y, maxWidth, lineHeight) {
180+
// const words = text.split('');
181+
// let line = '';
182+
// let currentY = y;
183+
184+
// for (let n = 0; n < words.length; n++) {
185+
// const testLine = line + words[n];
186+
// const metrics = context.measureText(testLine);
187+
// const testWidth = metrics.width;
188+
// if (testWidth > maxWidth && n > 0) {
189+
// context.fillText(line, x, currentY);
190+
// line = words[n];
191+
// currentY += lineHeight;
192+
// } else {
193+
// line = testLine;
194+
// }
195+
// }
196+
// context.fillText(line, x, currentY);
197+
// return currentY; // 返回最后一行的 Y 坐标
198+
// }
199+
200+
function prevQuote() {
201+
if (!isAnimating) {
202+
currentIndex = (currentIndex - 1 + quotes.length) % quotes.length;
203+
createQuoteCard(quotes[currentIndex], 'prev');
204+
}
205+
}
206+
207+
function nextQuote() {
208+
if (!isAnimating) {
209+
currentIndex = (currentIndex + 1) % quotes.length;
210+
createQuoteCard(quotes[currentIndex], 'next');
211+
}
212+
}
213+
214+
let touchStartX = 0;
215+
let touchEndX = 0;
216+
217+
// 触摸事件
218+
document.addEventListener('touchstart', function(e) {
219+
touchStartX = e.changedTouches[0].screenX;
220+
}, false);
221+
222+
document.addEventListener('touchend', function(e) {
223+
touchEndX = e.changedTouches[0].screenX;
224+
handleSwipe();
225+
}, false);
226+
227+
// 添加按钮点击事件
228+
prevButton.addEventListener('click', prevQuote);
229+
nextButton.addEventListener('click', nextQuote);
230+
231+
// 处理手势滑动
232+
document.addEventListener('touchstart', function(e) {
233+
touchStartX = e.changedTouches[0].screenX;
234+
}, false);
235+
236+
document.addEventListener('touchend', function(e) {
237+
touchEndX = e.changedTouches[0].screenX;
238+
handleSwipe();
239+
}, false);
240+
241+
function handleSwipe() {
242+
if (isAnimating) return;
243+
const swipeThreshold = 50; // 最小滑动距离
244+
if (touchEndX < touchStartX - swipeThreshold) {
245+
nextQuote(); // 左滑,下一个引用
246+
}
247+
if (touchEndX > touchStartX + swipeThreshold) {
248+
prevQuote(); // 右滑,上一个引用
249+
}
250+
}
251+
252+
// 处理回到顶部按钮
253+
window.addEventListener('scroll', function() {
254+
if (window.pageYOffset > 300) {
255+
backToTopButton.style.display = 'flex';
256+
} else {
257+
backToTopButton.style.display = 'none';
258+
}
259+
});
260+
261+
backToTopButton.addEventListener('click', function() {
262+
window.scrollTo({ top: 0, behavior: 'smooth' });
263+
});
264+
265+
// 初始化第一个引用
266+
createQuoteCard(quotes[0]);
267+
})
268+
.catch(error => {
269+
console.error('There was a problem with the fetch operation:', error);
270+
});

0 commit comments

Comments
 (0)