gitlink_help_center/src/components/ReadingPosition.js

166 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useCallback } from 'react';
import { useLocation } from '@docusaurus/router';
const ReadingPosition = () => {
const location = useLocation();
// 保存滚动位置
const saveScrollPosition = useCallback(() => {
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
const currentPath = location.pathname;
// 使用localStorage保存每个页面的滚动位置
const savedPositions = JSON.parse(localStorage.getItem('docReadingPositions') || '{}');
savedPositions[currentPath] = {
scrollTop: scrollPosition,
timestamp: Date.now(),
};
// 只保留最近30天的阅读位置
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
Object.keys(savedPositions).forEach(path => {
if (savedPositions[path].timestamp < thirtyDaysAgo) {
delete savedPositions[path];
}
});
localStorage.setItem('docReadingPositions', JSON.stringify(savedPositions));
}, [location.pathname]);
// 恢复滚动位置
const restoreScrollPosition = useCallback(() => {
const currentPath = location.pathname;
const savedPositions = JSON.parse(localStorage.getItem('docReadingPositions') || '{}');
if (savedPositions[currentPath]) {
const { scrollTop, timestamp } = savedPositions[currentPath];
// 如果保存的位置是在最近7天内的才恢复位置
const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
if (timestamp > sevenDaysAgo && scrollTop > 100) { // 只有滚动超过100px才恢复
// 延迟恢复,确保页面完全加载
setTimeout(() => {
window.scrollTo({
top: scrollTop,
behavior: 'smooth'
});
// 显示提示信息
showPositionNotification(scrollTop);
}, 300);
}
}
}, [location.pathname]);
// 显示位置恢复提示
const showPositionNotification = (scrollTop) => {
// 创建提示元素
const notification = document.createElement('div');
notification.innerHTML = `
<div style="
position: fixed;
top: 80px;
right: 20px;
background: #25c2a0;
color: white;
padding: 12px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 9999;
font-size: 14px;
max-width: 280px;
animation: slideInRight 0.3s ease-out;
">
<div style="display: flex; align-items: center; gap: 8px;">
<span>📖</span>
<span>已恢复到上次阅读位置</span>
<button onclick="this.parentElement.parentElement.parentElement.remove()"
style="background: none; border: none; color: white; font-size: 16px; cursor: pointer; margin-left: auto;">×</button>
</div>
</div>
`;
document.body.appendChild(notification);
// 添加动画样式
if (!document.querySelector('#reading-position-styles')) {
const styles = document.createElement('style');
styles.id = 'reading-position-styles';
styles.textContent = `
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
`;
document.head.appendChild(styles);
}
// 3秒后自动隐藏
setTimeout(() => {
if (notification.parentElement) {
notification.remove();
}
}, 3000);
};
// 节流函数
const throttle = (func, delay) => {
let timeoutId;
let lastExecTime = 0;
return function (...args) {
const currentTime = Date.now();
if (currentTime - lastExecTime > delay) {
func.apply(this, args);
lastExecTime = currentTime;
} else {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
lastExecTime = Date.now();
}, delay - (currentTime - lastExecTime));
}
};
};
useEffect(() => {
// 页面加载时恢复滚动位置
restoreScrollPosition();
// 监听滚动事件,节流保存位置
const throttledSavePosition = throttle(saveScrollPosition, 1000);
window.addEventListener('scroll', throttledSavePosition);
// 页面卸载时保存位置
const handleBeforeUnload = () => {
saveScrollPosition();
};
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('scroll', throttledSavePosition);
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [saveScrollPosition, restoreScrollPosition]);
// 路径变化时保存当前位置并尝试恢复新位置
useEffect(() => {
saveScrollPosition();
// 短暂延迟后恢复新页面的位置
const timer = setTimeout(() => {
restoreScrollPosition();
}, 100);
return () => clearTimeout(timer);
}, [location.pathname, saveScrollPosition, restoreScrollPosition]);
return null; // 这是一个功能性组件,不渲染任何内容
};
export default ReadingPosition;