添加官网景深效果

- 移除突兀的代码
- 为官网加装景深
- 修复PKG的导入
This commit is contained in:
Falck
2026-04-06 14:48:26 +08:00
parent d3dab8a27a
commit 0e5c28e0b3
45 changed files with 526 additions and 7003 deletions

View File

@@ -17,7 +17,6 @@
{ separator: true },
{ href: 'quickstart.html', tooltip: '快速开始', svg: 'M13 10V3L4 14h7v7l9-11h-7z' },
{ separator: true },
{ href: 'community/', tooltip: '社区', svg: 'M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z' },
{ href: 'https://gitee.com/starlight-apk/feature-oss', tooltip: '源码', svg: 'M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4', target: '_blank' }
];

View File

@@ -0,0 +1,83 @@
// 通用鼠标位置追踪器
// 提供全局鼠标位置状态,供其他模块使用
class MouseTracker {
constructor() {
this.x = 0; // 原始 X (0 ~ window.innerWidth)
this.y = 0; // 原始 Y (0 ~ window.innerHeight)
this.normalizedX = 0; // 归一化 X (-1 ~ 1)
this.normalizedY = 0; // 归一化 Y (-1 ~ 1)
this.smoothX = 0; // 平滑后的 X
this.smoothY = 0; // 平滑后的 Y
this.listeners = [];
this.smoothing = 0.1; // 平滑系数 (0~1越小越平滑)
this.isInside = false;
this._init();
}
_init() {
document.addEventListener('mousemove', (e) => {
this.x = e.clientX;
this.y = e.clientY;
this.normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
this.normalizedY = (e.clientY / window.innerHeight) * 2 - 1;
this.isInside = true;
this._notify();
});
document.addEventListener('mouseleave', () => {
this.isInside = false;
});
// 平滑动画循环
this._animate();
}
_animate() {
// 平滑插值
this.smoothX += (this.normalizedX - this.smoothX) * this.smoothing;
this.smoothY += (this.normalizedY - this.smoothY) * this.smoothing;
requestAnimationFrame(() => this._animate());
}
_notify() {
this.listeners.forEach(cb => {
cb({
x: this.x,
y: this.y,
normalizedX: this.normalizedX,
normalizedY: this.normalizedY,
smoothX: this.smoothX,
smoothY: this.smoothY,
isInside: this.isInside
});
});
}
// 添加监听器
onUpdate(callback) {
this.listeners.push(callback);
}
// 移除监听器
offUpdate(callback) {
const index = this.listeners.indexOf(callback);
if (index > -1) this.listeners.splice(index, 1);
}
// 设置平滑系数
setSmoothing(value) {
this.smoothing = Math.max(0, Math.min(1, value));
}
// 获取当前平滑值
getSmooth() {
return { x: this.smoothX, y: this.smoothY };
}
}
// 导出全局实例
window.mouseTracker = new MouseTracker();

219
website/js/parallax.js Normal file
View File

@@ -0,0 +1,219 @@
// 统一视差追踪器
// 自动检测并使用最佳输入源:鼠标 > 陀螺仪 > 触摸滑动
class ParallaxTracker {
constructor() {
this.smoothX = 0;
this.smoothY = 0;
this.targetX = 0;
this.targetY = 0;
this.elements = [];
this.listeners = [];
this.maxMove = 100; // 最大移动像素
this.smoothing = 0.08; // 平滑系数
this.isReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
this.activeSource = null; // 'mouse' | 'gyro' | 'touch'
this.rafId = null;
if (this.isReducedMotion) {
console.log('[Parallax] 用户偏好减少动画,已跳过');
return;
}
this._init();
}
_init() {
// 查找视差元素
document.querySelectorAll('[data-parallax-speed]').forEach(el => {
const speed = parseFloat(el.dataset.parallaxSpeed) || 0.1;
this.elements.push({ el, speed });
el.style.willChange = 'transform';
});
if (this.elements.length === 0) return;
const isMobile = this._isMobileDevice();
// 按优先级尝试输入源
if (isMobile && this._initGyroscope()) {
this.activeSource = 'gyro';
console.log('[Parallax] 使用陀螺仪输入');
} else if (!isMobile && this._initMouse()) {
this.activeSource = 'mouse';
console.log('[Parallax] 使用鼠标输入');
} else if (isMobile) {
// 移动端回退方案:触摸滑动
this._initTouchFallback();
this.activeSource = 'touch';
console.log('[Parallax] 使用触摸滑动输入');
} else {
// 电脑端最后的回退:仍然尝试鼠标
this._initMouse();
this.activeSource = 'mouse';
console.log('[Parallax] 使用鼠标输入(回退)');
}
// 启动动画循环
this._animate();
console.log(`[Parallax] 已初始化 ${this.elements.length} 个元素`);
}
// 检测是否为移动设备
_isMobileDevice() {
return /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|| (navigator.maxTouchPoints && navigator.maxTouchPoints > 2);
}
// ── 鼠标输入 ──
_initMouse() {
// 支持鼠标和触摸指针的设备
const hasFinePointer = window.matchMedia('(pointer: fine)').matches;
document.addEventListener('mousemove', (e) => {
this.targetX = (e.clientX / window.innerWidth) * 2 - 1;
this.targetY = (e.clientY / window.innerHeight) * 2 - 1;
});
// 即使没有 fine pointer 也返回 true让鼠标事件始终生效
return true;
}
// ── 陀螺仪输入 ──
_initGyroscope() {
if (!window.DeviceOrientationEvent) return false;
let hasFired = false;
const handler = (e) => {
// gamma: 左右倾斜 (-90 ~ 90)
// beta: 前后倾斜 (-180 ~ 180)
if (e.gamma === null && e.beta === null) return;
if (!hasFired) {
hasFired = true;
console.log('[Parallax] 陀螺仪数据已就绪');
}
// 归一化到 -1 ~ 1限制在 ±45 度范围内)
this.targetX = Math.max(-1, Math.min(1, e.gamma / 45));
this.targetY = Math.max(-1, Math.min(1, (e.beta - 45) / 45)); // 减去 45° 自然持握角度
};
// iOS 13+ 需要用户授权
if (typeof DeviceOrientationEvent.requestPermission === 'function') {
// 添加点击按钮请求权限的提示
this._addGyroPermissionButton(handler);
return false; // 等待用户授权后再启用
}
window.addEventListener('deviceorientation', handler);
return true;
}
_addGyroPermissionButton(handler) {
// iOS 设备需要用户交互才能请求陀螺仪权限
const btn = document.createElement('div');
btn.id = 'gyro-permission-btn';
btn.innerHTML = `
<div class="gyro-permission-overlay">
<div class="gyro-permission-card">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="32" height="32">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"/>
</svg>
<p>开启陀螺仪视差</p>
<span>移动设备时页面会跟随滚动</span>
<button>允许</button>
</div>
</div>
`;
document.body.appendChild(btn);
btn.querySelector('button').addEventListener('click', async () => {
try {
const permission = await DeviceOrientationEvent.requestPermission();
if (permission === 'granted') {
window.addEventListener('deviceorientation', handler);
this.activeSource = 'gyro';
console.log('[Parallax] 陀螺仪权限已授予');
}
} catch (err) {
console.warn('[Parallax] 陀螺仪权限被拒绝', err);
}
btn.remove();
});
// 点击遮罩关闭
btn.querySelector('.gyro-permission-overlay').addEventListener('click', () => btn.remove());
}
// ── 触摸滑动输入 ──
_initTouchFallback() {
let startX = 0, startY = 0;
let currentX = 0, currentY = 0;
let isTouching = false;
document.addEventListener('touchstart', (e) => {
isTouching = true;
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
currentX = startX;
currentY = startY;
}, { passive: true });
document.addEventListener('touchmove', (e) => {
if (!isTouching) return;
currentX = e.touches[0].clientX;
currentY = e.touches[0].clientY;
// 计算相对于起始点的偏移
const deltaX = (currentX - startX) / window.innerWidth;
const deltaY = (currentY - startY) / window.innerHeight;
this.targetX = Math.max(-1, Math.min(1, deltaX * 4)); // 放大灵敏度
this.targetY = Math.max(-1, Math.min(1, deltaY * 4));
}, { passive: true });
document.addEventListener('touchend', () => {
isTouching = false;
// 缓慢回归中心
const resetInterval = setInterval(() => {
this.targetX *= 0.9;
this.targetY *= 0.9;
if (Math.abs(this.targetX) < 0.01 && Math.abs(this.targetY) < 0.01) {
this.targetX = 0;
this.targetY = 0;
clearInterval(resetInterval);
}
}, 16);
}, { passive: true });
}
// ── 动画循环 ──
_animate() {
// 平滑插值(指数移动平均)
this.smoothX += (this.targetX - this.smoothX) * this.smoothing;
this.smoothY += (this.targetY - this.smoothY) * this.smoothing;
// 应用变换
this.elements.forEach(({ el, speed }) => {
const moveX = -this.smoothX * speed * this.maxMove;
const moveY = -this.smoothY * speed * this.maxMove;
el.style.transform = `translate3d(${moveX}px, ${moveY}px, 0)`;
});
requestAnimationFrame(() => this._animate());
}
// 添加监听器(供其他模块使用)
onUpdate(callback) {
this.listeners.push(callback);
}
// 获取当前平滑值
getSmooth() {
return { x: this.smoothX, y: this.smoothY };
}
}
// 导出全局实例
window.parallaxTracker = new ParallaxTracker();