|
289 | 289 | console.log('[目录插件] 点击目录项,滚动到:', heading.id); |
290 | 290 | const target = document.getElementById(heading.id); |
291 | 291 | 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 | + // 执行平滑滚动 |
292 | 319 | window.scrollTo({ |
293 | | - top: target.offsetTop - 100, |
| 320 | + top: Math.max(0, targetScrollTop), // 确保不小于0 |
294 | 321 | behavior: 'smooth' |
295 | 322 | }); |
| 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); |
296 | 355 | } |
297 | 356 | }; |
298 | 357 |
|
|
380 | 439 | setTimeout(createTableOfContents, 100); |
381 | 440 | }); |
382 | 441 |
|
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); |
392 | 460 | } |
393 | 461 | } |
394 | 462 |
|
|
0 commit comments