Skip to content

Commit 969be6e

Browse files
author
coderzcr
committed
feat(目录插件): 改进目录项点击的滚动定位算法
- 实现高级滚动定位算法,根据标题级别计算理想视口位置 - 添加滚动完成后的微调逻辑,确保标题准确居中显示 - 改进docsify钩子系统的访问方式,增加错误处理 - 优化活动项的高亮显示时机和样式
1 parent b8e4f56 commit 969be6e

File tree

1 file changed

+78
-10
lines changed

1 file changed

+78
-10
lines changed

index.html

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,69 @@
289289
console.log('[目录插件] 点击目录项,滚动到:', heading.id);
290290
const target = document.getElementById(heading.id);
291291
if (target) {
292+
// 高级滚动定位算法,确保标题准确显示在理想位置
293+
294+
// 获取标题级别
295+
const level = parseInt(heading.tagName.substring(1));
296+
297+
// 计算视口位置(调整为视口中心位置)
298+
const viewportHeight = window.innerHeight;
299+
const idealViewportPosition = viewportHeight * 0.5; // 设置为0.5,让标题在视口中居中显示
300+
// 获取目标元素在视口中的位置
301+
const targetRect = target.getBoundingClientRect();
302+
303+
// 计算当前滚动位置
304+
const currentScrollTop = window.pageYOffset || document.documentElement.scrollTop;
305+
306+
// 根据标题级别调整偏移量
307+
let levelOffset = 0;
308+
if (level === 1) levelOffset = 30;
309+
else if (level === 2) levelOffset = 20;
310+
else if (level === 3) levelOffset = 10;
311+
else levelOffset = 5;
312+
313+
// 计算最终滚动位置
314+
const targetScrollTop = currentScrollTop + targetRect.top - idealViewportPosition - levelOffset;
315+
316+
console.log(`[目录插件] 滚动计算 - 级别: ${level}, 当前滚动: ${currentScrollTop}, 目标偏移: ${targetRect.top}, 理想位置: ${idealViewportPosition}, 最终位置: ${targetScrollTop}`);
317+
318+
// 执行平滑滚动
292319
window.scrollTo({
293-
top: target.offsetTop - 100,
320+
top: Math.max(0, targetScrollTop), // 确保不小于0
294321
behavior: 'smooth'
295322
});
323+
324+
// 滚动完成后的微调逻辑
325+
// 平滑滚动完成后(约800ms后)检查实际位置并微调,确保居中
326+
setTimeout(() => {
327+
// 再次获取目标元素的位置
328+
const newTargetRect = target.getBoundingClientRect();
329+
const newScrollTop = window.pageYOffset || document.documentElement.scrollTop;
330+
331+
// 计算实际位置与理想位置的差值
332+
const positionDiff = newTargetRect.top - idealViewportPosition;
333+
334+
// 如果差值超过阈值(3px),进行微调
335+
if (Math.abs(positionDiff) > 3) {
336+
console.log(`[目录插件] 微调滚动位置 - 差值: ${positionDiff}`);
337+
// 微调时使用非平滑滚动以避免抖动
338+
window.scrollTo({
339+
top: newScrollTop - positionDiff,
340+
behavior: 'auto'
341+
});
342+
}
343+
}, 800); // 增加延迟时间,确保滚动完全完成
344+
345+
// 高亮当前活动项
346+
setTimeout(() => {
347+
const allLinks = document.querySelectorAll('.article-toc a');
348+
allLinks.forEach(a => {
349+
a.style.backgroundColor = 'transparent';
350+
a.style.color = '#555';
351+
});
352+
this.style.backgroundColor = 'rgba(66, 185, 131, 0.1)';
353+
this.style.color = '#42b983';
354+
}, 100);
296355
}
297356
};
298357

@@ -380,15 +439,24 @@
380439
setTimeout(createTableOfContents, 100);
381440
});
382441

383-
// 尝试访问docsify的钩子系统
384-
if (window.$docsify && window.$docsify.hooks) {
385-
console.log('[目录插件] 成功访问docsify钩子系统');
386-
window.$docsify.hooks.doneEach(function() {
387-
console.log('[目录插件] docsify doneEach钩子触发,生成目录');
388-
createTableOfContents();
389-
});
390-
} else {
391-
console.log('[目录插件] 无法访问docsify钩子系统,使用备选方案');
442+
// 尝试安全地访问docsify的钩子系统
443+
try {
444+
if (window.$docsify && window.$docsify.hooks && typeof window.$docsify.hooks === 'object') {
445+
console.log('[目录插件] 成功访问docsify钩子系统');
446+
// 使用安全的方式设置钩子
447+
if (typeof window.$docsify.hooks.doneEach === 'function') {
448+
window.$docsify.hooks.doneEach(function() {
449+
console.log('[目录插件] docsify doneEach钩子触发,生成目录');
450+
createTableOfContents();
451+
});
452+
} else {
453+
console.log('[目录插件] doneEach钩子不可用,使用备选方案');
454+
}
455+
} else {
456+
console.log('[目录插件] 无法访问docsify钩子系统,使用备选方案');
457+
}
458+
} catch (error) {
459+
console.warn('[目录插件] 访问docsify钩子时出错:', error);
392460
}
393461
}
394462

0 commit comments

Comments
 (0)