feat(about): 添加导航项的无障碍标签并优化二维码加载逻辑
- 为首页和博客导航链接增加 aria-label 属性以提升无障碍访问性 - 移除微信公众号模态框中失效的二维码降级提示注释 - 删除冗余的 moments.js 和粒子系统相关脚本文件内容
This commit is contained in:
@@ -74,7 +74,7 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="nav-menu">
|
<div class="nav-menu">
|
||||||
<a href="index.html" class="nav-item">
|
<a href="index.html" class="nav-item" aria-label="Home">
|
||||||
<i class="ri-home-smile-2-line"></i>
|
<i class="ri-home-smile-2-line"></i>
|
||||||
<span class="nav-label" data-i18n="nav.home">首页</span>
|
<span class="nav-label" data-i18n="nav.home">首页</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
<i class="ri-user-3-line"></i>
|
<i class="ri-user-3-line"></i>
|
||||||
<span class="nav-label" data-i18n="nav.about">关于</span>
|
<span class="nav-label" data-i18n="nav.about">关于</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://blog.hehouhui.cn" class="nav-item">
|
<a href="https://blog.hehouhui.cn" class="nav-item" aria-label="Honesty Blog">
|
||||||
<i class="ri-quill-pen-line"></i>
|
<i class="ri-quill-pen-line"></i>
|
||||||
<span class="nav-label" data-i18n="nav.blog">博客</span>
|
<span class="nav-label" data-i18n="nav.blog">博客</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -321,7 +321,6 @@
|
|||||||
<h3 data-i18n="modal.wechat">Official Account</h3>
|
<h3 data-i18n="modal.wechat">Official Account</h3>
|
||||||
<div class="qr-box">
|
<div class="qr-box">
|
||||||
<img src="./images/mp-honesy.jpg" alt="WeChat QR" onerror="this.style.display='none';this.nextElementSibling.style.display='block'" loading="lazy" width="200" height="200">
|
<img src="./images/mp-honesy.jpg" alt="WeChat QR" onerror="this.style.display='none';this.nextElementSibling.style.display='block'" loading="lazy" width="200" height="200">
|
||||||
<!-- <div class="qr-fallback">QR Load Failed</div>-->
|
|
||||||
</div>
|
</div>
|
||||||
<p data-i18n="modal.desc">Scan to follow Tech Share</p>
|
<p data-i18n="modal.desc">Scan to follow Tech Share</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,11 +15,18 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Home",
|
"name": "Home",
|
||||||
"stargazers_count": 2,
|
"stargazers_count": 3,
|
||||||
"forks_count": 0,
|
"forks_count": 0,
|
||||||
"description": "现代化个人主页,融合科技感与个性化元素,展示个人技术栈、开源项目和博客文章。",
|
"description": "现代化个人主页,融合科技感与个性化元素,展示个人技术栈、开源项目和博客文章。",
|
||||||
"html_url": "https://github.com/listener-He/Home"
|
"html_url": "https://github.com/listener-He/Home"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "auto-ip2region",
|
||||||
|
"stargazers_count": 2,
|
||||||
|
"forks_count": 0,
|
||||||
|
"description": "Auto IP2Region 是一个智能化的IP地址地理信息解析库,它结合了本地数据库和多个免费在线API服务,通过智能负载均衡和自动故障转移机制,为您提供准确、可靠的IP地理位置信息查询服务。",
|
||||||
|
"html_url": "https://github.com/listener-He/auto-ip2region"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "collection-complete",
|
"name": "collection-complete",
|
||||||
"stargazers_count": 2,
|
"stargazers_count": 2,
|
||||||
@@ -34,6 +41,13 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"html_url": "https://github.com/listener-He/keycloak-services-social-weixin"
|
"html_url": "https://github.com/listener-He/keycloak-services-social-weixin"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "fast-blur",
|
||||||
|
"stargazers_count": 1,
|
||||||
|
"forks_count": 0,
|
||||||
|
"description": "High-performance, lightweight data obfuscation library for Java. Offers fast, reversible data transformation without traditional encryption overhead. Ideal for caching, logging, and performance-critical applications. / 专为Java设计的高性能轻量级数据混淆库。提供快速、可逆的数据转换,无需传统加密开销。适用于缓存、日志和性能关键型应用。",
|
||||||
|
"html_url": "https://github.com/listener-He/fast-blur"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Notion-Wechat-Blog",
|
"name": "Notion-Wechat-Blog",
|
||||||
"stargazers_count": 1,
|
"stargazers_count": 1,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"blog": "https://www.hehouhui.cn",
|
"blog": "https://www.hehouhui.cn",
|
||||||
"hireable": true,
|
"hireable": true,
|
||||||
"bio": "Hi, I’m Honesty—Shanghai Java/AI Dev (7+ yrs). Spring AI, LLM, TensorFlow, Faiss. Write tech content, cycle for inspiration. AI-obsessed.",
|
"bio": "Hi, I’m Honesty—Shanghai Java/AI Dev (7+ yrs). Spring AI, LLM, TensorFlow, Faiss. Write tech content, cycle for inspiration. AI-obsessed.",
|
||||||
"public_repos": 165,
|
"public_repos": 167,
|
||||||
"public_gists": 0,
|
"public_gists": 0,
|
||||||
"followers": 6,
|
"followers": 6,
|
||||||
"following": 12,
|
"following": 12,
|
||||||
|
|||||||
@@ -1,369 +0,0 @@
|
|||||||
class ParticleSystem {
|
|
||||||
constructor(options = {}) {
|
|
||||||
this.particleCount = options.particleCount || 22000;
|
|
||||||
this.theme = options.theme || 'day';
|
|
||||||
this.callbacks = options.callbacks || {};
|
|
||||||
|
|
||||||
// 动画控制相关属性
|
|
||||||
this.scaleFactor = 1.0;
|
|
||||||
this.diffusionFactor = 1.0;
|
|
||||||
this.forwardFactor = 0.0;
|
|
||||||
this.speedFactor = 1.0;
|
|
||||||
|
|
||||||
// 当前动画类型
|
|
||||||
this.currentAnimation = 'saturn'; // 默认为土星动画
|
|
||||||
|
|
||||||
// 动画状态
|
|
||||||
this.animationState = 'scattered'; // 默认状态为分散状态
|
|
||||||
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
this.geometry = new THREE.BufferGeometry();
|
|
||||||
this.positions = new Float32Array(this.particleCount * 3);
|
|
||||||
this.targets = new Float32Array(this.particleCount * 3);
|
|
||||||
this.origin = new Float32Array(this.particleCount * 3);
|
|
||||||
this.velocities = new Float32Array(this.particleCount * 3);
|
|
||||||
this.sizes = new Float32Array(this.particleCount);
|
|
||||||
this.seeds = new Float32Array(this.particleCount);
|
|
||||||
|
|
||||||
this.simplex = new SimplexNoise();
|
|
||||||
|
|
||||||
// 初始化为默认的平铺散布状态
|
|
||||||
this.initScatteredParticles();
|
|
||||||
|
|
||||||
this.geometry.setAttribute('position', new THREE.BufferAttribute(this.positions, 3));
|
|
||||||
this.geometry.setAttribute('size', new THREE.BufferAttribute(this.sizes, 1));
|
|
||||||
this.geometry.setAttribute('seed', new THREE.BufferAttribute(this.seeds, 1));
|
|
||||||
|
|
||||||
this.createMaterial();
|
|
||||||
this.particleSystem = new THREE.Points(this.geometry, this.material);
|
|
||||||
|
|
||||||
if (this.callbacks.onInit) {
|
|
||||||
this.callbacks.onInit(this.particleSystem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化平铺散布的粒子分布(默认状态)
|
|
||||||
initScatteredParticles() {
|
|
||||||
for (let i = 0; i < this.particleCount; i++) {
|
|
||||||
const i3 = i * 3;
|
|
||||||
|
|
||||||
// 在一个较大的空间内随机分布粒子
|
|
||||||
const x = (Math.random() - 0.5) * 2000;
|
|
||||||
const y = (Math.random() - 0.5) * 2000;
|
|
||||||
const z = (Math.random() - 0.5) * 2000;
|
|
||||||
|
|
||||||
this.positions[i3] = x;
|
|
||||||
this.origin[i3] = x;
|
|
||||||
this.targets[i3] = x;
|
|
||||||
this.positions[i3 + 1] = y;
|
|
||||||
this.origin[i3 + 1] = y;
|
|
||||||
this.targets[i3 + 1] = y;
|
|
||||||
this.positions[i3 + 2] = z;
|
|
||||||
this.origin[i3 + 2] = z;
|
|
||||||
this.targets[i3 + 2] = z;
|
|
||||||
|
|
||||||
this.sizes[i] = (Math.random() * 2.5 + 0.5) * (this.theme === 'day' ? 1.3 : 1.0);
|
|
||||||
this.seeds[i] = Math.random();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化土星动画的粒子分布
|
|
||||||
initSaturnParticles() {
|
|
||||||
// 土星主体粒子数量 (约占70%)
|
|
||||||
const planetParticleCount = Math.floor(this.particleCount * 0.7);
|
|
||||||
// 土星环粒子数量 (约占30%)
|
|
||||||
const ringParticleCount = this.particleCount - planetParticleCount;
|
|
||||||
|
|
||||||
// 初始化土星主体粒子 (球形分布)
|
|
||||||
for (let i = 0; i < planetParticleCount; i++) {
|
|
||||||
const i3 = i * 3;
|
|
||||||
const y = 1 - (i / (planetParticleCount - 1)) * 2;
|
|
||||||
const radius = Math.sqrt(1 - y * y);
|
|
||||||
const theta = i * 2.39996;
|
|
||||||
const r = 100; // 土星半径
|
|
||||||
|
|
||||||
const x = Math.cos(theta) * radius * r;
|
|
||||||
const z = Math.sin(theta) * radius * r;
|
|
||||||
const py = y * r;
|
|
||||||
|
|
||||||
this.positions[i3] = x;
|
|
||||||
this.origin[i3] = x;
|
|
||||||
this.targets[i3] = x;
|
|
||||||
this.positions[i3 + 1] = py;
|
|
||||||
this.origin[i3 + 1] = py;
|
|
||||||
this.targets[i3 + 1] = py;
|
|
||||||
this.positions[i3 + 2] = z;
|
|
||||||
this.origin[i3 + 2] = z;
|
|
||||||
this.targets[i3 + 2] = z;
|
|
||||||
|
|
||||||
this.sizes[i] = (Math.random() * 2.5 + 0.5) * (this.theme === 'day' ? 1.3 : 1.0);
|
|
||||||
this.seeds[i] = Math.random();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化土星环粒子 (圆环分布)
|
|
||||||
for (let i = planetParticleCount; i < this.particleCount; i++) {
|
|
||||||
const i3 = i * 3;
|
|
||||||
// 多个环的配置
|
|
||||||
const ringConfigs = [
|
|
||||||
{ innerRadius: 130, outerRadius: 140 },
|
|
||||||
{ innerRadius: 150, outerRadius: 160 },
|
|
||||||
{ innerRadius: 170, outerRadius: 180 },
|
|
||||||
{ innerRadius: 190, outerRadius: 200 }
|
|
||||||
];
|
|
||||||
|
|
||||||
// 分配到不同环
|
|
||||||
const ringIndex = (i - planetParticleCount) % ringConfigs.length;
|
|
||||||
const ringConfig = ringConfigs[ringIndex];
|
|
||||||
|
|
||||||
// 在环内随机分布
|
|
||||||
const angle = Math.random() * Math.PI * 2;
|
|
||||||
const radius = ringConfig.innerRadius + Math.random() * (ringConfig.outerRadius - ringConfig.innerRadius);
|
|
||||||
|
|
||||||
const x = Math.cos(angle) * radius;
|
|
||||||
const z = Math.sin(angle) * radius;
|
|
||||||
const y = (Math.random() - 0.5) * 20; // 环有一定的厚度
|
|
||||||
|
|
||||||
this.positions[i3] = x;
|
|
||||||
this.origin[i3] = x;
|
|
||||||
this.targets[i3] = x;
|
|
||||||
this.positions[i3 + 1] = y;
|
|
||||||
this.origin[i3 + 1] = y;
|
|
||||||
this.targets[i3 + 1] = y;
|
|
||||||
this.positions[i3 + 2] = z;
|
|
||||||
this.origin[i3 + 2] = z;
|
|
||||||
this.targets[i3 + 2] = z;
|
|
||||||
|
|
||||||
this.sizes[i] = (Math.random() * 1.5 + 0.5) * (this.theme === 'day' ? 1.3 : 1.0);
|
|
||||||
this.seeds[i] = Math.random();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createMaterial() {
|
|
||||||
const colors = this.theme === 'day'
|
|
||||||
? {base: new THREE.Color(0x2c3e50), active: new THREE.Color(0x0055ff)}
|
|
||||||
: {base: new THREE.Color(0xFFFFFF), active: new THREE.Color(0x00FFFF)};
|
|
||||||
|
|
||||||
const paletteA = this.theme === 'day' ? new THREE.Color(0x6ec3ff) : new THREE.Color(0x00ffff);
|
|
||||||
const paletteB = this.theme === 'day' ? new THREE.Color(0xffb4c8) : new THREE.Color(0xff7af3);
|
|
||||||
const blending = this.theme === 'day' ? THREE.NormalBlending : THREE.AdditiveBlending;
|
|
||||||
|
|
||||||
this.material = new THREE.ShaderMaterial({
|
|
||||||
uniforms: {
|
|
||||||
scale: {value: window.innerHeight / 2},
|
|
||||||
baseColor: {value: colors.base},
|
|
||||||
activeColor: {value: colors.active},
|
|
||||||
mixVal: {value: 0.0},
|
|
||||||
time: {value: 0.0},
|
|
||||||
paletteA: {value: paletteA},
|
|
||||||
paletteB: {value: paletteB},
|
|
||||||
nebulaIntensity: {value: 0.0}
|
|
||||||
},
|
|
||||||
vertexShader: `
|
|
||||||
attribute float size;
|
|
||||||
attribute float seed;
|
|
||||||
varying float vSeed;
|
|
||||||
void main() {
|
|
||||||
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
|
||||||
gl_PointSize = size * (500.0 / -mvPosition.z);
|
|
||||||
gl_Position = projectionMatrix * mvPosition;
|
|
||||||
vSeed = seed;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
fragmentShader: `
|
|
||||||
uniform vec3 baseColor;
|
|
||||||
uniform vec3 activeColor;
|
|
||||||
uniform vec3 paletteA;
|
|
||||||
uniform vec3 paletteB;
|
|
||||||
uniform float mixVal;
|
|
||||||
uniform float time;
|
|
||||||
uniform float nebulaIntensity;
|
|
||||||
varying float vSeed;
|
|
||||||
void main() {
|
|
||||||
float r = length(gl_PointCoord - vec2(0.5));
|
|
||||||
if (r > 0.5) discard;
|
|
||||||
vec3 baseMix = mix(baseColor, activeColor, mixVal);
|
|
||||||
float drift = 0.5 + 0.5 * sin(time * 0.15 + vSeed * 6.28318);
|
|
||||||
vec3 nebula = mix(paletteA, paletteB, drift);
|
|
||||||
vec3 finalColor = mix(baseMix, nebula, nebulaIntensity);
|
|
||||||
gl_FragColor = vec4(finalColor, 1.0);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
blending: blending,
|
|
||||||
depthTest: false,
|
|
||||||
transparent: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
update(time, mode, handCount, handL, handR) {
|
|
||||||
const adjustedTime = time * this.speedFactor;
|
|
||||||
this.material.uniforms.time.value = adjustedTime;
|
|
||||||
|
|
||||||
if (this.theme === 'night') {
|
|
||||||
this.material.uniforms.nebulaIntensity.value = mode === 'UNLOCKED' ? 0.55 : 0.35;
|
|
||||||
} else {
|
|
||||||
this.material.uniforms.nebulaIntensity.value = mode === 'UNLOCKED' ? 0.22 : 0.12;
|
|
||||||
}
|
|
||||||
|
|
||||||
let targetMix = 0;
|
|
||||||
|
|
||||||
if (mode === 'LOCKED') {
|
|
||||||
targetMix = handCount > 0 ? 0.6 : 0.0;
|
|
||||||
const ns = 0.002 * this.diffusionFactor;
|
|
||||||
const ts = adjustedTime * 0.15;
|
|
||||||
|
|
||||||
for (let i = 0; i < this.particleCount; i++) {
|
|
||||||
const i3 = i * 3;
|
|
||||||
const ox = this.origin[i3] * this.scaleFactor;
|
|
||||||
const oy = this.origin[i3 + 1] * this.scaleFactor;
|
|
||||||
const oz = this.origin[i3 + 2] * this.scaleFactor;
|
|
||||||
const noise = this.simplex.noise3D(ox * ns + ts, oy * ns, oz * ns + ts);
|
|
||||||
|
|
||||||
let offX = 0, offY = 0;
|
|
||||||
if (handCount === 1) {
|
|
||||||
offX = handL.x * 0.15 * this.forwardFactor;
|
|
||||||
offY = handL.y * 0.15 * this.forwardFactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scale = 1 + noise * 0.3;
|
|
||||||
this.targets[i3] = ox * scale + offX;
|
|
||||||
this.targets[i3 + 1] = oy * scale + offY;
|
|
||||||
this.targets[i3 + 2] = oz * scale;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
targetMix = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.material.uniforms.mixVal.value += (targetMix - this.material.uniforms.mixVal.value) * 0.1;
|
|
||||||
|
|
||||||
for (let i = 0; i < this.particleCount; i++) {
|
|
||||||
const i3 = i * 3;
|
|
||||||
const px = this.positions[i3];
|
|
||||||
const py = this.positions[i3 + 1];
|
|
||||||
const pz = this.positions[i3 + 2];
|
|
||||||
|
|
||||||
const stiff = mode === 'LOCKED' ? 0.03 : 0.05;
|
|
||||||
this.velocities[i3] += (this.targets[i3] - px) * stiff;
|
|
||||||
this.velocities[i3 + 1] += (this.targets[i3 + 1] - py) * stiff;
|
|
||||||
this.velocities[i3 + 2] += (this.targets[i3 + 2] - pz) * stiff;
|
|
||||||
|
|
||||||
if (handCount === 1) {
|
|
||||||
const hx = handL.x;
|
|
||||||
const hy = handL.y;
|
|
||||||
const dx = hx - px;
|
|
||||||
const dy = hy - py;
|
|
||||||
const distSq = dx * dx + dy * dy;
|
|
||||||
|
|
||||||
if (distSq < 150000) {
|
|
||||||
const f = (150000 - distSq) / 150000;
|
|
||||||
this.velocities[i3] += dx * f * 0.05;
|
|
||||||
this.velocities[i3 + 1] += dy * f * 0.05;
|
|
||||||
this.velocities[i3 + 2] += Math.sin(adjustedTime * 10 + distSq * 0.0001) * 8 * f;
|
|
||||||
}
|
|
||||||
} else if (handCount === 2) {
|
|
||||||
[handL, handR].forEach(h => {
|
|
||||||
const dx = px - h.x;
|
|
||||||
const dy = py - h.y;
|
|
||||||
const distSq = dx * dx + dy * dy;
|
|
||||||
if (distSq < 80000) {
|
|
||||||
const f = (80000 - distSq) / 80000;
|
|
||||||
this.velocities[i3] -= dx * f * 0.3;
|
|
||||||
this.velocities[i3 + 1] -= dy * f * 0.3;
|
|
||||||
this.velocities[i3 + 2] += 15 * f;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.velocities[i3] *= 0.90;
|
|
||||||
this.velocities[i3 + 1] *= 0.90;
|
|
||||||
this.velocities[i3 + 2] *= 0.90;
|
|
||||||
|
|
||||||
this.positions[i3] += this.velocities[i3];
|
|
||||||
this.positions[i3 + 1] += this.velocities[i3 + 1];
|
|
||||||
this.positions[i3 + 2] += this.velocities[i3 + 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.geometry.attributes.position.needsUpdate = true;
|
|
||||||
|
|
||||||
if (this.callbacks.onUpdate) {
|
|
||||||
this.callbacks.onUpdate(this.particleSystem);
|
|
||||||
}
|
|
||||||
|
|
||||||
return targetMix;
|
|
||||||
}
|
|
||||||
|
|
||||||
explode(force) {
|
|
||||||
for (let i = 0; i < this.particleCount; i++) {
|
|
||||||
this.velocities[i * 3] += (Math.random() - 0.5) * force;
|
|
||||||
this.velocities[i * 3 + 1] += (Math.random() - 0.5) * force;
|
|
||||||
this.velocities[i * 3 + 2] += (Math.random() - 0.5) * force;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scatter() {
|
|
||||||
// 将粒子散开到随机位置
|
|
||||||
for (let i = 0; i < this.particleCount; i++) {
|
|
||||||
const i3 = i * 3;
|
|
||||||
this.targets[i3] = (Math.random() - 0.5) * 2000;
|
|
||||||
this.targets[i3 + 1] = (Math.random() - 0.5) * 2000;
|
|
||||||
this.targets[i3 + 2] = (Math.random() - 0.5) * 2000;
|
|
||||||
}
|
|
||||||
this.animationState = 'scattered';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 聚合粒子形成土星形状
|
|
||||||
aggregate() {
|
|
||||||
this.initSaturnParticles();
|
|
||||||
this.animationState = 'aggregated';
|
|
||||||
}
|
|
||||||
|
|
||||||
setScaleFactor(factor) {
|
|
||||||
this.scaleFactor = Math.max(0.1, Math.min(2.0, factor));
|
|
||||||
}
|
|
||||||
|
|
||||||
setDiffusionFactor(factor) {
|
|
||||||
this.diffusionFactor = Math.max(0.1, Math.min(3.0, factor));
|
|
||||||
}
|
|
||||||
|
|
||||||
setForwardFactor(factor) {
|
|
||||||
this.forwardFactor = Math.max(-2.0, Math.min(2.0, factor));
|
|
||||||
}
|
|
||||||
|
|
||||||
setSpeedFactor(factor) {
|
|
||||||
this.speedFactor = Math.max(0.01, Math.min(3.0, factor));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理来自手势的指令
|
|
||||||
handleGestureCommand(command, value) {
|
|
||||||
switch(command) {
|
|
||||||
case 'scale':
|
|
||||||
this.setScaleFactor(value);
|
|
||||||
break;
|
|
||||||
case 'diffusion':
|
|
||||||
this.setDiffusionFactor(value);
|
|
||||||
break;
|
|
||||||
case 'forward':
|
|
||||||
this.setForwardFactor(value);
|
|
||||||
break;
|
|
||||||
case 'speed':
|
|
||||||
this.setSpeedFactor(value);
|
|
||||||
break;
|
|
||||||
case 'explode':
|
|
||||||
this.explode(value);
|
|
||||||
break;
|
|
||||||
case 'scatter':
|
|
||||||
this.scatter();
|
|
||||||
break;
|
|
||||||
case 'aggregate':
|
|
||||||
this.aggregate();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
this.geometry.dispose();
|
|
||||||
this.material.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
215
js/StarrySky.js
215
js/StarrySky.js
@@ -1,215 +0,0 @@
|
|||||||
/**
|
|
||||||
* Starry Sky
|
|
||||||
*
|
|
||||||
* 作者: DoWake
|
|
||||||
* 描述:使用Canvas绘制星空
|
|
||||||
* 地址:https://github.com/DoWake/StarrySky
|
|
||||||
* 日期:2023/03/02
|
|
||||||
*/
|
|
||||||
|
|
||||||
const StarrySky = function () {
|
|
||||||
//Canvas元素
|
|
||||||
let canvasElement;
|
|
||||||
//Canvas 2D对象
|
|
||||||
let canvasContext;
|
|
||||||
//Canvas 宽度
|
|
||||||
let canvasWidth;
|
|
||||||
//Canvas 高度
|
|
||||||
let canvasHeight;
|
|
||||||
//星星列表
|
|
||||||
let starList;
|
|
||||||
//星星颜色列表,rgb格式:"255, 255, 255"
|
|
||||||
let starColorList;
|
|
||||||
//星星半径大小
|
|
||||||
let starRadius;
|
|
||||||
//焦距等级,与canvasWidth相乘,必须大于0
|
|
||||||
let focalDistanceLevel;
|
|
||||||
//星星数量等级,与canvasWidth相乘,必须大于0
|
|
||||||
let starCountLevel;
|
|
||||||
//星星速度等级,与焦距相乘,必须大于0
|
|
||||||
let starSpeedLevel;
|
|
||||||
//焦距
|
|
||||||
let focalDistance;
|
|
||||||
//星星数量
|
|
||||||
let starCount;
|
|
||||||
//执行动画
|
|
||||||
let rAF;
|
|
||||||
return {
|
|
||||||
//初始化
|
|
||||||
init: function (canvas_element) {
|
|
||||||
if (canvas_element && canvas_element.nodeName === "CANVAS") {
|
|
||||||
canvasElement = canvas_element;
|
|
||||||
canvasElement.width = canvasElement.clientWidth;
|
|
||||||
canvasElement.height = canvasElement.clientHeight;
|
|
||||||
canvasElement.style.backgroundColor = "black";
|
|
||||||
canvasContext = canvasElement.getContext("2d");
|
|
||||||
canvasWidth = canvasElement.clientWidth;
|
|
||||||
canvasHeight = canvasElement.clientHeight;
|
|
||||||
starColorList = ["255, 255, 255"];
|
|
||||||
starRadius = 1;
|
|
||||||
focalDistanceLevel = 0.4;
|
|
||||||
starCountLevel = 0.2;
|
|
||||||
starSpeedLevel = 0.0005;
|
|
||||||
focalDistance = canvasWidth * focalDistanceLevel;
|
|
||||||
starCount = Math.ceil(canvasWidth * starCountLevel);
|
|
||||||
starList = [];
|
|
||||||
for (let i = 0; i < starCount; i++) {
|
|
||||||
starList[i] = {
|
|
||||||
x: canvasWidth * (0.1 + 0.8 * Math.random()),
|
|
||||||
y: canvasHeight * (0.1 + 0.8 * Math.random()),
|
|
||||||
z: focalDistance * Math.random(),
|
|
||||||
color: starColorList[Math.ceil(Math.random() * 1000) % starColorList.length]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const self = this;
|
|
||||||
window.addEventListener("resize", self.throttle(function () {
|
|
||||||
canvasElement.width = canvasElement.clientWidth;
|
|
||||||
canvasElement.height = canvasElement.clientHeight;
|
|
||||||
canvasWidth = canvasElement.clientWidth;
|
|
||||||
canvasHeight = canvasElement.clientHeight;
|
|
||||||
focalDistance = canvasWidth * focalDistanceLevel;
|
|
||||||
|
|
||||||
const starCount2 = Math.ceil(canvasWidth * starCountLevel);
|
|
||||||
if (starCount > starCount2) {
|
|
||||||
starList.splice(starCount2);
|
|
||||||
} else {
|
|
||||||
let num = starCount2 - starCount;
|
|
||||||
while (num--) {
|
|
||||||
starList.push({
|
|
||||||
x: canvasWidth * (0.1 + 0.8 * Math.random()),
|
|
||||||
y: canvasHeight * (0.1 + 0.8 * Math.random()),
|
|
||||||
z: focalDistance * Math.random(),
|
|
||||||
color: starColorList[Math.ceil(Math.random() * 1000) % starColorList.length]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
starCount = Math.ceil(canvasWidth * starCountLevel);
|
|
||||||
}, 200), { passive: true });
|
|
||||||
} else {
|
|
||||||
console.error('初始化失败,必须传入Canvas元素');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//设置星空背景颜色
|
|
||||||
setSkyColor: function (sky_color = "black") {
|
|
||||||
canvasElement.style.backgroundColor = sky_color;
|
|
||||||
},
|
|
||||||
//设置星星半径大小
|
|
||||||
setStarRadius: function (star_radius = 1) {
|
|
||||||
starRadius = star_radius;
|
|
||||||
},
|
|
||||||
//设置焦距等级
|
|
||||||
setFocalDistanceLevel: function (focal_distance_level = 0.4) {
|
|
||||||
focalDistanceLevel = focal_distance_level;
|
|
||||||
focalDistance = canvasWidth * focalDistanceLevel
|
|
||||||
},
|
|
||||||
//设置星星数量等级
|
|
||||||
setStarCountLevel: function (star_count_level = 0.2) {
|
|
||||||
starCountLevel = star_count_level;
|
|
||||||
const starCount2 = Math.ceil(canvasWidth * starCountLevel);
|
|
||||||
if (starCount > starCount2) {
|
|
||||||
starList.splice(starCount2);
|
|
||||||
} else {
|
|
||||||
let num = starCount2 - starCount;
|
|
||||||
while (num--) {
|
|
||||||
starList.push({
|
|
||||||
x: canvasWidth * (0.1 + 0.8 * Math.random()),
|
|
||||||
y: canvasHeight * (0.1 + 0.8 * Math.random()),
|
|
||||||
z: focalDistance * Math.random(),
|
|
||||||
color: starColorList[Math.ceil(Math.random() * 1000) % starColorList.length]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
starCount = Math.ceil(canvasWidth * starCountLevel);
|
|
||||||
},
|
|
||||||
//设置星星速度等级
|
|
||||||
setStarSpeedLevel: function (star_speed_level = 0.0005) {
|
|
||||||
starSpeedLevel = star_speed_level
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 设置星星颜色
|
|
||||||
* @param {Array|String} color 星星颜色
|
|
||||||
* @param {Boolean} mode 是否立刻同步颜色
|
|
||||||
*/
|
|
||||||
setStarColorList: function (color, mode = false) {
|
|
||||||
if (typeof color === 'object') {
|
|
||||||
starColorList = color;
|
|
||||||
} else if (typeof color === 'string') {
|
|
||||||
starColorList.push(color);
|
|
||||||
}
|
|
||||||
if (mode) {
|
|
||||||
for (let i = 0; i < starList.length; i++) {
|
|
||||||
starList[i]["color"] = starColorList[Math.ceil(Math.random() * 1000) % starColorList.length];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//渲染
|
|
||||||
render: function () {
|
|
||||||
const starSpeed = canvasWidth * focalDistanceLevel * starSpeedLevel;
|
|
||||||
//清空画布
|
|
||||||
canvasContext.clearRect(0, 0, canvasWidth, canvasHeight);
|
|
||||||
//计算位置
|
|
||||||
for (let i = 0; i < starList.length; i++) {
|
|
||||||
const star = starList[i];
|
|
||||||
const star_x = (star["x"] - canvasWidth / 2) * (focalDistance / star["z"]) + canvasWidth / 2;
|
|
||||||
const star_y = (star["y"] - canvasHeight / 2) * (focalDistance / star["z"]) + canvasHeight / 2;
|
|
||||||
star["z"] -= starSpeed;
|
|
||||||
if (star["z"] > 0 && star["z"] <= focalDistance && star_x >= -20 && star_x <= canvasWidth + 20 && star_y >= -20 && star_y <= canvasHeight + 20) {
|
|
||||||
const star_radius = starRadius * (focalDistance / star["z"] * 0.8);
|
|
||||||
const star_opacity = 1 - 0.8 * (star["z"] / focalDistance);
|
|
||||||
canvasContext.fillStyle = "rgba(" + star["color"] + ", " + star_opacity + ")";
|
|
||||||
canvasContext.shadowOffsetX = 0;
|
|
||||||
canvasContext.shadowOffsetY = 0;
|
|
||||||
canvasContext.shadowColor = "rgb(" + star["color"] + ")";
|
|
||||||
canvasContext.shadowBlur = 5;
|
|
||||||
canvasContext.beginPath();
|
|
||||||
canvasContext.arc(star_x, star_y, star_radius, 0, 2 * Math.PI);
|
|
||||||
canvasContext.fill();
|
|
||||||
} else {
|
|
||||||
const z = focalDistance * Math.random();
|
|
||||||
star["x"] = canvasWidth * (0.1 + 0.8 * Math.random());
|
|
||||||
star["y"] = canvasHeight * (0.1 + 0.8 * Math.random());
|
|
||||||
star["z"] = z;
|
|
||||||
star["color"] = starColorList[Math.ceil(Math.random() * 1000) % starColorList.length];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const self = this;
|
|
||||||
rAF = window.requestAnimationFrame(function () {
|
|
||||||
self.render();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
//销毁
|
|
||||||
destroy: function () {
|
|
||||||
window.cancelAnimationFrame(rAF);
|
|
||||||
starList = [];
|
|
||||||
canvasContext.clearRect(0, 0, canvasWidth, canvasHeight);
|
|
||||||
canvasElement.width = 0;
|
|
||||||
canvasElement.height = 0;
|
|
||||||
},
|
|
||||||
//防抖
|
|
||||||
debounce: function (func, time = 200) {
|
|
||||||
let timeId;
|
|
||||||
return function () {
|
|
||||||
if (timeId) {
|
|
||||||
clearTimeout(timeId);
|
|
||||||
}
|
|
||||||
timeId = setTimeout(function () {
|
|
||||||
func();
|
|
||||||
}, time);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//节流
|
|
||||||
throttle: function (func, time = 200) {
|
|
||||||
let timeId = null;
|
|
||||||
let pre = 0;
|
|
||||||
return function () {
|
|
||||||
if (Date.now() - pre > time) {
|
|
||||||
clearTimeout(timeId);
|
|
||||||
pre = Date.now();
|
|
||||||
func();
|
|
||||||
} else {
|
|
||||||
timeId = setTimeout(func, time);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
$(document).ready(function () {
|
|
||||||
const iframe = document.getElementById('moment-frame');
|
|
||||||
const momentsContainer = document.getElementById('moments-container');
|
|
||||||
|
|
||||||
function animateIframe() {
|
|
||||||
momentsContainer.style.display = 'block';
|
|
||||||
momentsContainer.classList.add('visible');
|
|
||||||
}
|
|
||||||
|
|
||||||
function openMoments(url) {
|
|
||||||
if (iframe.src == null || iframe.src === '' || iframe.src !== url) {
|
|
||||||
iframe.src = url;
|
|
||||||
|
|
||||||
iframe.onload = () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
animateIframe();
|
|
||||||
}, 300); // 延迟更自然
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
animateIframe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理瞬间链接点击事件
|
|
||||||
$('.moments-link').on('click', function (e) {
|
|
||||||
e.preventDefault(); // 阻止默认跳转
|
|
||||||
|
|
||||||
// 获取链接地址
|
|
||||||
const url = "https://moments.hehouhui.cn";
|
|
||||||
|
|
||||||
// 判断是否是移动端
|
|
||||||
const isMobile = /iPhone|Android/i.test(navigator.userAgent);
|
|
||||||
|
|
||||||
if (isMobile) {
|
|
||||||
// 移动端:直接跳转
|
|
||||||
window.location.href = url;
|
|
||||||
} else {
|
|
||||||
// PC端:在iframe中显示
|
|
||||||
openMoments(url);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 关闭按钮点击事件
|
|
||||||
$('.close-btn').on('click', function () {
|
|
||||||
momentsContainer.classList.remove('visible');
|
|
||||||
setTimeout(() => {
|
|
||||||
momentsContainer.style.display = 'none';
|
|
||||||
}, 500); // 等待动画结束后隐藏
|
|
||||||
});
|
|
||||||
|
|
||||||
// 遮罩层点击事件 点击空白处关闭模拟器
|
|
||||||
$('.overlay').on('click', function () {
|
|
||||||
momentsContainer.classList.remove('visible');
|
|
||||||
setTimeout(() => {
|
|
||||||
momentsContainer.style.display = 'none';
|
|
||||||
}, 500); // 等待动画结束后隐藏
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user