- body 改为固定(background-attachment: fixed)的深色岩层渐变+噪点纹理,作为全站统一背景,所有子页面的主体区域都与首页品牌视觉背景一致 - 新增全局唯一的跟随鼠标粒子画布(.site-particles,position:fixed铺满视口),替换掉原来仅限首页hero/子页面page-hero局部的粒子画布 - about.css/pages.css/resources.css 全面转换为深色主题配色:白色卡片背景改为半透明深蓝玻璃质感,深色文字改为冰蓝/浅蓝,浅色边框改为半透明冰蓝边框 - 顶部导航:默认完全透明,滚动后切换为与背景同色调的深色玻璃底色,不再出现白色导航栏 - 底部导航:footer-fade渐变上色改为与页面背景一致(rock-navy-0),下色改为更深的近黑色,形成无缝过渡 - 底部导航结构调整:合作伙伴模块删除,合并为「关于海古纪」列表下的一个链接项;「关于海古纪」下的隐私协议链接删除,改为footer-bottom的“隐私政策”链接跳转到实际隐私协议内容 - 联系信息电话号码字体与其余文字保持一致(去除等宽字体特殊样式) - footer-section标题颜色统一改为与首页“最新动态”一致的冰蓝色(--ice-blue)
152 lines
5.0 KiB
JavaScript
152 lines
5.0 KiB
JavaScript
/**
|
|
* 全站深色岩层背景上的粒子网络效果
|
|
* 固定在视口内(position: fixed),任意页面、任意滚动位置都可见,跟随鼠标移动。
|
|
*/
|
|
(function () {
|
|
var canvas = document.querySelector('.site-particles');
|
|
if (!canvas) return;
|
|
|
|
var ctx = canvas.getContext('2d');
|
|
var DPR = window.devicePixelRatio || 1;
|
|
var prefersReducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
|
var width = 0;
|
|
var height = 0;
|
|
var particles = [];
|
|
var mouse = { x: null, y: null, active: false };
|
|
|
|
var config = {
|
|
density: 9000,
|
|
maxParticles: 130,
|
|
linkDistance: 150,
|
|
mouseLinkDistance: 220,
|
|
mouseForce: 1.1,
|
|
dotRadiusMin: 1.6,
|
|
dotRadiusMax: 3.4,
|
|
dotColor: '124, 211, 252',
|
|
lineColor: '56, 189, 248'
|
|
};
|
|
|
|
function resize() {
|
|
width = window.innerWidth;
|
|
height = window.innerHeight;
|
|
canvas.width = Math.max(1, Math.round(width * DPR));
|
|
canvas.height = Math.max(1, Math.round(height * DPR));
|
|
ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
|
|
createParticles();
|
|
}
|
|
|
|
function createParticles() {
|
|
var count = Math.min(config.maxParticles, Math.floor((width * height) / config.density));
|
|
particles = new Array(Math.max(count, 30)).fill(null).map(function () {
|
|
return {
|
|
x: Math.random() * width,
|
|
y: Math.random() * height,
|
|
vx: (Math.random() - 0.5) * 0.4,
|
|
vy: (Math.random() - 0.5) * 0.4,
|
|
r: Math.random() * (config.dotRadiusMax - config.dotRadiusMin) + config.dotRadiusMin
|
|
};
|
|
});
|
|
}
|
|
|
|
document.addEventListener('mousemove', function (e) {
|
|
mouse.x = e.clientX;
|
|
mouse.y = e.clientY;
|
|
mouse.active = true;
|
|
});
|
|
document.addEventListener('mouseleave', function () { mouse.active = false; });
|
|
|
|
function step() {
|
|
particles.forEach(function (p) {
|
|
p.x += p.vx;
|
|
p.y += p.vy;
|
|
|
|
if (p.x <= 0 || p.x >= width) p.vx *= -1;
|
|
if (p.y <= 0 || p.y >= height) p.vy *= -1;
|
|
|
|
p.x = Math.max(0, Math.min(width, p.x));
|
|
p.y = Math.max(0, Math.min(height, p.y));
|
|
|
|
if (mouse.active) {
|
|
var dx = mouse.x - p.x;
|
|
var dy = mouse.y - p.y;
|
|
var dist = Math.hypot(dx, dy);
|
|
if (dist < config.mouseLinkDistance && dist > 0.001) {
|
|
var pull = (1 - dist / config.mouseLinkDistance) * config.mouseForce;
|
|
p.x -= (dx / dist) * pull;
|
|
p.y -= (dy / dist) * pull;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function render() {
|
|
ctx.clearRect(0, 0, width, height);
|
|
|
|
for (var i = 0; i < particles.length; i++) {
|
|
for (var j = i + 1; j < particles.length; j++) {
|
|
var dx = particles[i].x - particles[j].x;
|
|
var dy = particles[i].y - particles[j].y;
|
|
var dist = Math.hypot(dx, dy);
|
|
if (dist < config.linkDistance) {
|
|
ctx.strokeStyle = 'rgba(' + config.lineColor + ', ' + ((1 - dist / config.linkDistance) * 0.45) + ')';
|
|
ctx.lineWidth = 0.7;
|
|
ctx.beginPath();
|
|
ctx.moveTo(particles[i].x, particles[i].y);
|
|
ctx.lineTo(particles[j].x, particles[j].y);
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
|
|
if (mouse.active) {
|
|
var mdx = particles[i].x - mouse.x;
|
|
var mdy = particles[i].y - mouse.y;
|
|
var mdist = Math.hypot(mdx, mdy);
|
|
if (mdist < config.mouseLinkDistance) {
|
|
ctx.strokeStyle = 'rgba(' + config.lineColor + ', ' + ((1 - mdist / config.mouseLinkDistance) * 0.65) + ')';
|
|
ctx.lineWidth = 0.9;
|
|
ctx.beginPath();
|
|
ctx.moveTo(particles[i].x, particles[i].y);
|
|
ctx.lineTo(mouse.x, mouse.y);
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
}
|
|
|
|
particles.forEach(function (p) {
|
|
ctx.beginPath();
|
|
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
|
|
ctx.fillStyle = 'rgba(' + config.dotColor + ', 0.85)';
|
|
ctx.shadowColor = 'rgba(56, 189, 248, 0.8)';
|
|
ctx.shadowBlur = 7;
|
|
ctx.fill();
|
|
ctx.shadowBlur = 0;
|
|
});
|
|
|
|
if (mouse.active) {
|
|
ctx.beginPath();
|
|
ctx.arc(mouse.x, mouse.y, 2.6, 0, Math.PI * 2);
|
|
ctx.fillStyle = 'rgba(255, 255, 255, 0.85)';
|
|
ctx.shadowColor = 'rgba(255, 255, 255, 0.85)';
|
|
ctx.shadowBlur = 12;
|
|
ctx.fill();
|
|
ctx.shadowBlur = 0;
|
|
}
|
|
}
|
|
|
|
function loop() {
|
|
step();
|
|
render();
|
|
requestAnimationFrame(loop);
|
|
}
|
|
|
|
window.addEventListener('resize', resize);
|
|
resize();
|
|
|
|
if (!prefersReducedMotion) {
|
|
loop();
|
|
} else {
|
|
render();
|
|
}
|
|
})();
|