mirror of
http://junepve.top:3000/chicrobot/mainPage.git
synced 2026-10-30 04:21:29 +08:00
583 lines
19 KiB
JavaScript
583 lines
19 KiB
JavaScript
// 获取所有导航链接
|
|
const navLinks = document.querySelectorAll('.nav-link');
|
|
const sections = document.querySelectorAll('section');
|
|
|
|
// 点击导航链接时更新active类
|
|
navLinks.forEach(link => {
|
|
link.addEventListener('click', function() {
|
|
// 移除所有链接的active类
|
|
navLinks.forEach(l => l.classList.remove('active'));
|
|
// 为当前点击的链接添加active类
|
|
this.classList.add('active');
|
|
});
|
|
});
|
|
|
|
// 滚动时更新active类
|
|
function updateActiveNavLink() {
|
|
let currentSectionId = '';
|
|
sections.forEach(section => {
|
|
const sectionTop = section.offsetTop;
|
|
const sectionHeight = section.clientHeight;
|
|
if (window.scrollY >= (sectionTop - 200) && window.scrollY < (sectionTop + sectionHeight - 200)) {
|
|
currentSectionId = section.getAttribute('id');
|
|
}
|
|
});
|
|
|
|
navLinks.forEach(link => {
|
|
link.classList.remove('active');
|
|
if (link.getAttribute('href') === '#' + currentSectionId) {
|
|
link.classList.add('active');
|
|
}
|
|
});
|
|
}
|
|
|
|
// 监听滚动事件,使用节流优化
|
|
let isScrolling;
|
|
window.addEventListener('scroll', function() {
|
|
// 清除之前的计时器
|
|
clearTimeout(isScrolling);
|
|
// 设置一个新的计时器,在滚动停止后50毫秒执行
|
|
isScrolling = setTimeout(updateActiveNavLink, 50);
|
|
}, false);
|
|
|
|
// 页面加载时也执行一次
|
|
updateActiveNavLink();
|
|
|
|
window.addEventListener('load', function() {
|
|
// 获取当前页面的域名或IP
|
|
const currentHost = window.location.hostname;
|
|
// 构建跳转URL (保持相同的协议和域名/IP)
|
|
const portainerUrl = `${window.location.protocol}//${currentHost}:9000`;
|
|
const giteaUrl = `${window.location.protocol}//${currentHost}:3000`;
|
|
const grafanaUrl = `${window.location.protocol}//${currentHost}:3001`;
|
|
const jenkinsUrl = `${window.location.protocol}//${currentHost}:9095`;
|
|
const chatUrl = `${window.location.protocol}//${currentHost}/baseTalk`;
|
|
const cloudreveUrl = `${window.location.protocol}//${currentHost}:5212`;
|
|
// 设置链接的href属性和target属性,确保在新标签中打开
|
|
const portainerLink = document.getElementById('portainerLink');
|
|
const giteaLink = document.getElementById('giteaLink');
|
|
const grafanaLink = document.getElementById('grafanaLink');
|
|
const jenkinsLink = document.getElementById('jenkinsLink');
|
|
const chatLink = document.getElementById('chatLink');
|
|
const cloudreveLink = document.getElementById('cloudreveLink');
|
|
if (portainerLink) {
|
|
portainerLink.href = portainerUrl;
|
|
portainerLink.target = '_blank';
|
|
}
|
|
if (giteaLink) {
|
|
giteaLink.href = giteaUrl;
|
|
giteaLink.target = '_blank';
|
|
}
|
|
if (grafanaLink) {
|
|
grafanaLink.href = grafanaUrl;
|
|
grafanaLink.target = '_blank';
|
|
}
|
|
if (jenkinsLink) {
|
|
jenkinsLink.href = jenkinsUrl;
|
|
jenkinsLink.target = '_blank';
|
|
}
|
|
if (chatLink) {
|
|
chatLink.href = chatUrl;
|
|
chatLink.target = '_blank';
|
|
}
|
|
if (cloudreveLink) {
|
|
cloudreveLink.href = cloudreveUrl;
|
|
cloudreveLink.target = '_blank';
|
|
}
|
|
});
|
|
|
|
// 粒子背景效果
|
|
const canvas = document.getElementById('particleCanvas');
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
// 设置画布大小
|
|
function setCanvasSize() {
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
}
|
|
|
|
setCanvasSize();
|
|
window.addEventListener('resize', setCanvasSize);
|
|
|
|
// 粒子类
|
|
class Particle {
|
|
constructor() {
|
|
this.x = Math.random() * canvas.width;
|
|
this.y = Math.random() * canvas.height;
|
|
this.size = Math.random() * 2 + 0.5;
|
|
this.speedX = Math.random() * 0.5 - 0.25;
|
|
this.speedY = Math.random() * 0.5 - 0.25;
|
|
this.color = Math.random() > 0.5 ? '#00ffeb' : '#ff6b35';
|
|
this.alpha = Math.random() * 0.7 + 0.3;
|
|
}
|
|
|
|
update() {
|
|
this.x += this.speedX;
|
|
this.y += this.speedY;
|
|
|
|
// 边界检测
|
|
if (this.x < 0 || this.x > canvas.width) this.speedX *= -1;
|
|
if (this.y < 0 || this.y > canvas.height) this.speedY *= -1;
|
|
}
|
|
|
|
draw() {
|
|
ctx.fillStyle = `${this.color}${Math.floor(this.alpha * 255).toString(16)}`;
|
|
ctx.beginPath();
|
|
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
}
|
|
|
|
// 创建粒子数组
|
|
const particles = [];
|
|
const particleCount = 150;
|
|
|
|
for (let i = 0; i < particleCount; i++) {
|
|
particles.push(new Particle());
|
|
}
|
|
|
|
// 连接粒子效果
|
|
function connectParticles() {
|
|
for (let a = 0; a < particles.length; a++) {
|
|
for (let b = a; b < particles.length; b++) {
|
|
const dx = particles[a].x - particles[b].x;
|
|
const dy = particles[a].y - particles[b].y;
|
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
|
|
if (distance < 100) {
|
|
// 根据粒子颜色决定连接线颜色
|
|
const color = particles[a].color === '#00ffeb' && particles[b].color === '#00ffeb' ? '#00ffeb' :
|
|
particles[a].color === '#ff6b35' && particles[b].color === '#ff6b35' ? '#ff6b35' : '#e0e0e0';
|
|
|
|
ctx.beginPath();
|
|
ctx.strokeStyle = `rgba(${hexToRgb(color).r}, ${hexToRgb(color).g}, ${hexToRgb(color).b}, ${0.1 - distance/1000})`;
|
|
ctx.lineWidth = 0.5;
|
|
ctx.moveTo(particles[a].x, particles[a].y);
|
|
ctx.lineTo(particles[b].x, particles[b].y);
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 十六进制颜色转RGB
|
|
function hexToRgb(hex) {
|
|
// 移除#如果存在
|
|
hex = hex.replace('#', '');
|
|
|
|
// 解析RGB值
|
|
const r = parseInt(hex.substring(0, 2), 16);
|
|
const g = parseInt(hex.substring(2, 4), 16);
|
|
const b = parseInt(hex.substring(4, 6), 16);
|
|
|
|
return { r, g, b };
|
|
}
|
|
|
|
// 系统状态图表
|
|
const ctxChart = document.getElementById('systemChart').getContext('2d');
|
|
|
|
// 生成随机数据
|
|
function generateData() {
|
|
return Array.from({length: 20}, () => Math.floor(Math.random() * 80) + 20);
|
|
}
|
|
|
|
const cpuData = generateData();
|
|
const memoryData = generateData();
|
|
const networkData = generateData();
|
|
const labels = Array.from({length: 20}, (_, i) => i);
|
|
|
|
// 根据主题获取图表颜色配置
|
|
function getChartColors(theme) {
|
|
switch(theme) {
|
|
case 'cyber':
|
|
return {
|
|
cpuBorder: '#ff6b35',
|
|
cpuBackground: 'rgba(255, 107, 53, 0.1)',
|
|
memoryBorder: '#00ffeb',
|
|
memoryBackground: 'rgba(0, 255, 235, 0.1)',
|
|
networkBorder: '#e0e0e0',
|
|
networkBackground: 'rgba(224, 224, 224, 0.1)',
|
|
gridColor: 'rgba(255, 255, 255, 0.05)',
|
|
tickColor: 'rgba(224, 224, 224, 0.7)',
|
|
legendColor: 'rgba(224, 224, 224, 0.9)',
|
|
tooltipBackground: 'rgba(10, 14, 23, 0.8)',
|
|
tooltipText: '#e0e0e0',
|
|
tooltipBorder: 'rgba(255, 255, 255, 0.1)'
|
|
};
|
|
case 'minimal':
|
|
return {
|
|
cpuBorder: '#333333', // 深灰色
|
|
cpuBackground: 'rgba(51, 51, 51, 0.1)',
|
|
memoryBorder: '#666666', // 中灰色
|
|
memoryBackground: 'rgba(102, 102, 102, 0.1)',
|
|
networkBorder: '#999999', // 浅灰色
|
|
networkBackground: 'rgba(153, 153, 153, 0.1)',
|
|
gridColor: 'rgba(0, 0, 0, 0.05)',
|
|
tickColor: 'rgba(224, 224, 224, 0.7)',
|
|
legendColor: 'rgba(224, 224, 224, 0.9)',
|
|
tooltipBackground: 'rgba(10, 14, 23, 0.8)',
|
|
tooltipText: '#e0e0e0',
|
|
tooltipBorder: 'rgba(0, 0, 0, 0.1)'
|
|
};
|
|
case 'gallery':
|
|
return {
|
|
cpuBorder: '#ff758f', // 粉色
|
|
cpuBackground: 'rgba(255, 117, 143, 0.1)',
|
|
memoryBorder: '#4dabf7', // 蓝色
|
|
memoryBackground: 'rgba(77, 171, 247, 0.1)',
|
|
networkBorder: '#333333',
|
|
networkBackground: 'rgba(51, 51, 51, 0.1)',
|
|
gridColor: 'rgba(0, 0, 0, 0.05)',
|
|
tickColor: 'rgba(224, 224, 224, 0.7)',
|
|
legendColor: 'rgba(224, 224, 224, 0.9)',
|
|
tooltipBackground: 'rgba(10, 14, 23, 0.8)',
|
|
tooltipText: '#e0e0e0',
|
|
tooltipBorder: 'rgba(77, 171, 247, 0.2)'
|
|
};
|
|
default:
|
|
return getChartColors('cyber');
|
|
}
|
|
}
|
|
|
|
// 创建图表
|
|
function createSystemChart(theme) {
|
|
const colors = getChartColors(theme);
|
|
|
|
return new Chart(ctxChart, {
|
|
type: 'line',
|
|
data: {
|
|
labels: labels,
|
|
datasets: [
|
|
{
|
|
label: 'CPU 使用率 (%)',
|
|
data: cpuData,
|
|
borderColor: colors.cpuBorder,
|
|
backgroundColor: colors.cpuBackground,
|
|
tension: 0.4,
|
|
fill: true,
|
|
borderWidth: 2,
|
|
pointRadius: 0, // 设置为0移除圆点
|
|
pointHoverRadius: 0 // 设置为0移除悬停圆点
|
|
},
|
|
{
|
|
label: '内存使用率 (%)',
|
|
data: memoryData,
|
|
borderColor: colors.memoryBorder,
|
|
backgroundColor: colors.memoryBackground,
|
|
tension: 0.4,
|
|
fill: true,
|
|
borderWidth: 2,
|
|
pointRadius: 0, // 设置为0移除圆点
|
|
pointHoverRadius: 0 // 设置为0移除悬停圆点
|
|
},
|
|
{
|
|
label: '网络流量 (MB/s)',
|
|
data: networkData,
|
|
borderColor: colors.networkBorder,
|
|
backgroundColor: colors.networkBackground,
|
|
tension: 0.4,
|
|
fill: true,
|
|
borderWidth: 2,
|
|
pointRadius: 0, // 设置为0移除圆点
|
|
pointHoverRadius: 0 // 设置为0移除悬停圆点
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
x: {
|
|
grid: {
|
|
color: colors.gridColor
|
|
},
|
|
ticks: {
|
|
color: colors.tickColor
|
|
}
|
|
},
|
|
y: {
|
|
grid: {
|
|
color: colors.gridColor
|
|
},
|
|
ticks: {
|
|
color: colors.tickColor
|
|
},
|
|
beginAtZero: true,
|
|
max: 100
|
|
}
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
labels: {
|
|
color: colors.legendColor
|
|
}
|
|
},
|
|
tooltip: {
|
|
mode: 'index',
|
|
intersect: false,
|
|
backgroundColor: colors.tooltipBackground,
|
|
titleColor: colors.tooltipText,
|
|
bodyColor: colors.tooltipText,
|
|
borderColor: colors.tooltipBorder,
|
|
borderWidth: 1,
|
|
boxPadding: 10,
|
|
bodyPadding: 10
|
|
}
|
|
},
|
|
interaction: {
|
|
mode: 'nearest',
|
|
axis: 'x',
|
|
intersect: false
|
|
},
|
|
animations: {
|
|
tension: {
|
|
duration: 1000,
|
|
easing: 'linear'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// 初始化图表
|
|
let systemChart = createSystemChart(document.body.getAttribute('data-theme') || 'cyber');
|
|
|
|
// 更新图表数据
|
|
function updateChart() {
|
|
cpuData.shift();
|
|
cpuData.push(Math.floor(Math.random() * 80) + 20);
|
|
|
|
memoryData.shift();
|
|
memoryData.push(Math.floor(Math.random() * 80) + 20);
|
|
|
|
networkData.shift();
|
|
networkData.push(Math.floor(Math.random() * 50) + 5);
|
|
|
|
systemChart.update();
|
|
}
|
|
|
|
// 每3秒更新一次图表
|
|
setInterval(updateChart, 3000);
|
|
|
|
// 导航栏滚动效果
|
|
window.addEventListener('scroll', () => {
|
|
const navbar = document.querySelector('.navbar');
|
|
if (window.scrollY > 50) {
|
|
navbar.style.padding = '15px 0';
|
|
navbar.style.boxShadow = '0 5px 15px rgba(0, 0, 0, 0.3)';
|
|
} else {
|
|
navbar.style.padding = '20px 0';
|
|
navbar.style.boxShadow = 'none';
|
|
}
|
|
});
|
|
|
|
// 服务卡片悬停效果增强
|
|
const serviceCards = document.querySelectorAll('.service-card');
|
|
|
|
serviceCards.forEach(card => {
|
|
card.addEventListener('mouseenter', () => {
|
|
card.style.transform = 'translateY(-10px) scale(1.02)';
|
|
card.style.boxShadow = '0 15px 30px rgba(0, 0, 0, 0.5), 0 0 15px rgba(0, 255, 235, 0.3)';
|
|
});
|
|
|
|
card.addEventListener('mouseleave', () => {
|
|
card.style.transform = 'translateY(0) scale(1)';
|
|
card.style.boxShadow = '0 8px 32px 0 rgba(0, 0, 0, 0.37)';
|
|
});
|
|
});
|
|
|
|
// 平滑滚动和链接处理
|
|
document.querySelectorAll('a').forEach(anchor => {
|
|
anchor.addEventListener('click', function (e) {
|
|
const href = this.getAttribute('href');
|
|
const target = this.getAttribute('target');
|
|
|
|
// 如果已经设置了target="_blank",让浏览器正常处理跳转
|
|
if (target === '_blank') {
|
|
return;
|
|
}
|
|
|
|
// 检查是否是有效的页面内ID选择器(以#开头且不是URL)
|
|
if (href && href.startsWith('#') && href.length > 1 && !href.includes('://')) {
|
|
const targetElement = document.querySelector(href);
|
|
if (targetElement) {
|
|
// 只在找到目标元素时阻止默认行为
|
|
e.preventDefault();
|
|
targetElement.scrollIntoView({
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
// 如果找不到目标元素,不阻止默认行为
|
|
} else if (href === '#') {
|
|
// 处理 href="#" 的情况,滚动到页面顶部
|
|
e.preventDefault();
|
|
window.scrollTo({
|
|
top: 0,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
// 对于URL链接等其他情况,如果没有设置target="_blank",添加它
|
|
else if (href && !href.startsWith('#') && (href.includes('://') || href.startsWith('//'))) {
|
|
// 不阻止默认行为,让浏览器处理带target="_blank"的跳转
|
|
this.target = '_blank';
|
|
}
|
|
});
|
|
});
|
|
|
|
// 视觉风格切换器功能
|
|
const themeToggle = document.getElementById('themeToggle');
|
|
const themeMenu = document.getElementById('themeMenu');
|
|
const themeOptions = document.querySelectorAll('.theme-option');
|
|
const particleCanvas = document.getElementById('particleCanvas');
|
|
// 在animate()函数前添加animationId变量声明
|
|
let animationId;
|
|
|
|
function animate() {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
for (let i = 0; i < particles.length; i++) {
|
|
particles[i].update();
|
|
particles[i].draw();
|
|
}
|
|
|
|
// 连接相近的粒子
|
|
connectParticles();
|
|
|
|
animationId = requestAnimationFrame(animate);
|
|
}
|
|
|
|
// 页面加载完成后执行
|
|
window.addEventListener('load', () => {
|
|
// 添加页面加载动画
|
|
document.body.classList.add('loaded');
|
|
// 初始化粒子颜色
|
|
const initialTheme = document.body.getAttribute('data-theme') || 'cyber';
|
|
updateParticleColors(initialTheme);
|
|
// 初始化动态效果
|
|
toggleDynamicEffects(initialTheme);
|
|
// 启动动画(确保在DOM加载完成后)
|
|
animate();
|
|
});
|
|
|
|
// 切换菜单显示/隐藏
|
|
themeToggle.addEventListener('click', () => {
|
|
themeMenu.classList.toggle('active');
|
|
});
|
|
|
|
// 点击页面其他区域关闭菜单
|
|
document.addEventListener('click', (e) => {
|
|
if (!themeToggle.contains(e.target) && !themeMenu.contains(e.target)) {
|
|
themeMenu.classList.remove('active');
|
|
}
|
|
});
|
|
|
|
// 主题切换功能
|
|
themeOptions.forEach(option => {
|
|
option.addEventListener('click', () => {
|
|
// 移除所有选项的active类
|
|
themeOptions.forEach(opt => opt.classList.remove('active'));
|
|
// 为当前选项添加active类
|
|
option.classList.add('active');
|
|
// 获取主题值
|
|
const theme = option.getAttribute('data-theme');
|
|
// 设置body的data-theme属性
|
|
document.body.setAttribute('data-theme', theme);
|
|
// 更新图标
|
|
updateThemeIcon(theme);
|
|
// 更新粒子背景颜色
|
|
updateParticleColors(theme);
|
|
// 切换动态效果
|
|
toggleDynamicEffects(theme);
|
|
// 关闭菜单
|
|
themeMenu.classList.remove('active');
|
|
});
|
|
});
|
|
|
|
// 更新主题图标
|
|
function updateThemeIcon(theme) {
|
|
const icon = themeToggle.querySelector('i');
|
|
switch(theme) {
|
|
case 'cyber':
|
|
icon.className = 'fas fa-bolt';
|
|
break;
|
|
case 'minimal':
|
|
icon.className = 'fas fa-moon';
|
|
break;
|
|
case 'gallery':
|
|
icon.className = 'fas fa-palette';
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 更新粒子背景颜色
|
|
function updateParticleColors(theme) {
|
|
let primaryColor, secondaryColor;
|
|
let particleVisibility = 'visible';
|
|
|
|
switch(theme) {
|
|
case 'cyber':
|
|
primaryColor = '#00ffeb';
|
|
secondaryColor = '#ff6b35';
|
|
particleVisibility = 'visible';
|
|
break;
|
|
case 'minimal':
|
|
primaryColor = '#6c757d';
|
|
secondaryColor = '#6c757d';
|
|
particleVisibility = 'hidden';
|
|
break;
|
|
case 'gallery':
|
|
primaryColor = '#ff758f'; // 粉色
|
|
secondaryColor = '#4dabf7'; // 蓝色
|
|
particleVisibility = 'visible';
|
|
break;
|
|
}
|
|
|
|
// 设置粒子可见性
|
|
if (particleCanvas) {
|
|
particleCanvas.style.visibility = particleVisibility;
|
|
}
|
|
|
|
particles.forEach(particle => {
|
|
particle.color = Math.random() > 0.5 ? primaryColor : secondaryColor;
|
|
});
|
|
}
|
|
|
|
// 切换动态效果
|
|
function toggleDynamicEffects(theme) {
|
|
// 暂停/恢复粒子动画
|
|
if (theme === 'minimal') {
|
|
if (animationId) {
|
|
cancelAnimationFrame(animationId);
|
|
animationId = null;
|
|
}
|
|
// 移除视差滚动效果
|
|
document.querySelectorAll('[data-parallax]').forEach(el => {
|
|
el.style.transform = 'translateY(0)';
|
|
el.removeAttribute('data-parallax');
|
|
el.setAttribute('data-parallax-disabled', 'true');
|
|
});
|
|
// 移除悬停效果
|
|
document.querySelectorAll('.service-card, .thought-card, .friend-card, .lab-card').forEach(card => {
|
|
card.style.transform = 'none';
|
|
card.style.boxShadow = 'none';
|
|
});
|
|
} else {
|
|
// 恢复粒子动画
|
|
if (!animationId) {
|
|
animate();
|
|
}
|
|
// 恢复视差滚动效果
|
|
document.querySelectorAll('[data-parallax-disabled]').forEach(el => {
|
|
el.setAttribute('data-parallax', '0.3');
|
|
el.removeAttribute('data-parallax-disabled');
|
|
});
|
|
}
|
|
|
|
// 添加以下代码来更新图表颜色
|
|
// 销毁旧图表
|
|
if (systemChart) {
|
|
systemChart.destroy();
|
|
}
|
|
// 使用新主题创建新图表
|
|
systemChart = createSystemChart(theme);
|
|
} |