85 Commits

Author SHA1 Message Date
hehh
a0e911421f feat(tracking): 集成新的统计追踪服务
- 在 about.html 中添加 vercount 统计脚本
- 在 christmas.html 中添加 vercount 统计脚本
- 在 index.html 中替换原有的分析服务为 vercount 统计脚本
- 在 me.html 中添加 vercount 统计脚本
- 移除旧的 analysee 统计服务相关代码
- 统一使用 defer 属性优化脚本加载性能
2026-01-10 18:28:17 +08:00
hehh
d0091fc713 config(edgeone): 添加重写规则配置
- 配置 .well-known 目录下 txt 文件的路径重写规则
- 将 /.well-known/*.txt 重定向到 /well-known/:splat.txt
- 解决 .well-known 目录文件访问问题
2026-01-10 18:00:07 +08:00
hehh
dac607472f feat(ui): 优化音频控制和提示文字交互体验
- 将音频控制按钮改为固定定位并增强悬停效果
- 重新设计提示文字样式,支持响应式和居中显示
- 添加提示文字点击事件以切换控制面板
- 设置 canvas 元素不拦截鼠标事件
- 更新提示文字内容,增强用户引导
- 增加控制面板显示状态的日志输出
2025-12-13 01:15:50 +08:00
hehh
10d73b33e1 feat(christmas): 增强粒子效果和音频状态管理
- 将粒子数量从1000增加到2000,尘埃粒子从2000增加到3000
- 修改 MediaPipe WASM 路径为本地 data/wasm 目录
- 实现音频状态的持久化存储和恢复功能
- 添加定时保存音频状态逻辑,每5秒保存一次
- 增强音频播放控制,确保循环播放和自动恢复
- 优化音频事件监听器,实时同步播放状态
2025-12-13 01:01:09 +08:00
hehh
19e9801318 feat(audio): 优化背景音乐播放控制
- 实现音频状态的本地存储与恢复功能
- 添加定期自动保存播放状态机制
- 支持页面加载时恢复上次播放位置
- 增强自动播放逻辑,提升用户体验
- 监听多种用户交互事件触发音频播放
- 添加音频播放结束后的自动重播功能
- 优化静音与播放状态切换逻辑
- 提升移动端触摸事件对音频控制的支持
2025-12-12 20:16:38 +08:00
hehh
b424f898db feat(gesture): 优化移动端手势识别与控制交互
- 调整移动端手势识别阈值,提升识别准确率
- 增加移动端专属手势指令:左右滑动控制前后移动
- 优化手势影响范围计算逻辑,增强触摸屏设备体验
- 完善摄像头权限检查机制,改善用户授权流程
- 支持通过点击提示文字唤起控制台界面
- 统一控制台显示/隐藏逻辑,提高代码可维护性
- 调整响应式布局参数,适配更多设备屏幕尺寸
2025-12-12 20:15:56 +08:00
hehh
ad744a0690 feat(christmas): 优化粒子动画以适配不同屏幕尺寸
- 根据屏幕宽度动态调整散射模式下粒子的分布范围
- 调整焦点照片的位置和距离以适应小屏幕设备
- 根据屏幕尺寸改变噪声强度,使动画更自然
- 优化手势识别的影响范围和灵敏度,提升触摸体验
- 调整插值速度和缩放比例,确保动画在各种设备上流畅运行
- 重构窗口大小变化处理函数,提高代码可维护性
2025-12-12 18:20:42 +08:00
hehh
78329785a7 feat(responsive): 优化多设备响应式设计
- 添加超小设备 (小于 480px) 的样式适配
- 完善小设备 (480px 到 768px) 的布局调整
- 新增平板设备 (769px 到 1024px) 的显示优化
- 补充大屏幕设备 (大于 1024px) 的视觉增强
- 统一各设备尺寸下的控件尺寸与间距标准
- 移除加载文字中的英文提示 (Loading Holiday Magic)
2025-12-12 18:17:51 +08:00
hehh
3fb3739666 feat(christmas.html): 增强页面 SEO 与社交分享功能
- 添加 favicon 和苹果触屏图标支持
- 补充 Open Graph 与 Twitter Card 元数据
- 增加微信分享标题、描述与图片配置
- 替换 Google Fonts 为国内加速镜像链接
- 优化背景色至更舒适的极深午夜蓝色
- 新增手势操作引导说明区域
- 实现音频自动播放及用户交互触发逻辑
- 添加点击与键盘事件以改善播放体验
2025-12-12 18:12:32 +08:00
hehh
28013c438f feat(christmas): 实现3D圣诞树互动页面
- 添加上传照片功能,用户可添加个人回忆到3D场景
- 集成MediaPipe手势识别,支持多种手势控制粒子动画
- 优化粒子系统,实现更自然的运动和交互反馈
- 设计奢华金色主题UI,增强节日氛围视觉效果
- 实现响应式布局,适配移动端和桌面端体验
- 添加背景音乐和音量控制功能
- 重构动画逻辑,提高手势响应速度和流畅度
- 优化Three.js渲染性能,提升整体运行效率
2025-12-12 18:03:50 +08:00
hehh
785bf0fe61 perf(christmas): 优化圣诞树页面性能
- 减少CSS代码行数,合并重复样式声明
- 降低粒子系统中几何体的分段数以减少内存占用
- 减少粒子和尘埃粒子的数量以提升渲染性能
- 添加硬件加速样式以提高动画流畅度
- 优化WebGL渲染器配置,限制像素比率
- 调整后期处理效果参数以平衡视觉效果与性能
- 优化动画插值计算,防止过度计算
- 在窗口大小调整事件中添加防抖动处理
- 限制动画帧时间差最大值以稳定动画表现
- 添加手势状态变化检测以减少不必要的状态切换
2025-12-12 17:05:25 +08:00
hehh
cada354dbe feat(christmas.html): 添加背景音乐和不蒜子统计功能
- 格式化HTML代码,提升可读性
- 添加背景音乐播放功能,支持自动播放和用户交互播放
- 集成不蒜子网站统计脚本
- 优化粒子系统和3D渲染效果
- 改进手部识别交互逻辑
- 更新默认图片文字内容
- 添加音频播放错误处理机制
- 优化CSS样式结构和动画效果
2025-12-12 16:56:38 +08:00
hehh
b5974a4407 feat(christmas): 添加圣诞主题3D互动页面
- 创建了基于Three.js的3D圣诞树场景
- 实现了粒子系统,包括装饰球、礼盒和彩带
- 添加了照片上传功能,支持用户自定义装饰
- 集成了手势识别,可通过手势控制视角和模式
- 设计了多种显示模式:散落、树形和聚焦视图
- 添加了后期处理效果,增强视觉体验
- 实现了响应式设计,适配不同屏幕尺寸
- 添加了节日加载动画和UI界面
- 配置了SEO和社交平台分享元数据
2025-12-12 16:47:58 +08:00
hehh
a7cc299695 feat(about): 添加导航项的无障碍标签并优化二维码加载逻辑
- 为首页和博客导航链接增加 aria-label 属性以提升无障碍访问性
- 移除微信公众号模态框中失效的二维码降级提示注释
- 删除冗余的 moments.js 和粒子系统相关脚本文件内容
2025-12-12 16:28:29 +08:00
hehh
6b38d01dd3 refactor(about): 移除内联关键CSS并优化页面结构
- 删除了 about.html 中的内联关键 CSS 样式代码
- 保留了主题变量和基础样式的定义逻辑
- 简化了主容器的媒体查询配置
- 调整了页面底部链接与脚本加载顺序
- 优化了整体HTML结构以提升可维护性
2025-12-12 16:22:32 +08:00
hehh
503ae0c273 feat(physics): 重构粒子物理系统并增强手势交互
- 重写物理核心为基于力的系统,支持更自然的粒子流动
- 引入旋度噪声(Curl Noise)实现流体感运动
- 添加弹性回归力使粒子可自动飘回原位
- 实现高级手势交互场,支持任意数量手部追踪
- 增加掌心力场与指尖轨迹交互逻辑
- 优化粒子着色器,提升视觉表现与性能
- 改进主题系统与颜色配置结构
- 更新UI布局与加载动画效果
-
2025-12-12 16:19:05 +08:00
hehh
75231ee73a refactor(ui): 重构界面与粒子系统提升视觉体验
- 更新标题为 FLUID ARCHIVE 并调整字体与背景色
- 简化 UI 布局,增强沉浸感并隐藏鼠标指针
- 优化加载动画为旋转圆环,改善过渡效果
- 粒子系统重写:减少数量、引入着色器材质与辉光效果
- 改进粒子物理行为,支持跟随、聚合与自由漂浮模式
- 更新主题配置逻辑,简化节气与节日判断规则
- 修复手势控制响应延迟与丢失问题,增强交互反馈
- 调整摄像机视角与后处理参数以增强空间氛围感
2025-12-12 12:27:36 +08:00
hehh
31b7d72123 refactor(ui): 重构界面布局与主题系统
- 移除旧版加载屏与叙事层DOM结构
- 新增基于Three.js的粒子系统引擎
- 实现动态主题检测与切换功能
- 更新CSS变量系统以支持RGB颜色模式
- 优化UI层布局结构提升响应式体验
- 添加节庆主题自动识别逻辑
- 简化HTML结构并增强语义化程度
- 调整字体与色彩配置提高可读性
- 引入新的动画呼吸效果与脉冲效果
- 重构JavaScript模块提升代码组织性
2025-12-12 12:27:20 +08:00
hehh
2b6d363aba refactor(config): 重构站点配置加载逻辑
- 将SiteConfig封装为IIFE避免全局污染
- 移除about.html中的内联SiteConfig定义
- 统一通过js/config.js管理所有配置项
- 修复统计脚本加载时对SiteConfig的依赖问题
- 确保配置在模块化和浏览器环境中正常导出
- 优化不蒜子统计ID获取方式提升容错性
2025-12-12 12:06:49 +08:00
hehh
e47c5c2803 perf(about): 优化页面加载性能与结构
- 将外部CSS资源改为preload方式异步加载
- 内联关键首屏CSS样式减少渲染阻塞
- 添加图片尺寸属性提升渲染性能
- 内联关键JS配置提升初始化速度
- 为脚本标签添加defer属性优化加载顺序
- 增加noscript回退方案确保无JS兼容性
2025-12-12 11:57:03 +08:00
hehh
a98496c232 feat(particles): 添加粒子系统类以支持土星动画和交互控制
- 创建 ParticleSystem 类,支持多种粒子动画效果
- 实现土星主体和光环的粒子分布初始化逻辑
- 添加粒子材质和着色器定义,支持日夜主题切换
- 实现粒子位置更新逻辑,包括噪声扰动和手势交互
- 添加粒子爆炸、散开和聚合等动画控制方法
- 支持通过手势命令动态调整动画参数
- 提供粒子系统资源释放接口
2025-12-12 11:42:54 +08:00
hehh
1ed730a3d2 feat(me): 重构粒子系统与动画加载逻辑
- 引入独立的ParticleSystem类管理粒子系统
- 添加土星动画作为默认加载动画
- 实现动态动画切换机制,避免资源冲突
- 优化粒子爆炸与散射效果调用方式
- 移除旧版粒子初始化与物理计算代码
- 更新手势交互与UI状态同步逻辑
- 修复动画模式下按钮显示时机问题
2025-12-10 00:04:26 +08:00
hehh
cff4db87af feat(me): 调整倒计时逻辑和界面显示时机
- 将倒计时初始值从变量替换为固定字符串 '5'
- 移除 explode 函数调用
- 延迟显示进入选项和启动倒计时逻辑
- 调整倒计时间隔从 980ms 到 1000ms
- 将倒计时相关代码包裹在 setTimeout 中延迟执行
2025-12-04 22:17:08 +08:00
hehh
dd837153b8 fix(animation): 调整动画延迟时间
- 将动画延迟从1000毫秒调整为980毫秒
- 优化动画启动时机以提升用户体验
2025-12-04 21:50:58 +08:00
hehh
b9461c5741 feat(me): 动态化倒计时提示文本
- 将倒计时提示文本中的固定时间替换为动态占位符
- 使用 `{second}` 占位符实现多语言时间显示
- 更新中文和英文场景下的倒计时文本格式
- 优化倒计时逻辑以支持动态文本更新
- 确保语言切换时提示文本正确渲染
2025-12-04 21:50:01 +08:00
hehh
fef7ca2288 feat(me): 优化页面加载和摄像头权限处理逻辑
- 移除enterAnimationMode函数中的APP_STATE.isLoaded检查,确保动画模式始终可进入
- 改进加载屏幕隐藏逻辑,增加DOM存在性检查防止潜在错误
- 根据摄像头状态动态更新UI提示信息
- 增加摄像头权限检查机制,在页面加载时自动检测并启用摄像头
- 实现延迟显示交互模式按钮逻辑,确保页面过渡完成后再显示
- 调整资源加载完成后的进入按钮显示逻辑,移除不必要的条件判断
- 优化倒计时文本显示,支持中英文切换
- 统一页面加载完成后的提示信息更新逻辑
2025-12-04 21:41:26 +08:00
hehh
5268f35af1 feat(ui): 优化加载界面动画效果
- 将原有的旋转圆环加载动画替换为三点脉冲动画
- 调整加载文字字号从14px增大到16px,提高可读性
- 重构HTML结构,新增loading-container包裹元素
- 修改状态文本样式,增加字体粗细和间距调整
- 删除旧的spin关键帧动画定义
- 更新JavaScript轮播文案逻辑,使用取模运算循环显示提示语
- 增加APP_STATE.namasteStableFrames变量重置逻辑
- 调整加载文案切换间隔时间从600ms延长至800ms
2025-12-04 21:19:28 +08:00
hehh
978b618df2 refactor(config): 统一缓存键命名并优化主题设置逻辑
- 将缓存键名从带版本后缀的形式统一为无版本后缀
- 更新主题设置与读取逻辑,使用配置中心的缓存键定义
- 修正语言检测时对中文标识的判断逻辑
- 优化页面脚本加载顺序,确保配置文件优先加载
- 调整主题显示文本的中英文切换条件判断表达式
2025-12-04 19:30:57 +08:00
hehh
0174d29bde feat(ui): 重构进入界面与交互模式按钮
- 重新设计进入界面的按钮和倒计时提示样式
- 新增交互模式按钮,支持动态显示和隐藏
- 修改默认进入方式为动画模式而非叙事模式
- 调整摄像头权限申请逻辑,改为用户主动点击触发
- 优化加载文案循环播放逻辑
- 更新多语言字典中的相关提示文本
- 改进页面加载完成后不自动启动摄像头的处理方式
2025-12-04 19:25:20 +08:00
hehh
7b9d343cf9 fix(me): 调整无摄像头进入逻辑与定时器设置
- 将自动进入延迟从5秒缩短至3秒
- 修改进入条件检查,使用 isLoaded 替代 mode 判断
- 移除粒子爆炸效果和叙事模式启动
- 清除加载文案定时器以避免冲突
- 更新点击事件中的状态判断逻辑
2025-12-04 18:55:17 +08:00
hehh
a2b627b4ed feat(me): 添加无摄像头权限时的直接进入功能
- 新增直接进入按钮和倒计时提示界面
- 实现5秒后自动进入档案馆逻辑
- 添加摄像头权限状态管理和重试机制
- 支持点击按钮立即跳过摄像头授权
- 更新多语言字典中的相关提示文本
- 调整粒子爆炸效果参数提升视觉体验
- 优化手势识别逻辑仅在摄像头启用时运行
- 添加重新申请摄像头权限的交互入口
2025-12-04 18:52:09 +08:00
hehh
0f6504a46b perf(me.html): 优化DOM访问以提升页面性能
- 引入DOM_CACHE对象缓存频繁访问的DOM元素
- 修改safeUpdateText和safeClass函数以接受DOM元素而非ID
- 替换所有document.getElementById调用为DOM_CACHE中的缓存引用
- 减少重复DOM查询,提高渲染和交互性能
2025-12-04 18:27:47 +08:00
hehh
5ced418d1a style(me.html): 优化CSS样式和布局结构
- 统一CSS属性书写格式,增加空格分隔rgba值
- 重构多行CSS属性,提高可读性
- 调整HTML元素间距和布局结构
- 优化JavaScript函数格式和变量命名
- 更新加载文案和界面提示文本
- 改进粒子系统动画参数配置
- 优化手势识别逻辑和UI交互反馈
2025-12-04 18:22:28 +08:00
hehh
67049a126f feat(me): 全面升级个人主页视觉与交互体验
- 引入昼夜模式下的动态渐变标题与光晕背景效果
- 新增极光动画层,提升页面沉浸感与视觉层次
- 优化粒子系统着色器,支持主题色彩漂移与星云渲染
- 改进叙事文本层样式,增强文字可读性与动效表现
- 调整双手合十解锁逻辑,提高手势识别准确率与稳定性
- 更新多语言文案内容,强化品牌表达与情感连接
- 增加退出冷却机制,避免误触并改善用户体验流畅度
- 统一动画曲线与过渡时长,确保界面响应自然和谐
2025-12-04 16:36:50 +08:00
hehh
c21d276f40 feat(ui): 重构加载与叙事层,优化手势交互体验
- 重构智能加载层结构与样式,提升初始化体验
- 优化白天/黑夜主题配色与粒子渲染效果
- 增强手势识别逻辑与视觉反馈
- 改进UI层DOM结构与类名语义化
- 更新内容字典与加载文案
- 修复安全DOM操作与空指针问题
- 调整物理引擎参数,增强交互手感
- 优化粒子爆炸与散开动画效果
- 统一状态管理对象,提高代码可维护性
- 增加权限提示与超时处理机制
2025-12-04 16:02:55 +08:00
hehh
e183f8bf63 feat(me): 实现昼夜主题切换与智能叙事系统
- 新增昼夜模式自动切换功能,根据时间和系统偏好设置主题
- 优化粒子系统,支持不同主题下的视觉效果差异
- 重构UI层结构,增强交互提示与视觉反馈
- 添加智能AI加载动画与科技感加载页
- 实现手势交互物理引擎,支持单手磁流体牵引与双手斥力场
- 增加多语言支持(中/英),动态内容切换
- 优化Three.js渲染配置,提升性能与视觉表现
- 添加叙事文本动画与过渡效果
- 改进着色器材质,支持颜色渐变插值
- 实现主题缓存机制,提高用户体验一致性
2025-12-04 14:26:51 +08:00
hehh
3031355836 feat(webgl): 重构粒子系统与手势交互引擎
- 重写粒子物理系统,支持速度与目标点分离的Verlet积分
- 新增辉光后期处理(UnrealBloomPass)增强视觉表现
- 优化手势识别引擎,提高捏合与滑动手势精度
- 修复摄像头画面镜像问题,改善交互准确性
- 增加叙事模式自动播放与手动切换功能
- 改进UI布局与加载动画,提升用户体验
- 调整粒子形态生成算法,新增黑洞、螺旋等预设
- 优化移动端渲染性能,限制高密度屏幕采样率
2025-12-04 00:32:11 +08:00
hehh
0de81219bb feat(me): 初始化个人介绍页面并集成手势交互粒子系统
- 创建基于Three.js的全屏粒子动画背景
- 集成MediaPipe手势识别实现手部追踪
- 实现多种手势控制:Namaste解锁、Pinch扭曲、Swipe旋转
- 设计默认动画循环(云朵、晶格、流动等六种形态)
- 添加叙事模式展示个人信息与理念
- 内置Web Audio API生成环境音效与交互音效
- 构建HUD显示系统监控帧率、手势状态与实体数量
- 支持响应式布局适配移动端与桌面端体验
- 使用着色器材质确保粒子渲染清晰度与性能
- 程序化生成文本点阵用于信息可视化呈现
2025-12-04 00:28:28 +08:00
hehh
aca4d5a0de fix(css): 调整英文版统计键和标签的字体大小响应式样式
- 删除了针对英文版移动端统计键字体大小的旧样式规则
- 新增了针对平板尺寸范围内英文版统计键字体大小的媒体查询
- 添加了针对特定屏幕宽度内英文版 MBTI 标签字体大小的媒体查询
- 优化了多语言环境下响应式布局的表现一致性
2025-12-02 23:59:55 +08:00
hehh
2d52212ad1 fix(audio): 更新音频文件路径以移除压缩后缀
- 移除了音频文件名中的 "_compressed" 后缀
- 确保音频文件能被正确加载和播放
- 保持了原有的自动播放和循环属性
2025-12-01 13:02:06 +08:00
hehh
88e1d9e3d9 style(css): 更新技术标签颜色值
- 将 tech-tag-3d 和 tech-tag-mobile 的颜色从白色更改为紫色 (#d57eeb)
2025-12-01 13:01:34 +08:00
hehh
4fe9034fb9 feat(about): 添加推荐分享模块和社交链接样式优化
- 新增推荐分享模块,包含动画效果和夜间模式适配
- 优化社交图标样式,统一设计风格
- 调整移动端社交布局,提升用户体验
- 修复夜间模式下文字阴影显示问题
- 移除重复的桌面端社交样式定义
2025-11-30 22:05:46 +08:00
hehh
6272941f3e style(artalk): 移除评论区编辑器的玻璃态样式
- 删除 atk-main-editor 的背景和边框玻璃态效果
- 移除相关的 backdrop-filter 和模糊样式
- 简化评论区域的样式定义
2025-11-30 18:34:04 +08:00
hehh
67b7c0e5f9 feat(audio): 改进音频自动播放逻辑
- 在HTML中将音频preload属性从none改为auto
- 添加用户交互检测以提高自动播放成功率
- 使用setTimeout延迟播放尝试以绕过浏览器限制
- 移除不必要的console.error输出
- 优化CSS以确保评论头像正确显示
2025-11-30 18:27:31 +08:00
hehh
a871a734ee chore(about): 更新页脚版权信息显示
- 将页脚中的版权符号从"©"更改为"Copyright ©"
- 保持其他页脚内容和链接不变
- 确保年份动态更新功能继续正常工作
2025-11-30 16:42:45 +08:00
hehh
30e13e5bde feat(audio): 优化音频自动播放逻辑以适应浏览器策略
- 添加用户交互检查,避免浏览器阻止自动播放
- 实现一次性用户交互监听器,提升播放体验
- 保留requestAnimationFrame以确保播放时机准确
- 增加多种用户交互事件支持(点击、触摸、键盘、鼠标移动)
2025-11-30 16:13:15 +08:00
hehh
41184ad1d8 feat(about): 扩展渐变色板并优化技术标签样式
- 新增 15 种 CSS 渐变色变量(从 --gradient-11 到 --gradient-25)
- 为白天和夜间主题添加对应的新技术标签颜色类(.tag-color-11 到 .tag-color-25)
- 更新 JavaScript 中 gradientId 的取值范围从 10 扩展到 25
- 移除旧的控制台日志输出
- 调整配置文件中的空行格式,提升可读性
2025-11-30 16:02:49 +08:00
hehh
bff0b529d2 fix(audio): 修复音频自动播放问题
- 使用 requestAnimationFrame 包装音频播放逻辑
- 确保在用户交互后正确触发播放
- 保留原有的静默错误处理机制

style(artalk): 优化移动端评论样式

- 移除重复的 CSS 选择器定义
- 调整评论区域 padding 和字体大小
- 优化黑夜模式下评论背景透明度
- 修复移动端头像拉伸显示问题
- 移除夜间模式下不必要的边框和圆角
- 完全移除评论容器的边框和阴影样式
- 更新分页按钮的内边距和字体大小
- 修复移动端输入框背景色问题
2025-11-30 15:25:56 +08:00
hehh
b179431aaa feat(about): 优化技术标签云渲染与状态管理
- 引入 sessionStorage 缓存技术标签云状态,提升页面切换性能
- 区分移动端与桌面端渲染逻辑,实现响应式适配
- 抽离 3D 球体动画初始化方法,增强代码可维护性
- 添加窗口大小变化监听器,动态调整渲染内容
- 实现标签云生成与状态保存功能,避免重复计算
- 优化动画性能,使用 requestAnimationFrame 处理鼠标交互
2025-11-30 02:13:13 +08:00
hehh
569ad36db1 fix(css): 修复黑夜模式下插件面板和评论计数的样式问题
- 修复黑夜模式下插件面板背景色和文字颜色显示异常
- 调整插件面板边框、圆角和阴影效果
- 优化评论计数文字颜色、粗细和阴影效果
- 修复下拉菜单背景、边框和悬停状态样式
- 调整下拉菜单项的激活状态背景色
- 修复箭头图标在黑夜模式下的亮度问题
2025-11-30 00:07:51 +08:00
hehh
d0cd750e22 style(css): 调整玻璃态背景透明度并修复动画键帧格式
- 将玻璃态背景的不透明度从 0.85 调整为 0.55
- 修复 fadeIn 动画关键帧中的多余空格问题
- 确保动画过渡更加平滑自然
- 统一 CSS 样式代码格式以提高可维护性
- 优化加载过渡效果的视觉表现
- 移除不必要的样式声明以减少文件体积
2025-11-29 21:05:32 +08:00
hehh
ccda6074be feat(about): 增强 GitHub 数据获取与降级机制
- 为用户数据请求添加本地 JSON 降级方案
- 为仓库数据请求添加本地 JSON 降级方案
- 拆分用户与仓库数据获取逻辑,提高可维护性
- 增加对 AbortError 等异常情况的处理
- 移除全局异常处理中的默认配置兜底逻辑
- 优化超时设置,区分不同资源的加载时间限制
2025-11-29 21:02:32 +08:00
hehh
1b94b8bf93 feat(about): 优化夜间模式视觉效果和可访问性
- 提高玻璃态背景和文本的不透明度以增强可读性
- 调整夜间模式下评论区域的颜色和边框可见性
- 为功能按钮和链接添加tabindex属性提升键盘导航
- 增加多语言和SEO相关的meta标签及canonical链接
- 优化Artalk评论系统的主题切换逻辑和UI增强
- 更新移动端悬浮按钮和社交链接的无障碍标签
2025-11-29 20:52:30 +08:00
hehh
9a73273c34 feat(about): 添加 GitHub 数据的本地回退机制
- 在 GitHub API 请求失败时,从本地 JSON 文件获取用户数据
- 在 GitHub API 请求失败时,从本地 JSON 文件获取仓库数据
- 实现带超时的 fetch 方法以提高加载性能
- 优化错误处理逻辑,增强页面容错能力
2025-11-29 20:34:27 +08:00
hehh
ea04260290 feat(data): 更新 GitHub 仓库数据列表
- 添加 spring-ai-example 项目信息
- 更新 yunxiao-LLM-reviewer 项目描述与星标数
- 移除旧 hexo-theme-stellar 项目记录
- 添加 Home 个人主页项目信息
- 添加 collection-complete Java 库项目信息
- 添加 keycloak-services-social-weixin 项目信息
- 替换 Universal-IoT-Java 为 Notion-Wechat-Blog 项目
- 添加 social-auth 项目信息
- 添加 hp-lite 内网穿透工具项目信息
2025-11-29 20:30:53 +08:00
hehh
9c8a1f035f feat(about): 移除 Artalk.js 的异步加载属性
- 将 Artalk.js 的加载方式从异步改为同步
- 确保评论系统在页面加载时立即初始化
- 避免因异步加载导致的评论框渲染延迟问题
2025-11-29 12:21:58 +08:00
hehh
a4cd5d6535 feat(perf): 优化图片加载性能
- 为多个页面中的图片元素添加 loading="lazy" 属性
- 对 Artalk.js 脚本引入增加 async 属性以提升加载性能
- 减少初始页面加载时不必要的资源阻塞
- 提高用户访问速度与页面响应效率
2025-11-28 17:07:58 +08:00
hehh
c2585ea504 fix(audio): 修复页面加载时音频自动播放问题
- 移除音频元素的 autoplay 属性
- 将音频播放逻辑移至 window load 事件后执行
- 添加播放失败的静默错误处理
- 保留循环播放和预加载设置
- 确保用户暂停状态在24小时内被记住
- 优化音频播放时机以提升用户体验
2025-11-28 17:02:56 +08:00
hehh
3589df9032 chore(about): 更新页面元数据和音频资源引用
- 调整页面中多个区域间的空行格式
- 更新背景音乐文件名为压缩版本以优化加载
- 确保HTML标签正确闭合
2025-11-28 16:51:06 +08:00
hehh
99d5420024 chore(about): 移除冗余样式文件引用
- 删除了对 css/style.css 的重复引用
- 保留了 about 页面专用的 css/about.css 引用
- 确保样式加载顺序和依赖关系正确
2025-11-28 11:13:47 +08:00
hehh
f66c17095b refactor(css): 添加 contain 属性优化布局性能
- 在多个组件样式中添加 contain: layout style 提升渲染性能
- 移除移动端浮动按钮的拖拽功能实现
- 简化 JavaScript 中的 FAB 元素初始化逻辑
- 清理未使用的拖拽相关事件监听器绑定代码
2025-11-27 15:59:24 +08:00
hehh
86468a9f77 chore(site): 优化网站性能与样式细节
- 将 jQuery 引用从 BootCDN 改为本地文件以提升加载速度
- 移除页面 body 标签中的禁止右键和选择属性,提高用户体验
- 为事件监听器添加 passive 选项以提升滚动性能
- 调整 StarrySky.js 中 canvas 阴影模糊值以优化视觉效果
- 更新 CSS 样式颜色值,增强文字可读性与整体美观度
- 统一并优化部分文本阴影及字体抗锯齿设置
2025-11-27 15:49:12 +08:00
hehh
0c55a90662 refactor(about): 移除多语言包引入
- 删除了 artalk 多语言包的引入
- 简化了页面资源加载逻辑
- 减少了不必要的网络请求
2025-11-27 14:59:28 +08:00
hehh
0eac618a71 style(css): 优化夜间模式下的编辑器样式
- 调整夜间模式下工具栏按钮的颜色和亮度
- 修改夜间模式下输入框的背景透明度和边框颜色
- 增强夜间模式下输入框的阴影效果
- 提高夜间模式下输入框聚焦时的阴影透明度
2025-11-27 02:40:11 +08:00
hehh
200da80262 fix(main): 禁用自定义壁纸功能
- 注释掉从sessionStorage获取图片URL的代码
- 将imgUrls变量初始化为null
- 暂时禁用自定义壁纸相关逻辑
2025-11-27 02:30:29 +08:00
hehh
753f12e5d9 fix(about): 修复多语言支持中的区域设置问题
- 将默认英文区域设置从 'en-US' 更改为 'en'
- 统一日期本地化方法中的区域参数
- 确保中英文切换时的语言一致性
2025-11-27 02:24:46 +08:00
hehh
50fcc7454d feat(assets): 将图片资源从 JPG/PNG 更新为 WebP 格式
- 将 MBTI 图标从 PNG 更改为 WebP 格式以优化加载性能
- 更新背景图片路径配置以使用 WebP 图片资源
- 移除对 sessionStorage 中图片 URL 列表的重复设置逻辑
2025-11-27 02:19:45 +08:00
hehh
747021c259 feat(i18n): 支持通过URL参数设置语言
- 新增从URL查询参数中读取lang值的功能
- 支持lang参数为zh或en时设置并存储语言
- 保留原有的本地存储语言获取逻辑作为备用方案
- 添加错误处理防止URL解析异常

chore(assets): 更新微信二维码图片路径

- 将about.html中的微信二维码图片地址改为相对路径
- 将index.html中的微信二维码图片地址改为相对路径
- 移除远程CDN备用加载逻辑,统一使用本地资源
2025-11-26 17:18:54 +08:00
hehh
9f0d3d0a2a fix(about): 调整GitHub用户数据获取超时时间
- 将GitHub用户数据请求的超时时间从5秒调整为1秒
- 优化用户体验,减少等待时间
- 确保快速失败,提高页面响应速度
2025-11-26 12:41:34 +08:00
hehh
ac6c85a490 refactor(about): 调整 DataManager 初始化顺序
- 将 DataManager 的初始化移到 UIManager 之后
- 保持依赖注入的一致性
- 提高代码可读性和维护性
2025-11-26 12:36:53 +08:00
hehh
7f959b1b34 style(css): 优化黑夜模式下的评论区样式
- 调整输入框背景透明度和文字颜色以提升可读性
- 增强评论区域的视觉层次和悬停效果
- 新增编辑器底部工具栏的美化样式
- 优化发送按钮的渐变背景和交互动效
- 改进评论列表项的文字颜色和链接样式
- 统一黑夜模式下各组件的边框和阴影效果
2025-11-26 10:20:53 +08:00
hehh
d3cd2b69fe fix(ui): 修复夜间模式下编辑器文本颜色显示异常
- 将夜间模式编辑器文本颜色从 #dfe6e9 调整为 #1f2937
- 确保文本在深色背景下具有更好的可读性
- 保持与其他夜间模式组件的一致性
2025-11-26 01:06:39 +08:00
hehh
eec6778f8d fix(css): 修正夜间模式下的样式问题
- 删除了 about.css 中多余的闭合括号
- 调整了 artalk.css 中夜间模式输入框的文字颜色以提高可读性
2025-11-26 01:04:12 +08:00
hehh
0fbfd7b6dd style(css): 调整黑夜模式下的输入框和评论列表样式
- 为PC端黑夜模式下的编辑器文本区域添加背景、文字颜色及边框样式
- 设置编辑器文本区域聚焦时的边框和阴影效果
- 为评论包裹元素添加黑夜模式下的背景、边框和阴影样式
- 添加评论包裹元素在悬停状态下的样式变化
- 为评论列表头部和通用头部添加底部边框样式
2025-11-26 01:02:18 +08:00
hehh
ace68a0bad feat(about): 优化夜间主题样式及统计数据显示逻辑
- 为夜间主题的区域统计添加悬停效果
- 修复CSS语法错误,移除多余闭合括号
- 调整GitHub仓库拉取逻辑,增加超时控制和排序方式
- 修改展示仓库数量从12个至16个
- 移除页面访问量的格式化函数及相关调用
- 删除冗余的数据格式化脚本,简化加载逻辑
2025-11-26 00:59:09 +08:00
hehh
01e55e6d82 feat(analytics): 添加不蒜子统计数据格式化功能
- 更新不蒜子统计元素 ID 配置
- 实现自动格式化脚本,支持 K/W/M 单位转换
- 添加对 Google 和腾讯分析配置的动态引用
- 替换博客链接为 Twitter 链接并更新图标
- 优化统计数据显示逻辑,处理禁用状态与数字格式化
2025-11-26 00:16:40 +08:00
hehh
f9a1587587 refactor(css): 重新组织夜间主题样式结构
- 将夜间主题样式从文件中间位置移动到文件末尾
- 保持所有夜间主题相关样式的一致性和完整性
- 改善代码可读性和维护性
- 确保样式在不同主题切换时的正确应用
2025-11-25 23:58:19 +08:00
hehh
cea62b1c77 feat(analytics): 更新不蒜子统计脚本并移除本地格式化逻辑
- 将不蒜子统计脚本源地址更新为官方 CDN 地址
- 移除本地实现的访问量格式化脚本
- 依赖外部库自带的格式化功能替代原有手动实现
- 简化代码结构,减少冗余逻辑
- 提升页面加载性能与维护性
- 统一使用外部库的标准格式化规则
2025-11-25 22:35:24 +08:00
hehh
c293d31969 docs(readme): 修复代码块语法和任务列表格式
- 修复 README.md 中的代码块语法,将 `-``shell` 更正为 ```shell
- 修复任务列表中的格式错误,将 `-- []` 更正为 `- [ ]`
2025-11-25 22:28:25 +08:00
hehh
20b46b9fd2 fix(css): 调整英文状态下.stat-key的字体大小
- 针对[data-lang="en"]选择器下的.stat-key元素
- 将字体大小从0.6rem改为6px
- 使用!important确保样式优先级
2025-11-25 21:52:51 +08:00
hehh
b606b54d9b fix(i18n): 修正中英文翻译标签错误
- 交换了中文环境下 stats.visitors 和 stats.visitNum 的翻译值
- 交换了英文环境下 stats.visitors 和 stats.visitNum 的翻译值
- 确保访客数和访问量的显示标签正确对应
- 避免用户统计数据展示时产生误解
2025-11-25 21:50:02 +08:00
hehh
ff78d2c8b4 Merge branch 'version-2.2'
# Conflicts:
#	about.html
#	index.html
#	js/sw.js
2025-11-25 21:46:40 +08:00
hehh
14e669178e style(css): 增强黑夜模式下的统计区域样式
- 移除旧的 stat-key 样式定义
- 添加黑夜模式下 area-stats 的背景、边框和阴影效果
- 定义黑夜模式下 stat-item 的背景及悬停效果
- 设置黑夜模式下 stat-val 的文字颜色和发光效果
- 配置黑夜模式下 stat-key 的文字颜色和微弱发光效果
2025-11-25 21:45:58 +08:00
hehh
d8290974b5 refactor(about): 重构关于页面样式与交互逻辑
- 优化技术栈展示区域,新增3D标签云效果
- 调整兴趣模块布局,增强移动端适配
- 改进统计区域响应式设计,统一白天/黑夜模式样式
- 新增移动端社交链接区域,优化触摸体验
- 引入防抖机制优化3D球体动画性能
- 增强评论系统UI,支持动态主题切换
- 完善移动端评论内容展开收起功能
- 优化Artalk评论初始化流程,提高加载效率
- 统一全局样式命名规范,提升代码可维护性
- 移除冗余CSS规则,精简样式文件体积
- 修复黑夜模式下文字渐变显示异常问题
- 改进多语言支持,增强国际化体验
2025-11-25 20:21:22 +08:00
hehh
56d260e0d6 refactor(pwa): 移除PWA相关配置和Service Worker实现
- 删除 about.html 和 index.html 中的 PWA meta 标签
- 移除 PWA 注册脚本和服务工作器文件 js/sw.js
- 清理 Apple 和 Windows PWA 支持的相关配置
- 简化应用加载逻辑,不再依赖离线缓存机制
- 更新 manifest.json 引用及相关主题颜色设置
- 优化移动端显示设置,去除 user-scalable 属性
2025-11-25 18:44:31 +08:00
37 changed files with 4873 additions and 3630 deletions

View File

@@ -139,7 +139,7 @@ Home/
## 🚀 快速开始
``shell
```shell
# 克隆项目
git clone https://github.com/listener-He/Home.git
@@ -181,7 +181,7 @@ open about.html
- [x] 添加骨架屏加载效果
- [x] 实现数据缓存机制,提升页面加载速度
- [x] 添加淡入动画效果,提升用户体验
- [x] 实现 PWA 支持,支持离线访问
### 待完成
- [ ] 添加更多数据源(如 Twitter、知乎等
@@ -193,6 +193,7 @@ open about.html
- [ ] 添加更多主题选项(如高对比度模式等)
- [ ] 实现深色模式下的图片优化处理
- [ ] 添加无障碍访问支持ARIA 属性完善)
- [ ] 实现 PWA 支持,支持离线访问
## 🙏 鸣谢与致敬

View File

@@ -1,19 +1,18 @@
<!DOCTYPE html>
<html lang="zh-CN">
<html lang="zh-CN" data-lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>关于我 - Honesty</title>
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- PWA相关配置 -->
<meta name="theme-color" content="#6c5ce7">
<link rel="manifest" href="./manifest.json">
<!--SEO信息 -->
<meta name="description" content="关于Honesty,关于HeHouHui,关于HeHui,关于明厚, About Me Honesty, About Me HeHouHui, About Me HeHui">
<meta name="keywords" content="Honesty,HeHouHui,HeHui,明厚">
<link rel="canonical" href="https://www.hehouhui.cn/about.html">
<meta name="author" content="Honesty">
<!-- 社交平台分享优化 -->
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
@@ -22,7 +21,8 @@
<meta property="og:description" content="我是一名充满热情的Java后端开发工程师专注于AI技术的探索与应用。来自湖南现在上海工作享受在这座充满活力的城市中追求技术梦想。">
<meta property="og:image" content="https://www.hehouhui.cn/images/avatar.jpeg">
<meta property="og:site_name" content="Honesty的个人主页">
<meta property="og:locale" content="zh_CN">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://www.hehouhui.cn/about.html">
@@ -30,23 +30,26 @@
<meta property="twitter:description" content="我是一名充满热情的Java后端开发工程师专注于AI技术的探索与应用。来自湖南现在上海工作享受在这座充满活力的城市中追求技术梦想。">
<meta property="twitter:image" content="https://www.hehouhui.cn/images/avatar.jpeg">
<meta property="twitter:site" content="@Honesty861024">
<link rel="alternate" hreflang="zh-cn" href="https://www.hehouhui.cn/about.html">
<link rel="alternate" hreflang="en" href="https://www.hehouhui.cn/about.html?lang=en">
<link rel="alternate" hreflang="x-default" href="https://www.hehouhui.cn/about.html">
<!-- 微信小程序/朋友圈分享 -->
<meta property="wechat:image" content="https://www.hehouhui.cn/images/avatar.jpeg">
<meta property="wechat:title" content="关于我 - Honesty的个人主页">
<meta property="wechat:description" content="我是一名充满热情的Java后端开发工程师专注于AI技术的探索与应用。">
<!-- 核心资源:使用 BootCDN 加速 -->
<link href="https://cdn.bootcdn.net/ajax/libs/normalize/8.0.1/normalize.min.css" rel="stylesheet">
<link href="https://cdn.bootcdn.net/ajax/libs/remixicon/3.5.0/remixicon.min.css" rel="stylesheet">
<link rel="stylesheet" href="css/style.css?version=20251125">
<link rel="stylesheet" href="css/about.css?version=20251125">
<link href="https://cdn.bootcdn.net/ajax/libs/normalize/8.0.1/normalize.min.css" rel="preload" as="style" onload="this.onload=null;this.rel='stylesheet'">
<link href="https://cdn.bootcdn.net/ajax/libs/remixicon/3.5.0/remixicon.min.css" rel="preload" as="style" onload="this.onload=null;this.rel='stylesheet'">
<link rel="preload" href="css/about.css?version=20251125" as="style" onload="this.onload=null;this.rel='stylesheet'">
<!-- Artalk 评论样式 -->
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/artalk/2.9.1/Artalk.css">
<link rel="stylesheet" href="css/artalk.css?version=20251125">
<link rel="preload" href="https://cdn.bootcdn.net/ajax/libs/artalk/2.9.1/Artalk.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<link rel="preload" href="css/artalk.css?version=20251125" as="style" onload="this.onload=null;this.rel='stylesheet'">
<link rel="icon" href="favicon.ico">
<link rel="apple-touch-icon" href="./images/logo.png">
<!--IE淘汰计划-->
<script>
if (/*@cc_on!@*/false || (!!window.MSInputMethodContext && !!document.documentMode)) window.location.href = "https://imsyy.top/upgrade-your-browser/index.html?referrer=" + encodeURIComponent(window.location.href);
@@ -71,7 +74,7 @@
</a>
<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>
<span class="nav-label" data-i18n="nav.home">首页</span>
</a>
@@ -79,7 +82,7 @@
<i class="ri-user-3-line"></i>
<span class="nav-label" data-i18n="nav.about">关于</span>
</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>
<span class="nav-label" data-i18n="nav.blog">博客</span>
</a>
@@ -87,11 +90,11 @@
<div class="nav-divider"></div>
<!-- 功能按钮 -->
<button id="lang-btn" class="action-btn" aria-label="Switch Language">
<button id="lang-btn" class="action-btn" aria-label="Switch Language" tabindex="0">
<i class="ri-translate-2"></i>
<span class="btn-text">CN/EN</span>
</button>
<button id="theme-btn" class="action-btn" aria-label="Toggle Theme">
<button id="theme-btn" class="action-btn" aria-label="Toggle Theme" tabindex="0">
<i class="ri-sun-line icon-sun"></i>
<i class="ri-moon-line icon-moon"></i>
</button>
@@ -108,7 +111,7 @@
<div class="bento-card area-profile">
<div class="profile-content">
<div class="avatar-ring">
<img src="images/avatar.jpeg" alt="Honesty" class="avatar-img">
<img src="images/avatar.jpeg" alt="Honesty" class="avatar-img" loading="lazy" width="120" height="120">
<div class="status-dot" data-i18n="status.online">Online</div>
</div>
<div class="profile-info">
@@ -120,12 +123,21 @@
</div>
<!-- 6个社交链接 (PC端显示) -->
<div class="social-dock desktop-social">
<a href="https://github.com/listener-He" target="_blank" class="s-icon"><i class="ri-github-fill"></i></a>
<a href="mailto:hehouhui@foxmail.com" class="s-icon"><i class="ri-mail-send-fill"></i></a>
<a href="https://blog.hehouhui.cn" target="_blank" class="s-icon"><i class="ri-pages-line"></i></a>
<a href="https://www.zhihu.com/people/wen-xin-92-2-57" target="_blank" class="s-icon"><i class="ri-zhihu-line"></i></a>
<a href="javascript:void(0);" onclick="toggleWechat()" class="s-icon"><i class="ri-wechat-fill"></i></a>
<a href="https://juejin.cn/user/3659591622878503" target="_blank" class="s-icon"><i class="ri-code-box-line"></i></a>
<a href="https://github.com/listener-He" target="_blank" class="s-icon" aria-label="GitHub profile" tabindex="0"><i class="ri-github-fill"></i></a>
<a href="mailto:hehouhui@foxmail.com" class="s-icon" aria-label="Email contact" tabindex="0"><i class="ri-mail-send-fill"></i></a>
<a href="https://twitter.com/Honesty861024" target="_blank" class="s-icon" aria-label="Twitter profile" tabindex="0"><i class="ri-twitter-line"></i></a>
<a href="https://www.zhihu.com/people/wen-xin-92-2-57" target="_blank" class="s-icon" aria-label="Zhihu profile" tabindex="0"><i class="ri-zhihu-line"></i></a>
<a href="javascript:void(0);" onclick="toggleWechat()" class="s-icon" aria-label="WeChat QR code" tabindex="0"><i class="ri-wechat-fill"></i></a>
<a href="https://juejin.cn/user/3659591622878503" target="_blank" class="s-icon" aria-label="Juejin profile" tabindex="0"><i class="ri-code-box-line"></i></a>
</div>
<!-- 推荐分享模块 TRAE 链接https://www.trae.ai/s/8HSXCa -->
<div class="recommend-share-module">
<a href="https://www.trae.ai/s/8HSXCa" target="_blank" class="share-link" aria-label="推荐分享">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="none" viewBox="0 0 18 18" class="share-icon">
<path stroke="#32F08C" stroke-width="1.35" d="M3.75 9H3V6h12v3h-.75M3.75 9v6h10.5V9M3.75 9h10.5M9 6V4.75M9 6H7.75a2.5 2.5 0 0 1-2.5-2.5c0-.69.56-1.25 1.25-1.25A2.5 2.5 0 0 1 9 4.75M9 6h1.25a2.5 2.5 0 0 0 2.5-2.5c0-.69-.56-1.25-1.25-1.25A2.5 2.5 0 0 0 9 4.75M9 6v9"></path>
</svg>
<span class="badge-dot"></span>
</a>
</div>
</div>
</div>
@@ -157,11 +169,11 @@
<span class="stat-key" data-i18n="stats.followers">Followers</span>
</div>
<div class="stat-item">
<span class="stat-val neon-font" id="busuanzi_value_site_pv">0</span>
<span class="stat-val neon-font" id="busuanzi_site_pv">0</span>
<span class="stat-key" data-i18n="stats.visitNum">Visit num</span>
</div>
<div class="stat-item">
<span class="stat-val neon-font" id="busuanzi_value_site_uv">0</span>
<span class="stat-val neon-font" id="busuanzi_site_uv">0</span>
<span class="stat-key" data-i18n="stats.visitors">Visitors</span>
</div>
</div>
@@ -173,7 +185,7 @@
<span class="mbti-code gradient-text">INFJ</span>
<span class="mbti-name" data-i18n="mbti.name">Advocate</span>
<span class="mbti-icon">
<img src="images/INFJ.png" alt="INFJ" style="width:32px;height:32px;border-radius:50%;border:2px solid rgba(255,255,255,0.4)"/>
<img src="images/INFJ.webp" alt="INFJ" style="width:32px;height:32px;border-radius:50%;border:2px solid rgba(255,255,255,0.4)" loading="lazy" width="32" height="32"/>
</span>
</div>
<p class="mbti-desc" data-i18n="mbti.desc">"理想主义与道德感,果断决绝的行动力。深度洞察与创意,关怀与同理心。"</p>
@@ -238,12 +250,12 @@
<!-- 移动端显示的社交栏 (6个链接) -->
<div class="bento-card area-social-mobile mobile-social">
<a href="https://github.com/listener-He" class="ms-btn"><i class="ri-github-fill"></i></a>
<a href="mailto:hehouhui@foxmail.com" class="ms-btn"><i class="ri-mail-send-fill"></i></a>
<a href="https://blog.hehouhui.cn" class="ms-btn"><i class="ri-pages-line"></i></a>
<a href="https://www.zhihu.com/people/wen-xin-92-2-57" class="ms-btn"><i class="ri-zhihu-line"></i></a>
<a href="javascript:void(0);" onclick="toggleWechat()" class="ms-btn"><i class="ri-wechat-fill"></i></a>
<a href="https://juejin.cn/user/3659591622878503" class="ms-btn"><i class="ri-code-box-line"></i></a>
<a href="https://github.com/listener-He" class="ms-btn" aria-label="GitHub profile" tabindex="0"><i class="ri-github-fill"></i></a>
<a href="mailto:hehouhui@foxmail.com" class="ms-btn" aria-label="Email contact" tabindex="0"><i class="ri-mail-send-fill"></i></a>
<a href="https://twitter.com/Honesty861024" class="ms-btn" aria-label="Twitter profile" tabindex="0"><i class="ri-twitter-line"></i></a>
<a href="https://www.zhihu.com/people/wen-xin-92-2-57" class="ms-btn" aria-label="Zhihu profile" tabindex="0"><i class="ri-zhihu-line"></i></a>
<a href="javascript:void(0);" onclick="toggleWechat()" class="ms-btn" aria-label="WeChat QR code" tabindex="0"><i class="ri-wechat-fill"></i></a>
<a href="https://juejin.cn/user/3659591622878503" class="ms-btn" aria-label="Juejin profile" tabindex="0"><i class="ri-code-box-line"></i></a>
</div>
</div>
@@ -264,7 +276,7 @@
</div>
</div>
</div>
<!-- 第三部分:最新文章 -->
<div class="content-section">
<div class="content-col">
@@ -292,11 +304,11 @@
<!-- 移动端悬浮功能按钮 -->
<div class="mobile-fab">
<button id="fab-main" class="fab-main" aria-label="Actions"><i class="ri-magic-line"></i><span class="fab-label">Tools</span></button>
<button id="fab-main" class="fab-main" aria-label="Actions" tabindex="0"><i class="ri-magic-line"></i><span class="fab-label">Tools</span></button>
<div class="fab-menu" id="fab-menu">
<button id="fab-lang" class="fab-item"><i class="ri-translate-2"></i><span class="fab-text">Lang</span></button>
<button id="fab-theme" class="fab-item"><i class="ri-moon-line"></i><span class="fab-text">Theme</span></button>
<button id="fab-music" class="fab-item"><i class="ri-music-2-line"></i><span class="fab-text">Play</span></button>
<button id="fab-lang" class="fab-item" tabindex="0"><i class="ri-translate-2"></i><span class="fab-text">Lang</span></button>
<button id="fab-theme" class="fab-item" tabindex="0"><i class="ri-moon-line"></i><span class="fab-text">Theme</span></button>
<button id="fab-music" class="fab-item" tabindex="0"><i class="ri-music-2-line"></i><span class="fab-text">Play</span></button>
</div>
</div>
<audio id="site-audio" class="site-audio" src="data/至少做一件离谱的事-Kiri T.mp3" autoplay loop preload="auto"></audio>
@@ -308,15 +320,14 @@
<button class="modal-close" onclick="toggleWechat()"><i class="ri-close-line"></i></button>
<h3 data-i18n="modal.wechat">Official Account</h3>
<div class="qr-box">
<img src="https://blog-file.hehouhui.cn/wechat/mp-honesy.jpg" alt="WeChat QR" onerror="this.style.display='none';this.nextElementSibling.style.display='block'">
<!-- <div class="qr-fallback">QR Load Failed</div>-->
<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>
<p data-i18n="modal.desc">Scan to follow Tech Share</p>
</div>
</div>
<footer class="site-footer">
<p>&copy; 2018 <span id="currentYear"></span> Honesty. All rights reserved. <a class="icp" href="https://beian.miit.gov.cn/" target="_blank">湘ICP备20014902号</a> Powered By <a href="https://pages.edgeone.ai/" target="_blank"> Tencent EdgeOne </a></p>
<p>Copyright &copy; 2018 <span id="currentYear"></span> Honesty. All rights reserved. <a class="icp" href="https://beian.miit.gov.cn/" target="_blank">湘ICP备20014902号</a> Powered By <a href="https://pages.edgeone.ai/" target="_blank"> Tencent EdgeOne </a></p>
<script>
document.getElementById("currentYear").textContent = ' - ' + new Date().getFullYear();
</script>
@@ -324,27 +335,25 @@
</div>
<!-- 脚本BootCDN jQuery / Artalk -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="js/config.js?version=20251125"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/artalk/2.9.1/Artalk.js"></script>
<!-- 引入多语言包(按需) -->
<script src="https://cdn.jsdelivr.net/npm/artalk@latest/dist/i18n/zh-cn.js"></script>
<script src="https://cdn.jsdelivr.net/npm/artalk@latest/dist/i18n/en.js"></script>
<script src="js/about.js?version=20251125"></script>
<script src="js/jquery.min.js" defer></script>
<script src="js/config.js?version=20251125" defer></script>
<script src="https://cdn.bootcdn.net/ajax/libs/artalk/2.9.1/Artalk.js" defer></script>
<script src="js/about.js?version=20251125" defer></script>
<script defer src="https://events.vercount.one/js"></script>
<!-- 不蒜子统计 -->
<script>
document.addEventListener('DOMContentLoaded', function () {
// 动态加载不蒜子统计脚本
const script = document.createElement('script');
script.src = SiteConfig.analytics.busuanzi.src;
script.src = "//cdn.busuanzi.cc/busuanzi/3.6.9/busuanzi.abbr.min.js";
script.async = true;
document.head.appendChild(script);
});
</script>
<script>
try {
!function(p){"use strict";!function(t){var s=window,e=document,i=p,c="".concat("https:"===e.location.protocol?"https://":"http://","sdk.51.la/js-sdk-pro.min.js"),n=e.createElement("script"),r=e.getElementsByTagName("script")[0];n.type="text/javascript",n.setAttribute("charset","UTF-8"),n.async=!0,n.src=c,n.id="LA_COLLECT",i.d=n;var o=function(){s.LA.ids.push(i)};s.LA?s.LA.ids&&o():(s.LA=p,s.LA.ids=[],o()),r.parentNode.insertBefore(n,r)}()}({id: SiteConfig.analytics.tencent.id, ck: SiteConfig.analytics.tencent.ck});
!function(p){"use strict";!function(t){var s=window,e=document,i=p,c="".concat("https:"===e.location.protocol?"https://":"http://","sdk.51.la/js-sdk-pro.min.js"),n=e.createElement("script"),r=e.getElementsByTagName("script")[0];n.type="text/javascript",n.setAttribute("charset","UTF-8"),n.async=!0,n.src=c,n.id="LA_COLLECT",i.d=n;var o=function(){s.LA.ids.push(i)};s.LA?s.LA.ids&&o():(s.LA=p,s.LA.ids=[],o()),r.parentNode.insertBefore(n,r)}()}({id:"3OBGjwDdEIRS7XZ1",ck:"3OBGjwDdEIRS7XZ1"});
} catch (e) {
console.log("51.la统计错误", e);
}
@@ -356,7 +365,7 @@
(function () {
try {
var hm = document.createElement("script");
hm.src = SiteConfig.analytics.baidu.src;
hm.src = "https://hm.baidu.com/hm.js?ae2a009a75b13c21d5121ee51375ea4e";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
} catch (e) {
@@ -368,53 +377,24 @@
<!-- 自动格式化脚本 -->
<script>
// 核心格式化函数:支持 K / W / M保留最多两位小数去除尾随零
function formatWithUnit(num) {
num = Number(num);
if (isNaN(num) || num < 0) return '0';
// 小于 1000直接显示
if (num < 1_000) {
return Math.floor(num).toString();
}
// 1K ~ 9.99K
if (num < 10_000) {
let val = (num / 1_000).toFixed(2);
return parseFloat(val) + 'K';
}
// 1W ~ 99.99W 1W = 10,000
if (num < 1_000_000) {
let val = (num / 10_000).toFixed(2);
return parseFloat(val) + 'W';
}
// ≥ 1M
let val = (num / 1_000_000).toFixed(2);
return parseFloat(val) + 'M';
}
// 监听不蒜子数据更新并格式化
// 监听不蒜子数据的错误兜底
function initFormatter() {
const pvEl = document.getElementById(SiteConfig.analytics.busuanzi.site_pv_id);
const uvEl = document.getElementById(SiteConfig.analytics.busuanzi.site_uv_id);
const pvEl = document.getElementById("busuanzi_site_pv");
const uvEl = document.getElementById("busuanzi_site_uv");
if (!pvEl && !uvEl) return;
console.log('[Busuanzi]', 'Formatting... Listener observer');
const observer = new MutationObserver(() => {
if (pvEl?.textContent) {
const raw = pvEl.textContent.trim().replace(/[,]/g, '');
const num = parseFloat(raw);
if (!isNaN(num)) {
pvEl.textContent = formatWithUnit(num);
if (pvEl.textContent.includes('禁用')) {
pvEl.textContent = '-';
return;
}
}
if (uvEl?.textContent) {
const raw = uvEl.textContent.trim().replace(/[,]/g, '');
const num = parseFloat(raw);
if (!isNaN(num)) {
uvEl.textContent = formatWithUnit(num);
if (uvEl.textContent.includes('禁用')) {
uvEl.textContent = '-';
return;
}
}
});
@@ -430,37 +410,12 @@
initFormatter();
}
</script>
<!-- PWA注册 -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
setTimeout(() => {
navigator.serviceWorker.register('./js/sw.js')
.then(function(registration) {
console.log('SW registered: ', registration);
})
.catch(function(registrationError) {
console.log('SW registration failed: ', registrationError);
});
}, 3000); // 页面稳定后再注册
});
}
</script>
<!-- Apple PWA支持 -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Honesty">
<link rel="apple-touch-icon" href="./images/avatar.jpeg">
<!-- Windows PWA支持 -->
<meta name="msapplication-TileImage" content="./images/avatar.jpeg">
<meta name="msapplication-TileColor" content="#6c5ce7">
<meta name="msapplication-tap-highlight" content="no">
<!-- 其他PWA相关meta标签 -->
<meta name="mobile-web-app-capable" content="yes">
<noscript>
<link href="https://cdn.bootcdn.net/ajax/libs/normalize/8.0.1/normalize.min.css" rel="stylesheet">
<link href="https://cdn.bootcdn.net/ajax/libs/remixicon/3.5.0/remixicon.min.css" rel="stylesheet">
<link rel="stylesheet" href="css/about.css?version=20251125">
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/artalk/2.9.1/Artalk.css">
<link rel="stylesheet" href="css/artalk.css?version=20251125">
</noscript>
</body>
</html>
</html>

1497
christmas.html Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,17 +3,10 @@
/* Base Artalk container styles */
#artalk-container {
border-radius: 12px;
contain: layout style;
}
/* 确保评论区域适配白天/黑夜模式 */
#artalk-container .atk-main-editor {
background: var(--glass-bg);
border: var(--glass-border);
box-shadow: var(--glass-shadow);
backdrop-filter: blur(var(--glass-blur)) saturate(130%);
-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(130%);
border-radius: 24px;
}
#artalk-container .atk-comment-wrap {
background: rgba(128, 128, 128, 0.03);
@@ -31,7 +24,7 @@
background: rgba(128, 128, 128, 0.08);
}
#artalk-container .atk-dialog,
#artalk-container .atk-dialog,
#artalk-container .atk-layer .atk-dialog {
background: var(--glass-bg);
color: var(--text-primary);
@@ -44,7 +37,6 @@
/* Light theme styles */
.atk-main-editor {
background: rgba(255, 255, 255, 0.85) !important;
backdrop-filter: blur(28px) saturate(180%) !important;
-webkit-backdrop-filter: blur(28px) saturate(180%) !important;
border: 1px solid rgba(108, 92, 231, 0.2) !important;
@@ -208,118 +200,7 @@
color: #6c5ce7 !important;
}
/* Night theme styles */
[data-theme="night"] .atk-main-editor {
background: rgba(30, 30, 35, 0.75) !important;
backdrop-filter: blur(28px) saturate(180%) !important;
-webkit-backdrop-filter: blur(28px) saturate(180%) !important;
border: 1px solid rgba(255, 255, 255, 0.12) !important;
box-shadow: 0 18px 60px rgba(0, 0, 0, 0.8) !important;
}
[data-theme="night"] .atk-send-btn {
background: linear-gradient(135deg, #00cec9, #00b3ae) !important;
border: none !important;
border-radius: 20px !important;
padding: 8px 20px !important;
font-weight: 500 !important;
transition: all 0.3s ease !important;
box-shadow: 0 4px 15px rgba(0, 206, 201, 0.4);
}
[data-theme="night"] .atk-send-btn:hover {
background: linear-gradient(135deg, #00b3ae, #009690) !important;
transform: translateY(-2px) !important;
box-shadow: 0 6px 20px rgba(0, 206, 201, 0.6) !important;
}
[data-theme="night"] .atk-send-btn:active {
transform: translateY(0) !important;
}
[data-theme="night"] .atk-editor,
[data-theme="night"] .atk-editor textarea,
[data-theme="night"] .atk-editor input {
background: rgba(30, 30, 35, 0.75);
color: #dfe6e9;
}
[data-theme="night"] .atk-comment-wrap {
background: rgba(40, 40, 45, 0.85); /* 提高背景不透明度以增强可读性 */
border: 1px solid rgba(255, 255, 255, 0.12);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
transition: all 0.3s ease;
}
[data-theme="night"] .atk-comment-wrap:hover {
background: rgba(45, 45, 50, 0.95); /* 提高悬停时的背景不透明度 */
border: 1px solid rgba(255, 255, 255, 0.18);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7);
}
[data-theme="night"] .atk-dialog,
[data-theme="night"] .atk-layer .atk-dialog {
background: rgba(30, 30, 35, 0.85);
color: #dfe6e9;
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow: 0 18px 60px rgba(0, 0, 0, 0.8);
}
[data-theme="night"] .atk-header-item {
color: #dfe6e9 !important;
}
[data-theme="night"] .atk-editor-textarea {
color: #dfe6e9 !important;
background: rgba(40, 40, 45, 0.5) !important;
border: 1px solid rgba(255, 255, 255, 0.08) !important;
}
[data-theme="night"] .atk-editor-bottom .atk-item {
color: #b2bec3 !important;
}
[data-theme="night"] .atk-list-header .atk-comment-count {
color: #dfe6e9 !important;
}
[data-theme="night"] .atk-sort-select {
background: rgba(255, 255, 255, 0.08) !important;
border: 1px solid rgba(255, 255, 255, 0.15) !important;
color: #b2bec3 !important;
}
[data-theme="night"] .atk-comment .atk-nick {
color: #ffffff !important;
font-weight: 600 !important;
}
[data-theme="night"] .atk-comment .atk-date {
color: #a0aec0 !important;
}
[data-theme="night"] .atk-comment .atk-content {
color: #e2e8f0 !important;
}
[data-theme="night"] .atk-comment .atk-content a {
color: #00cec9 !important;
text-decoration: underline !important;
}
[data-theme="night"] .atk-comment .atk-content pre {
background: rgba(20, 20, 25, 0.7) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
color: #e2e8f0 !important;
}
[data-theme="night"] .atk-actions .atk-action {
color: #b2bec3 !important;
}
[data-theme="night"] .atk-actions .atk-action:hover {
color: #00cec9 !important;
}
/* Mobile specific styles */
.atk-mobile .atk-main-editor {
@@ -456,30 +337,7 @@
box-shadow: 0 6px 15px rgba(108, 92, 231, 0.25) !important;
}
[data-theme="night"] .atk-pagination .atk-page-item {
background: rgba(255, 255, 255, 0.08) !important;
color: #b2bec3 !important;
border: 1px solid rgba(255, 255, 255, 0.12) !important;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4) !important;
backdrop-filter: blur(28px) saturate(180%) !important;
-webkit-backdrop-filter: blur(28px) saturate(180%) !important;
transition: all 0.3s ease;
}
[data-theme="night"] .atk-pagination .atk-page-item:hover {
background: linear-gradient(135deg, #00cec9, #00b3ae) !important;
color: white !important;
border: 1px solid rgba(0, 206, 201, 0.3) !important;
box-shadow: 0 8px 25px rgba(0, 206, 201, 0.3) !important;
transform: translateY(-2px);
}
[data-theme="night"] .atk-pagination .atk-page-item.atk-active {
background: linear-gradient(135deg, #00cec9, #00b3ae) !important;
color: white !important;
border: 1px solid rgba(0, 206, 201, 0.4) !important;
box-shadow: 0 8px 25px rgba(0, 206, 201, 0.4) !important;
}
/* Loading spinner */
.atk-loading {
@@ -516,6 +374,11 @@
margin: 2px !important;
}
[data-theme="night"] .atk-editor-toolbar .atk-btn {
color: #b2bec3 !important;
filter: brightness(1.3);
}
/* 暗色模式下聚焦边框 */
.atk-dark .atk-editor-textarea:focus {
box-shadow: 0 0 0 2px rgba(66, 153, 225, 0.5) !important;
@@ -526,42 +389,42 @@
.atk-header {
padding: 15px !important;
}
.atk-editor-plug-wrap {
padding: 0 15px 15px !important;
}
.atk-editor-textarea {
padding: 12px !important;
}
.atk-editor-bottom {
padding: 0 15px 15px !important;
}
.atk-list-header {
padding: 15px !important;
}
.atk-comment-wrap {
padding: 15px !important;
}
.atk-pagination {
padding: 15px !important;
}
.atk-pagination .atk-page-item {
padding: 6px 12px !important;
margin: 0 3px !important;
font-size: 0.9rem !important;
}
/* 移动端增强样式 */
.atk-main-editor {
border-radius: 16px !important;
}
.atk-comment-wrap {
padding: 12px 0 !important;
font-size: 0.95rem !important;
@@ -573,24 +436,30 @@
border-top: none !important;
background: transparent !important;
}
.atk-avatar {
width: 28px !important;
height: 28px !important;
object-fit: cover; /* 修复移动端头像拉伸问题 */
}
.atk-comment>.atk-avatar img {
object-fit: cover;
object-position: center;
width: 28px !important;
height: 28px !important;
}
.atk-meta {
font-size: 12px !important;
}
/* 修复移动端输入框底色问题 */
[data-theme="night"] .atk-editor-textarea {
background: rgba(40, 40, 45, 0.5) !important;
color: #dfe6e9 !important;
border: 1px solid rgba(255, 255, 255, 0.08) !important;
}
/* 完全移除评论区域的容器样式以节省宽度 */
#artalk-container {
border-radius: 0 !important;
@@ -599,13 +468,299 @@
border: none !important;
box-shadow: none !important;
}
/* 黑夜模式下移动端评论背景色优化 */
[data-theme="night"] .atk-comment-wrap {
background: rgba(40, 40, 45, 0.9) !important; /* 提高移动端黑夜模式下的背景不透明度 */
}
[data-theme="night"] .atk-comment-wrap:hover {
background: rgba(45, 45, 50, 0.95) !important;
}
}
/* Night theme styles */
[data-theme="night"] .atk-main-editor {
background: rgba(30, 30, 35, 0.75) !important;
backdrop-filter: blur(28px) saturate(180%) !important;
-webkit-backdrop-filter: blur(28px) saturate(180%) !important;
border: 1px solid rgba(255, 255, 255, 0.12) !important;
box-shadow: 0 18px 60px rgba(0, 0, 0, 0.8) !important;
}
[data-theme="night"] .atk-send-btn {
background: linear-gradient(135deg, #00cec9, #00b3ae) !important;
border: none !important;
border-radius: 20px !important;
padding: 8px 20px !important;
font-weight: 500 !important;
transition: all 0.3s ease !important;
box-shadow: 0 4px 15px rgba(0, 206, 201, 0.4);
}
[data-theme="night"] .atk-send-btn:hover {
background: linear-gradient(135deg, #00b3ae, #009690) !important;
transform: translateY(-2px) !important;
box-shadow: 0 6px 20px rgba(0, 206, 201, 0.6) !important;
}
[data-theme="night"] .atk-send-btn:active {
transform: translateY(0) !important;
}
[data-theme="night"] .atk-editor,
[data-theme="night"] .atk-editor textarea,
[data-theme="night"] .atk-editor input {
background: rgba(30, 30, 35, 0.75)!important;
color: #dfe6e9 !important;
}
[data-theme="night"] .atk-comment-wrap {
background: rgba(40, 40, 45, 0.85) !important; /* 提高背景不透明度以增强可读性 */
border: 1px solid rgba(255, 255, 255, 0.12) !important;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5) !important;
transition: all 0.3s ease !important;
}
[data-theme="night"] .atk-comment-wrap:hover {
background: rgba(45, 45, 50, 0.95) !important; /* 提高悬停时的背景不透明度 */
border: 1px solid rgba(255, 255, 255, 0.18) !important;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7) !important;
}
[data-theme="night"] .atk-dialog,
[data-theme="night"] .atk-layer .atk-dialog {
background: rgba(30, 30, 35, 0.85) !important;
color: #dfe6e9 !important;
border: 1px solid rgba(255, 255, 255, 0.15) !important;
box-shadow: 0 18px 60px rgba(0, 0, 0, 0.8) !important;
}
[data-theme="night"] .atk-header-item {
color: #dfe6e9 !important;
}
[data-theme="night"] .atk-editor-textarea {
color: #1f2937 !important;
background: rgba(40, 40, 45, 0.5) !important;
border: 1px solid rgba(255, 255, 255, 0.08) !important;
}
[data-theme="night"] .atk-editor-bottom .atk-item {
color: #b2bec3 !important;
}
[data-theme="night"] .atk-list-header .atk-comment-count {
color: #dfe6e9 !important;
}
[data-theme="night"] .atk-sort-select {
background: rgba(255, 255, 255, 0.08) !important;
border: 1px solid rgba(255, 255, 255, 0.15) !important;
color: #b2bec3 !important;
}
[data-theme="night"] .atk-comment .atk-nick {
color: #ffffff !important;
font-weight: 600 !important;
}
[data-theme="night"] .atk-comment .atk-date {
color: #a0aec0 !important;
}
[data-theme="night"] .atk-comment .atk-content {
color: #e2e8f0 !important;
}
[data-theme="night"] .atk-comment .atk-content a {
color: #00cec9 !important;
text-decoration: underline !important;
}
[data-theme="night"] .atk-comment .atk-content pre {
background: rgba(20, 20, 25, 0.7) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
color: #e2e8f0 !important;
}
[data-theme="night"] .atk-actions .atk-action {
color: #b2bec3 !important;
}
[data-theme="night"] .atk-actions .atk-action:hover {
color: #00cec9 !important;
}
[data-theme="night"] .atk-pagination .atk-page-item {
background: rgba(255, 255, 255, 0.08) !important;
color: #b2bec3 !important;
border: 1px solid rgba(255, 255, 255, 0.12) !important;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4) !important;
backdrop-filter: blur(28px) saturate(180%) !important;
-webkit-backdrop-filter: blur(28px) saturate(180%) !important;
transition: all 0.3s ease;
}
[data-theme="night"] .atk-pagination .atk-page-item:hover {
background: linear-gradient(135deg, #00cec9, #00b3ae) !important;
color: white !important;
border: 1px solid rgba(0, 206, 201, 0.3) !important;
box-shadow: 0 8px 25px rgba(0, 206, 201, 0.3) !important;
transform: translateY(-2px);
}
[data-theme="night"] .atk-pagination .atk-page-item.atk-active {
background: linear-gradient(135deg, #00cec9, #00b3ae) !important;
color: white !important;
border: 1px solid rgba(0, 206, 201, 0.4) !important;
box-shadow: 0 8px 25px rgba(0, 206, 201, 0.4) !important;
}
}
/* PC端黑夜模式下的输入框颜色和列表样式修正 - 全面优化版 */
[data-theme="night"] .atk-editor-textarea {
background: rgba(30, 30, 35, 0.95) !important; /* 提高背景不透明度增强可读性 */
color: #ffffff !important; /* 使用更亮的文字颜色 */
border: 1px solid rgba(255, 255, 255, 0.2) !important; /* 增强边框可见性 */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
}
[data-theme="night"] .atk-editor-textarea:focus {
border: 1px solid #00cec9 !important;
box-shadow: 0 0 0 3px rgba(0, 206, 201, 0.4) !important;
}
[data-theme="night"] .atk-comment-wrap {
background: rgba(30, 30, 35, 0.95) !important; /* 提高背景不透明度增强可读性 */
border: 1px solid rgba(255, 255, 255, 0.15) !important;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3) !important;
}
[data-theme="night"] .atk-comment-wrap:hover {
background: rgba(40, 40, 45, 0.98) !important; /* 进一步提高背景不透明度 */
border: 1px solid rgba(0, 206, 201, 0.5) !important; /* 增强边框可见性 */
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4) !important;
}
[data-theme="night"] .atk-list-header {
border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
}
[data-theme="night"] .atk-header {
border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
}
/* 修复黑夜模式下插件面板的背景色问题 */
[data-theme="night"] .atk-editor-plug-wrap,
[data-theme="night"] .atk-plug-panel-wrap,
[data-theme="night"] .atk-editor-plug-emoticons,
[data-theme="night"] .atk-editor-plug-preview {
background: rgba(30, 30, 35, 0.95) !important;
color: #ffffff !important;
}
[data-theme="night"] .atk-plug-panel-wrap {
border: 1px solid rgba(255, 255, 255, 0.15) !important;
border-radius: 12px !important;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3) !important;
}
[data-theme="night"] .atk-editor-plug-emoticons {
border-top: 1px solid rgba(255, 255, 255, 0.08) !important;
}
/* 优化评论底部工具栏在黑夜模式下的样式 */
[data-theme="night"] .atk-editor-bottom {
background: rgba(40, 40, 45, 0.7) !important;
border-top: 1px solid rgba(255, 255, 255, 0.08) !important;
padding: 12px 20px !important;
border-radius: 0 0 12px 12px !important;
}
[data-theme="night"] .atk-editor-bottom .atk-item {
color: #b2bec3 !important;
margin-right: 12px !important;
}
[data-theme="night"] .atk-send-btn {
background: linear-gradient(135deg, #00cec9, #00b3ae) !important;
border: none !important;
border-radius: 20px !important;
padding: 8px 20px !important;
font-weight: 500 !important;
transition: all 0.3s ease !important;
box-shadow: 0 4px 15px rgba(0, 206, 201, 0.4);
}
[data-theme="night"] .atk-send-btn:hover {
background: linear-gradient(135deg, #00b3ae, #009690) !important;
transform: translateY(-2px) !important;
box-shadow: 0 6px 20px rgba(0, 206, 201, 0.6) !important;
}
[data-theme="night"] .atk-send-btn:active {
transform: translateY(0) !important;
}
/* 优化评论列表项在黑夜模式下的样式 */
[data-theme="night"] .atk-comment {
color: #e2e8f0 !important;
}
[data-theme="night"] .atk-comment .atk-nick {
color: #ffffff !important;
font-weight: 600 !important;
}
[data-theme="night"] .atk-comment .atk-date {
color: #a0aec0 !important;
}
[data-theme="night"] .atk-comment .atk-content {
color: #ffffff !important; /* 使用更亮的文字颜色提高可读性 */
line-height: 1.6 !important;
}
[data-theme="night"] .atk-comment .atk-content a {
color: #00cec9 !important;
text-decoration: underline !important;
}
[data-theme="night"] .atk-actions .atk-action {
color: #b2bec3 !important;
transition: all 0.2s ease !important;
margin-right: 15px; /* 增加回复评论组件的边距 */
}
[data-theme="night"] .atk-actions .atk-action:hover {
color: #00cec9 !important;
}
/* 修复黑夜模式下评论计数和下拉菜单的可见性 */
[data-theme="night"] .atk-comment-count {
color: #ffffff !important;
font-weight: 600 !important;
text-shadow: 0 0 8px rgba(255, 255, 255, 0.3) !important;
}
[data-theme="night"] .atk-dropdown-wrap {
background: rgba(30, 30, 35, 0.95) !important;
}
[data-theme="night"] .atk-dropdown-item {
color: #ffffff !important;
}
[data-theme="night"] .atk-dropdown-item:hover {
background: rgba(0, 206, 201, 0.2) !important;
color: #00cec9 !important;
}
[data-theme="night"] .atk-dropdown-item.active {
background: rgba(0, 206, 201, 0.3) !important;
color: #00cec9 !important;
}
[data-theme="night"] .atk-arrow-down-icon {
filter: brightness(2) !important;
}

View File

@@ -262,7 +262,7 @@ pre code { padding: 0; background: none; border: none; word-wrap: normal; }
font-size: 1.3em;
font-weight: normal;
letter-spacing: 2px;
color: #f0f0f0;
color: #ffffff;
-webkit-font-smoothing: antialiased;
margin-bottom: 20px;
}
@@ -275,7 +275,7 @@ pre code { padding: 0; background: none; border: none; word-wrap: normal; }
line-height: 2;
letter-spacing: 2px;
color: #ffffff;
text-shadow: 0 1px 2px rgba(141, 114, 114, 0.2) !important;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) !important;
position: relative;
z-index: 1;
font-feature-settings: 'kern' 1;
@@ -872,7 +872,7 @@ nav {
.weixin-qrcode-desc {
font-size: 14px;
color: #666;
color: #555;
margin-bottom: 15px;
}
@@ -986,12 +986,12 @@ nav {
display: inline-block;
margin: 0 5px;
font-size: 0.5em;
color: rgba(255, 255, 255, 0.7);
color: rgba(255, 255, 255, 0.85);
line-height: 1.2;
}
.power a {
color: rgba(255, 255, 255, 0.8);
color: rgba(255, 255, 255, 0.95);
font-size: 0.5em;
text-decoration: none;
transition: color 0.3s ease;
@@ -1006,8 +1006,7 @@ nav {
.remark .power:not(:last-child):after {
content: "|";
margin-left: 8px;
color: rgba(255, 255, 255, 0.5);
color: rgba(255, 255, 255, 0.5);
color: rgba(255, 255, 255, 0.7);
}

BIN
data/christmas.mp3 Normal file

Binary file not shown.

View File

@@ -1,32 +1,72 @@
[
{
"id": 1011713435,
"name": "spring-ai-example",
"stargazers_count": 13,
"forks_count": 0,
"description": "本项目是一个基于 Spring AI 框架构建的检索增强生成RAG应用旨在通过向量检索技术增强大模型的生成能力使 AI 回答既智能又有依据。 项目采用现代化 Java 技术栈,核心框架为 Spring Boot 3.3 与 Spring AI 1.0,集成 OpenAI 模型deepseek-r1、doubao-embedding主打白嫖支持 PostgreSQL+pgvector、Elasticsearch 等多种向量存储方案,并利用 Redis 实现缓存加速、Elasticsearch 管理对话记忆。",
"html_url": "https://github.com/listener-He/spring-ai-example"
},
{
"name": "yunxiao-LLM-reviewer",
"html_url": "https://github.com/listener-He/yunxiao-LLM-reviewer",
"description": "一款专为阿里云云效 Flow 平台设计的自动化代码审查工具。通过集成 Qwen、DeepSeek 等先进大模型,该工具能够实时分析 Git 合并请求MR中的代码变更智能识别潜在问题并自动生成结构化评审意见。",
"stargazers_count": 9,
"stargazers_count": 10,
"forks_count": 3,
"language": "TypeScript",
"updated_at": "2025-10-10T11:06:34Z"
"description": "一款专为阿里云云效 Flow 平台设计的自动化代码审查工具。通过集成 Qwen、DeepSeek 等先进大模型,该工具能够实时分析 Git 合并请求MR中的代码变更智能识别潜在问题并自动生成结构化评审意见。 工具支持多维度代码检测,包括逻辑错误、安全漏洞,有效提升团队代码质量与开发效率。 作为云效 Flow 的自定义步骤,该工具可无缝集成到 CI/CD 流水线中,实现 MR 提交时的自动化代码审查,大幅减少人工评审工作量,加速代码交付流程。",
"html_url": "https://github.com/listener-He/yunxiao-LLM-reviewer"
},
{
"id": 1064414600,
"name": "hexo-theme-stellar",
"html_url": "https://github.com/listener-He/hexo-theme-stellar",
"description": "综合型hexo主题博客+知识库+专栏+笔记,内置海量的标签组件和动态数据组件。",
"stargazers_count": 0,
"name": "Home",
"stargazers_count": 3,
"forks_count": 0,
"language": null,
"updated_at": "2025-09-26T02:12:18Z"
"description": "现代化个人主页,融合科技感与个性化元素,展示个人技术栈、开源项目和博客文章。",
"html_url": "https://github.com/listener-He/Home"
},
{
"id": 1060085476,
"name": "Universal-IoT-Java",
"html_url": "https://github.com/listener-He/Universal-IoT-Java",
"description": "通用 IoT Java 平台(示例)",
"stargazers_count": 0,
"name": "auto-ip2region",
"stargazers_count": 2,
"forks_count": 0,
"language": null,
"updated_at": "2025-10-13T02:30:28Z"
"description": "Auto IP2Region 是一个智能化的IP地址地理信息解析库它结合了本地数据库和多个免费在线API服务通过智能负载均衡和自动故障转移机制为您提供准确、可靠的IP地理位置信息查询服务。",
"html_url": "https://github.com/listener-He/auto-ip2region"
},
{
"name": "collection-complete",
"stargazers_count": 2,
"forks_count": 2,
"description": "collection-complete 是一个用于处理集合数据并补充相关信息的Java库。它提供了链式调用的功能可以方便地对集合中的元素进行批量操作和属性补充。全程函数式 均衡了易用性与扩展性的设计",
"html_url": "https://github.com/listener-He/collection-complete"
},
{
"name": "keycloak-services-social-weixin",
"stargazers_count": 2,
"forks_count": 3,
"description": "",
"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",
"stargazers_count": 1,
"forks_count": 0,
"description": "基于NotionNext作为后端的微信小程序",
"html_url": "https://github.com/listener-He/Notion-Wechat-Blog"
},
{
"name": "social-auth",
"stargazers_count": 1,
"forks_count": 0,
"description": "",
"html_url": "https://github.com/listener-He/social-auth"
},
{
"name": "hp-lite",
"stargazers_count": 0,
"forks_count": 1,
"description": "内网穿透 轻量版 支持 https http tcp udp 支持云端动态控制穿透配置支持免费SSL证书和续签、支持限流和IP黑白名单多账户、统计、http、socks代理、反向代理、多设备管理等功能。",
"html_url": "https://github.com/listener-He/hp-lite"
}
]
]

View File

@@ -10,10 +10,10 @@
"blog": "https://www.hehouhui.cn",
"hireable": true,
"bio": "Hi, Im 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,
"followers": 6,
"following": 12,
"created_at": "2018-05-14T02:57:55Z",
"updated_at": "2025-11-09T05:45:15Z"
}
}

BIN
data/hand_landmarker.task Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

8
edgeone.json Normal file
View File

@@ -0,0 +1,8 @@
{
"rewrites": [
{
"source": "/.well-known/*.txt",
"destination": "/well-known/:splat.txt"
}
]
}

BIN
images/INFJ.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
images/bj/1.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

BIN
images/bj/2.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

BIN
images/bj/3.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 KiB

BIN
images/bj/4.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
images/bj/5.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

BIN
images/bj/6.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
images/bj/7.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 KiB

BIN
images/bj/8.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

BIN
images/mp-honesy.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -13,9 +13,6 @@
<meta name="keywords" content="Honesty,HeHouHui,HeHui,明厚">
<meta name="author" content="Honesty">
<!-- PWA相关配置 -->
<meta name="theme-color" content="#6c5ce7">
<link rel="manifest" href="./manifest.json">
<!-- 社交平台分享优化 -->
<!-- Open Graph / Facebook -->
@@ -67,7 +64,7 @@
</script>
</head>
<body oncontextmenu=self.event.returnValue=false onselectstart="return false">
<body>
<header id="panel" class="panel-cover">
<script>
WIDGET = {
@@ -107,7 +104,7 @@
<div class="info iUp">
<div class="info-back">
<img alt="img" src="images/kl.gif"
class="js-avatar profilepic">
class="js-avatar profilepic" loading="lazy">
</div>
</div>
</a>
@@ -266,9 +263,9 @@
<div class="weixin-qrcode-container">
<div class="weixin-qrcode-title">扫描二维码</div>
<div class="weixin-qrcode-desc">请使用微信扫一扫添加关注</div>
<img src="https://blog-file.hehouhui.cn/wechat/mp-honesy.jpg" alt="微信二维码"
<img src="./images/mp-honesy.jpg" alt="微信二维码"
class="weixin-qrcode-image"
onerror="this.src='https://cdn.jsdmirror.com/gh/listener-He/Home/images/logo.png'; this.alt='二维码加载失败'">
onerror="this.src='https://cdn.jsdmirror.com/gh/listener-He/Home/images/logo.png'; this.alt='二维码加载失败'" loading="lazy">
<button class="weixin-qrcode-close" onclick="closeWeixin()">关闭</button>
</div>
</div>
@@ -280,7 +277,7 @@
// 防止滚动穿透
document.body.style.overflow = 'hidden';
// 添加ESC键关闭
document.addEventListener('keydown', closeOnEsc);
document.addEventListener('keydown', closeOnEsc, { passive: false });
}
function closeWeixin() {
@@ -289,7 +286,7 @@
// 恢复滚动
document.body.style.overflow = '';
// 移除ESC键监听
document.removeEventListener('keydown', closeOnEsc);
document.removeEventListener('keydown', closeOnEsc, { passive: false });
}
function closeOnEsc(e) {
@@ -303,12 +300,12 @@
if (e.target === this) {
closeWeixin();
}
});
}, { passive: false });
// 阻止点击弹框内容时关闭
document.querySelector('.weixin-qrcode-container').addEventListener('click', function (e) {
e.stopPropagation();
});
}, { passive: false });
</script>
</div>
<div class="remark iUp">
@@ -338,10 +335,7 @@
<script type="text/javascript" src="js/fetch.min.js"></script>
<script type="text/javascript" src="js/config.js"></script>
<script type="text/javascript" src="js/main.js?version=3"></script>
<!--<script type="text/javascript" src="js/bj.js"></script>-->
<!--<script type="text/javascript" src="https://cdn.jsdmirror.com/gh/listener-He/Home/js/moments.js"></script>-->
<script async src="https://analyse.hehouhui.cn/tracker.js" data-ackee-server="https://analyse.hehouhui.cn"
data-ackee-domain-id="7887135f-a413-46e2-a98c-52d4f18d9973"></script>
<script defer src="https://events.vercount.one/js"></script>
<!-- 不蒜子统计 -->
<script>
@@ -395,7 +389,7 @@
<!-- 可选:继续调用 config -->
<script type="text/javascript">
try {
gtag('config', 'G-DYWDEVKDP0');
gtag('config', SiteConfig.analytics.google.id);
} catch (e) {
console.error("Google Analytics config 失败", e);
}
@@ -414,38 +408,7 @@
};
s.LA ? s.LA.ids && o() : (s.LA = p, s.LA.ids = [], o()), r.parentNode.insertBefore(n, r)
}()
}({id: "3OBGjwDdEIRS7XZ1", ck: "3OBGjwDdEIRS7XZ1"});
}({id: SiteConfig.analytics.tencent.id, ck: SiteConfig.analytics.tencent.ck});
</script>
<!-- PWA注册 -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
setTimeout(() => {
navigator.serviceWorker.register('./js/sw.js')
.then(function(registration) {
console.log('SW registered: ', registration);
})
.catch(function(registrationError) {
console.log('SW registration failed: ', registrationError);
});
}, 3000); // 页面稳定后再注册
});
}
</script>
<!-- Apple PWA支持 -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Honesty">
<link rel="apple-touch-icon" href="./images/logo.png">
<!-- Windows PWA支持 -->
<meta name="msapplication-TileImage" content="./images/avatar.jpeg">
<meta name="msapplication-TileColor" content="#6c5ce7">
<meta name="msapplication-tap-highlight" content="no">
<!-- 其他PWA相关meta标签 -->
<meta name="mobile-web-app-capable" content="yes">
</body>
</html>
</html>

View File

@@ -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));
} 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 = 10;
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);
}
};
}
}
}();

View File

@@ -13,7 +13,7 @@ function getStoredLanguage() {
// 公共方法:设置本地存储的主题设置
function setStoredTheme(theme) {
const cacheKey = window.SiteConfig?.cacheKeys?.theme?.key || 'theme-v2';
const cacheKey = window.SiteConfig?.cacheKeys?.theme?.key || 'theme';
localStorage.setItem(cacheKey, JSON.stringify({
value: theme, time: new Date().getTime()
}));
@@ -53,8 +53,8 @@ class AppCore {
constructor() {
this.i18n = new I18nManager();
this.theme = new ThemeManager();
this.data = new DataManager();
this.ui = new UIManager();
this.data = new DataManager();
}
}
@@ -63,7 +63,23 @@ class AppCore {
=========================== */
class I18nManager {
constructor() {
this.lang = getStoredLanguage();
// 获取当前请求参数有无 lang 参数
try {
this.query = new URLSearchParams(window.location.search);
if (this.query.has('lang')) {
let lang = this.query.get('lang');
if (lang === 'zh' || lang === 'en') {
this.lang = lang;
setStoredLanguage(lang);
}
}
} catch (e) {
console.error(e);
}
if (!this.lang) {
this.lang = getStoredLanguage();
}
this.dict = {
zh: {
"nav.home": "首页",
@@ -79,8 +95,8 @@ class I18nManager {
"stats.exp": "编程年限",
"stats.repos": "开源项目",
"stats.followers": "关注者",
"stats.visitors": "访问量",
"stats.visitNum": "访客数",
"stats.visitors": "访客数",
"stats.visitNum": "访问量",
"mbti.name": "提倡者",
"mbti.desc": "提倡者人格类型的人非常稀少只有不到1%的人口属于这种类型,但他们对世界的贡献不容忽视。",
"mbti.tag1": "理想主义与道德感",
@@ -120,8 +136,8 @@ class I18nManager {
"stats.exp": "Years Exp",
"stats.repos": "Projects",
"stats.followers": "Followers",
"stats.visitors": "Visitors",
"stats.visitNum": "Visiting guests",
"stats.visitors": "Access User",
"stats.visitNum": "Visitors",
"mbti.name": "Advocate",
"mbti.desc": "Advocates of this personality type are very rare, with less than 1% of the population belonging to this type, but their contributions to the world cannot be ignored.",
"mbti.tag1": "Idealism & Morality",
@@ -239,10 +255,10 @@ class DataManager {
// 创建带超时的fetch函数
async fetchWithTimeout(url, options = {}) {
const { timeout = 5000 } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(url, {
...options,
signal: controller.signal
@@ -268,28 +284,60 @@ class DataManager {
try {
// Parallel Fetch with timeout
const uRes = await this.fetchWithTimeout(`https://api.github.com/users/${user}`, { timeout: 5000 });
const userData = uRes.ok ? await uRes.json() : (window.SiteConfig?.defaults?.user);
let allRepos = [];
let page = 1;
const perPage = 100;
while (page <= 10) { // 最多抓取1000条直到满足条件或为空
const rRes = await this.fetchWithTimeout(`https://api.github.com/users/${user}/repos?sort=stars&per_page=${perPage}&page=${page}`, { timeout: 5000 });
if (!rRes.ok) break;
const repos = await rRes.json();
if (!Array.isArray(repos) || repos.length === 0) break;
allRepos = allRepos.concat(repos);
if (repos.length < perPage || allRepos.length >= 1000) break; // 足量
page++;
let userData, repoData;
try {
const uRes = await this.fetchWithTimeout(`https://api.github.com/users/${user}`, { timeout: 1000 });
if (uRes.ok) {
userData = await uRes.json();
} else {
const fallbackUser = await this.fetchWithTimeout("./data/github_user.json", { timeout: 200 });
userData = await fallbackUser.json();
}
} catch (err) {
// Handle abort errors and other fetch errors
if (err.name === 'AbortError') {
console.warn("GitHub user fetch aborted, using fallback data");
}
const fallbackUser = await this.fetchWithTimeout("./data/github_user.json", { timeout: 200 });
userData = await fallbackUser.json();
}
try {
let allRepos = [];
let page = 1;
const perPage = 100;
while (page <= 50) { // 最多抓取500条直到满足条件或为空
const rRes = await this.fetchWithTimeout(`https://api.github.com/users/${user}/repos?sort=pushed&direction=desc&per_page=${perPage}&page=${page}`, { timeout: 3000 });
if (!rRes.ok) break;
const repos = await rRes.json();
if (!Array.isArray(repos) || repos.length === 0) break;
allRepos = allRepos.concat(repos);
if (repos.length < perPage || allRepos.length >= 500) break; // 足量
page++;
}
if (allRepos.length) {
repoData = allRepos;
} else {
const fallbackRepos = await this.fetchWithTimeout("./data/github_repos.json", { timeout: 300 });
repoData = await fallbackRepos.json();
}
} catch (err) {
// Handle abort errors and other fetch errors
if (err.name === 'AbortError') {
console.warn("GitHub repos fetch aborted, using fallback data");
}
const fallbackRepos = await this.fetchWithTimeout("./data/github_repos.json", { timeout: 300 });
repoData = await fallbackRepos.json();
}
let repoData = allRepos.length ? allRepos : (window.SiteConfig?.defaults?.repos);
// 过滤掉fork项目并按星数排序
if (Array.isArray(repoData)) {
repoData = repoData
.filter(repo => !repo.fork && (repo.stargazers_count > 0 || repo.forks_count > 0))
.sort((a, b) => (b.stargazers_count || 0) - (a.stargazers_count || 0))
.slice(0, 12); // 只取前12
.slice(0, 16); // 只取前16
}
const slimUser = {
@@ -328,22 +376,10 @@ class DataManager {
});
}
formatVisitors() {
const siteVisitorDom = window.document.getElementById('busuanzi_value_site_pv');
if (siteVisitorDom) {
let count = siteVisitorDom.innerText;
if (count && count.length > 3) {
// 格式化 k,w m等单位 支持两位小数点
count = count.replace(/(\d)(?=(\d{3})+$)/g, '$1,');
count = count.replace(/(\d+)(?=(\d{3})+(?:\.\d+)?$)/g, '$0 ');
siteVisitorDom.innerText = count;
}
}
}
renderRepos(list) {
if (!Array.isArray(list)) list = window.SiteConfig?.defaults?.repos;
let html = '';
list.slice(0, 12).forEach(repo => {
list.forEach(repo => {
// Fix: API field compatibility
const stars = repo.stargazers_count !== undefined ? repo.stargazers_count : (repo.stars || 0);
const forks = repo.forks_count !== undefined ? repo.forks_count : (repo.forks || 0);
@@ -363,7 +399,7 @@ class DataManager {
<div class="repo-desc">${escapeHtml(dShort)}</div>
</div>`;
});
// 使用requestAnimationFrame避免强制重排
requestAnimationFrame(() => {
const pc = $('#projects-container');
@@ -480,7 +516,7 @@ class DataManager {
<div class="b-cat">${escapeHtml(cat)}</div>
</div>`;
});
// 使用requestAnimationFrame避免强制重排
requestAnimationFrame(() => {
const bc = $('#blog-container');
@@ -560,37 +596,34 @@ class UIManager {
server: window.SiteConfig.artalk.server,
site: window.SiteConfig.artalk.site,
// 多语言支持
locale: isZh ? 'zh-CN' : 'en-US',
locale: isZh ? 'zh-CN' : 'en',
// 自定义占位符(支持多语言)
placeholder: isZh ? '说点什么吧...支持 Markdown 语法,可 @用户、发送表情' : 'Leave a comment... Supports Markdown, @mentions, and Send 😊',
// 无评论时显示
noComment: isZh ? '快来成为第一个评论的人吧~' : 'Be the first to leave a comment~😍',
// 发送按钮文字(多语言)
sendBtn: isZh ? '发送' : 'Send',
loginBtn: isZh ? '发送' : 'Send',
// 主题支持
darkMode: document.documentElement.getAttribute('data-theme') === 'night',
// 编辑器增强配置
editor: {
// 启用 Markdown
markdown: true,
// 表情面板
emoji: {
// 使用默认表情包
preset: 'twemoji'
},
emoticons: "https://emoticons.hzchu.top/json/artalk/zaoandandandeyouyongquan.json",
// 启用 @ 用户提醒功能
mention: true,
// 自动聚焦(仅桌面端)
autoFocus: !('ontouchstart' in window),
// 限制编辑器高度
maxHeight: 200,
// 工具栏
toolbar: [
'bold', 'italic', 'strike', 'link',
@@ -599,7 +632,7 @@ class UIManager {
'emoji', 'mention'
]
},
// 评论格式化函数
commentFormatter: (comment) => {
// 美化时间显示
@@ -607,22 +640,22 @@ class UIManager {
const date = new Date(dateStr);
const now = new Date();
const diffSec = Math.floor((now - date) / 1000);
if (diffSec < 60) return isZh ? '刚刚' : 'Just now';
if (diffSec < 3600) return isZh ? `${Math.floor(diffSec / 60)}分钟前` : `${Math.floor(diffSec / 60)} minutes ago`;
if (diffSec < 86400) return isZh ? `${Math.floor(diffSec / 3600)}小时前` : `${Math.floor(diffSec / 3600)} hours ago`;
const isToday = date.toDateString() === now.toDateString();
if (isToday) return isZh ?
`今天 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}` :
`Today ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
const isYesterday = new Date(now - 86400000).toDateString() === date.toDateString();
if (isYesterday) return isZh ?
`昨天 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}` :
`Yesterday ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
return date.toLocaleString(isZh ? 'zh-CN' : 'en-US', {
return date.toLocaleString(isZh ? 'zh-CN' : 'en', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
@@ -630,21 +663,21 @@ class UIManager {
minute: '2-digit'
}).replace(/\//g, '-');
};
// 如果是管理员,添加徽章
if (comment.is_admin) {
comment.nick = `👑${comment.nick}`;
}
// 更新显示时间
comment.create_date_formatted = formatTime(comment.date || comment.created_at || comment.create_date);
return comment;
}
};
Artalk.init(artalkConfig);
this.enhanceArtalkUI();
let artalkRef = Artalk.init(artalkConfig);
this.enhanceArtalkUI(artalkRef);
} catch (e) {
console.error("Artalk Error", e);
const msg = isZh ? '当前评论区已关闭' : 'Comments are closed';
@@ -667,29 +700,29 @@ class UIManager {
}
}
// 清空容器
const container = document.getElementById('artalk-container');
if (container) {
container.innerHTML = '';
}
// 重新初始化
this.initArtalk();
}
enhanceArtalkUI() {
enhanceArtalkUI(artalkRef) {
const container = document.getElementById('artalk-container');
if (!container) return;
// 检测是否为移动端
const isMobile = window.matchMedia('(max-width: 768px)').matches;
container.classList.toggle('atk-mobile', isMobile);
container.classList.toggle('atk-desktop', !isMobile);
// 获取当前语言
const lang = getStoredLanguage();
// 获取当前主题
const currentTheme = document.documentElement.getAttribute('data-theme');
@@ -697,19 +730,32 @@ class UIManager {
if (isMobile) {
this.enhanceMobileArtalk(container, lang);
}
// 监听主题/语言变化
const themeObserver = new MutationObserver(() => {
const newTheme = document.documentElement.getAttribute('data-theme');
const newLang = document.documentElement.getAttribute('data-lang');
console.log('Theme/Language changed:', newTheme, newLang);
// 延迟执行
setTimeout(() => {
// 重新加载整个评论组件
this.reloadArtalk();
}, 300);
if (newLang && newLang !== lang) {
// 延迟执行
setTimeout(() => {
// 重新加载整个评论组件
this.reloadArtalk();
}, 300);
} else if (newTheme && newTheme !== currentTheme) {
try {
artalkRef.ui.setDarkMode(newTheme === 'night')
} catch (e) {
setTimeout(() => {
// 重新加载整个评论组件
this.reloadArtalk();
}, 300);
}
}
});
themeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme', 'data-lang']
@@ -723,32 +769,32 @@ class UIManager {
container.querySelectorAll('.atk-comment-wrap .atk-content').forEach(el => {
// 检查是否已经处理过
if (el.dataset.mobileProcessed) return;
// 检查内容是否超过3行才添加展开收起功能
const lineHeight = parseInt(window.getComputedStyle(el).lineHeight);
const paddingTop = parseInt(window.getComputedStyle(el).paddingTop);
const paddingBottom = parseInt(window.getComputedStyle(el).paddingBottom);
const actualHeight = el.clientHeight - paddingTop - paddingBottom;
// 如果内容高度超过3倍行高则添加展开收起功能
if (actualHeight > lineHeight * 3) {
// 添加移动端内容截断
el.classList.add('clamped');
el.style.setProperty('--max-lines', '3');
// 创建展开/收起按钮
const btn = document.createElement('button');
btn.className = 'atk-expand-btn';
const expandText = lang === 'zh' ? '展开' : 'Expand';
const collapseText = lang === 'zh' ? '收起' : 'Collapse';
btn.textContent = expandText;
btn.addEventListener('click', (e) => {
e.stopPropagation();
const isClamped = el.classList.toggle('clamped');
btn.textContent = isClamped ? expandText : collapseText;
});
// 将按钮插入到适当位置
const actionsElement = el.closest('.atk-comment').querySelector('.atk-actions');
if (actionsElement) {
@@ -757,23 +803,23 @@ class UIManager {
el.parentNode.appendChild(btn);
}
}
// 标记为已处理
el.dataset.mobileProcessed = '1';
});
};
// 初始应用
applyMobileStyles();
// 创建观察器以处理动态添加的评论
const observer = new MutationObserver(applyMobileStyles);
observer.observe(container, {
childList: true,
observer.observe(container, {
childList: true,
subtree: true,
attributes: false
});
// 添加移动端特定的样式类
const theme = document.documentElement.getAttribute('data-theme');
container.classList.add(`atk-theme-${theme || 'day'}`);
@@ -788,15 +834,55 @@ class UIManager {
//const name = item.name || '';
//const hash = Array.from(name).reduce((a, c) => a + c.charCodeAt(0), 0);
const gid = Number(item.gradientId) && Number.isFinite(Number(item.gradientId))
? Math.max(1, Math.min(10, Number(item.gradientId)))
: (idx % 10) + 1;
? Math.max(1, Math.min(25, Number(item.gradientId)))
: (idx % 25) + 1;
return {...item, gradientId: gid};
});
const isMobile = window.matchMedia('(max-width: 768px)').matches;
container.innerHTML = '';
const currentState = window.matchMedia('(max-width: 768px)').matches ? 'mobile' : 'desktop';
// 检查是否已保存状态到 sessionStorage
const savedState = sessionStorage.getItem('techCloudState_' + currentState);
if (isMobile) {
if (savedState) {
const parsedState = JSON.parse(savedState);
// 如果当前状态与保存的状态一致,直接使用保存的内容
if (parsedState.type === currentState) {
container.innerHTML = parsedState.html;
if (currentState === 'mobile') {
container.classList.add('mobile-scroll');
}
if (currentState === 'desktop') {
container.classList.remove('mobile-scroll');
// 重新初始化3D球体动画
this.init3DSphereAnimation(container, techStack);
}
// 监听窗口大小变化
this.setupResizeListener(container, techStack, currentState);
return;
}
}
// 清空容器并重新生成
this.generateTechCloud(container, techStack, currentState);
// 监听窗口大小变化
this.setupResizeListener(container, techStack, currentState);
}
// 保存技术标签云状态到 sessionStorage
saveTechCloudState(container, type) {
const state = {
type: type,
html: container.innerHTML
};
sessionStorage.setItem('techCloudState_' + type, JSON.stringify(state));
}
// 生成技术标签云
generateTechCloud(container, techStack, type) {
container.innerHTML = '';
if (type === 'mobile') {
// Mobile: 3-row seamless marquee
container.classList.add('mobile-scroll');
const rows = 3;
@@ -808,7 +894,7 @@ class UIManager {
const appendItem = (rowEl, item, idx) => {
const el = document.createElement('span');
el.className = 'tech-tag-mobile';
const colorClass = `tag-color-${item.gradientId || ((idx % 10) + 1)}`;
const colorClass = `tag-color-${item.gradientId || ((idx % 25) + 1)}`;
el.classList.add(colorClass);
el.innerText = item.name;
el.style.border = 'none';
@@ -826,129 +912,157 @@ class UIManager {
} else {
// PC: 3D Sphere
container.classList.remove('mobile-scroll');
// 使用防抖优化尺寸计算
let resizeTimeout;
const updateContainerSize = () => {
if (resizeTimeout) {
clearTimeout(resizeTimeout);
}
resizeTimeout = setTimeout(() => {
init3DSphere();
}, 100);
};
// 初始化3D球体
const init3DSphere = () => {
// 清除之前的动画
if (container.__animToken) {
cancelAnimationFrame(container.__animToken);
}
// 清空容器
container.innerHTML = '';
const tags = [];
techStack.forEach((item, index) => {
const el = document.createElement('a');
el.className = 'tech-tag-3d';
const colorClass = `tag-color-${item.gradientId || ((index % 10) + 1)}`;
el.classList.add(colorClass);
el.innerText = item.name;
el.style.border = 'none';
container.appendChild(el);
tags.push({el, x: 0, y: 0, z: 0});
});
// 动态半径,避免容器溢出,使用防抖优化
let radius = Math.max(160, Math.min(container.offsetWidth, container.offsetHeight) / 2 - 24);
const dtr = Math.PI / 180;
let lasta = 1, lastb = 1;
let active = false, mouseX = 0, mouseY = 0;
// 初始化位置
tags.forEach((tag, i) => {
let phi = Math.acos(-1 + (2 * i + 1) / tags.length);
let theta = Math.sqrt(tags.length * Math.PI) * phi;
tag.x = radius * Math.cos(theta) * Math.sin(phi);
tag.y = radius * Math.sin(theta) * Math.sin(phi);
tag.z = radius * Math.cos(phi);
});
container.onmouseover = () => active = true;
container.onmouseout = () => active = false;
container.onmousemove = (e) => {
// 使用requestAnimationFrame处理鼠标移动事件避免强制重排
requestAnimationFrame(() => {
let rect = container.getBoundingClientRect();
mouseX = (e.clientX - (rect.left + rect.width / 2)) / 5;
mouseY = (e.clientY - (rect.top + rect.height / 2)) / 5;
});
};
const update = () => {
let a, b;
if (active) {
a = (-Math.min(Math.max(-mouseY, -200), 200) / radius) * 2;
b = (Math.min(Math.max(-mouseX, -200), 200) / radius) * 2;
} else {
a = lasta * 0.98; // Auto rotate
b = lastb * 0.98;
}
lasta = a;
lastb = b;
if (Math.abs(a) <= 0.01 && Math.abs(b) <= 0.01 && !active) a = 0.5; // Keep spinning slowly
let sa = Math.sin(a * dtr), ca = Math.cos(a * dtr);
let sb = Math.sin(b * dtr), cb = Math.cos(b * dtr);
// 批量更新样式以减少重排
// 先收集所有需要更新的样式信息
const updates = [];
tags.forEach(tag => {
let rx1 = tag.x, ry1 = tag.y * ca - tag.z * sa, rz1 = tag.y * sa + tag.z * ca;
let ry2 = ry1, rz2 = rx1 * -sb + rz1 * cb;
tag.x = rx1 * cb + rz1 * sb;
tag.y = ry2;
tag.z = rz2;
let scale = (tag.z + radius) / (2 * radius) + 0.45;
scale = Math.min(Math.max(scale, 0.7), 1.15);
const opacity = (tag.z + radius) / (2 * radius) + 0.2;
const zIndex = parseInt(scale * 100);
const left = tag.x + container.offsetWidth / 2 - tag.el.offsetWidth / 2;
const top = tag.y + container.offsetHeight / 2 - tag.el.offsetHeight / 2;
updates.push({
el: tag.el,
transform: `translate(${left}px, ${top}px) scale(${scale})`,
opacity: opacity,
zIndex: zIndex
});
});
updates.forEach(update => {
update.el.style.transform = update.transform;
update.el.style.opacity = update.opacity;
update.el.style.zIndex = update.zIndex;
});
container.__animToken = requestAnimationFrame(update);
};
container.__animToken = requestAnimationFrame(update);
};
// 初始化3D球体
init3DSphere();
// 监听窗口大小变化
window.addEventListener('resize', updateContainerSize);
this.init3DSphereAnimation(container, techStack);
}
// 保存当前状态
this.saveTechCloudState(container, type);
}
// 初始化3D球体动画
init3DSphereAnimation(container, techStack) {
// 清除之前的动画
if (container.__animToken) {
cancelAnimationFrame(container.__animToken);
}
// 清空容器
container.innerHTML = '';
const tags = [];
techStack.forEach((item, index) => {
const el = document.createElement('a');
el.className = 'tech-tag-3d';
const colorClass = `tag-color-${item.gradientId || ((index % 25) + 1)}`;
el.classList.add(colorClass);
el.innerText = item.name;
el.style.border = 'none';
container.appendChild(el);
tags.push({el, x: 0, y: 0, z: 0});
});
// 动态半径,避免容器溢出,使用防抖优化
let radius = Math.max(160, Math.min(container.offsetWidth, container.offsetHeight) / 2 - 24);
const dtr = Math.PI / 180;
let lasta = 1, lastb = 1;
let active = false, mouseX = 0, mouseY = 0;
// 初始化位置
tags.forEach((tag, i) => {
let phi = Math.acos(-1 + (2 * i + 1) / tags.length);
let theta = Math.sqrt(tags.length * Math.PI) * phi;
tag.x = radius * Math.cos(theta) * Math.sin(phi);
tag.y = radius * Math.sin(theta) * Math.sin(phi);
tag.z = radius * Math.cos(phi);
});
container.onmouseover = () => active = true;
container.onmouseout = () => active = false;
container.onmousemove = (e) => {
// 使用requestAnimationFrame处理鼠标移动事件避免强制重排
requestAnimationFrame(() => {
let rect = container.getBoundingClientRect();
mouseX = (e.clientX - (rect.left + rect.width / 2)) / 5;
mouseY = (e.clientY - (rect.top + rect.height / 2)) / 5;
});
};
const update = () => {
let a, b;
if (active) {
a = (-Math.min(Math.max(-mouseY, -200), 200) / radius) * 2;
b = (Math.min(Math.max(-mouseX, -200), 200) / radius) * 2;
} else {
a = lasta * 0.98; // Auto rotate
b = lastb * 0.98;
}
lasta = a;
lastb = b;
if (Math.abs(a) <= 0.01 && Math.abs(b) <= 0.01 && !active) a = 0.5; // Keep spinning slowly
let sa = Math.sin(a * dtr), ca = Math.cos(a * dtr);
let sb = Math.sin(b * dtr), cb = Math.cos(b * dtr);
// 批量更新样式以减少重排
// 先收集所有需要更新的样式信息
const updates = [];
tags.forEach(tag => {
let rx1 = tag.x, ry1 = tag.y * ca - tag.z * sa, rz1 = tag.y * sa + tag.z * ca;
let ry2 = ry1, rz2 = rx1 * -sb + rz1 * cb;
tag.x = rx1 * cb + rz1 * sb;
tag.y = ry2;
tag.z = rz2;
let scale = (tag.z + radius) / (2 * radius) + 0.45;
scale = Math.min(Math.max(scale, 0.7), 1.15);
const opacity = (tag.z + radius) / (2 * radius) + 0.2;
const zIndex = parseInt(scale * 100);
const left = tag.x + container.offsetWidth / 2 - tag.el.offsetWidth / 2;
const top = tag.y + container.offsetHeight / 2 - tag.el.offsetHeight / 2;
updates.push({
el: tag.el,
transform: `translate(${left}px, ${top}px) scale(${scale})`,
opacity: opacity,
zIndex: zIndex
});
});
updates.forEach(update => {
update.el.style.transform = update.transform;
update.el.style.opacity = update.opacity;
update.el.style.zIndex = update.zIndex;
});
container.__animToken = requestAnimationFrame(update);
};
container.__animToken = requestAnimationFrame(update);
}
// 设置窗口大小变化监听器
setupResizeListener(container, techStack, currentType) {
// 使用防抖优化尺寸计算
let resizeTimeout;
let windowRef = currentType;
const handleResize = () => {
if (resizeTimeout) {
clearTimeout(resizeTimeout);
}
resizeTimeout = setTimeout(() => {
const isMobile = window.matchMedia('(max-width: 768px)').matches;
const currentState = isMobile ? 'mobile' : 'desktop';
if (windowRef === currentState) {
return
}
windowRef = currentState;
// 检查 sessionStorage 中是否有对应状态的内容
const savedState = sessionStorage.getItem('techCloudState_' + currentState);
if (savedState) {
// 直接使用保存的内容
const parsedState = JSON.parse(savedState);
container.innerHTML = parsedState.html;
if (currentState === 'mobile') {
container.classList.add('mobile-scroll');
}
if (currentState === 'desktop') {
container.classList.remove('mobile-scroll');
this.init3DSphereAnimation(container, techStack);
}
} else {
// 重新生成
container.innerHTML = '';
this.generateTechCloud(container, techStack, currentState);
}
}, 100);
};
// 监听窗口大小变化
window.addEventListener('resize', handleResize);
}
initFab() {
const main = document.getElementById('fab-main');
@@ -957,10 +1071,8 @@ class UIManager {
const fTheme = document.getElementById('fab-theme');
const fMusic = document.getElementById('fab-music');
if (!main || !menu || !fLang || !fTheme || !fMusic) return;
// 添加拖拽功能
this.initDraggableFab();
const updateLabels = () => {
// 使用requestAnimationFrame避免强制重排
requestAnimationFrame(() => {
@@ -1008,73 +1120,6 @@ class UIManager {
requestAnimationFrame(updateLabels);
}
// 初始化拖拽功能
initDraggableFab() {
const fab = document.querySelector('.mobile-fab');
if (!fab) return;
let isDragging = false;
let initialX, initialY, currentX, currentY, xOffset = 0, yOffset = 0;
fab.style.willChange = 'transform';
// 拖拽相关方法
const setTranslate = (xPos, yPos, el) => {
el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
};
const dragStart = (e) => {
if (e.type === 'touchstart') {
initialX = e.touches[0].clientX - xOffset;
initialY = e.touches[0].clientY - yOffset;
} else {
initialX = e.clientX - xOffset;
initialY = e.clientY - yOffset;
}
isDragging = true;
};
const dragEnd = () => {
initialX = currentX;
initialY = currentY;
isDragging = false;
};
const drag = (e) => {
if (isDragging) {
e.preventDefault();
if (e.type === 'touchmove') {
currentX = e.touches[0].clientX - initialX;
currentY = e.touches[0].clientY - initialY;
} else {
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
}
// 使用requestAnimationFrame优化拖拽动画
requestAnimationFrame(() => {
const ww = window.innerWidth;
const wh = window.innerHeight;
const rect = fab.getBoundingClientRect();
const fw = rect.width;
const fh = rect.height;
currentX = Math.max(0, Math.min(currentX, ww - fw));
currentY = Math.max(0, Math.min(currentY, wh - fh));
xOffset = currentX;
yOffset = currentY;
setTranslate(currentX, currentY, fab);
});
}
};
// 绑定事件
fab.addEventListener('touchstart', dragStart, { passive: false });
fab.addEventListener('touchend', dragEnd, { passive: false });
fab.addEventListener('touchmove', drag, { passive: false });
fab.addEventListener('mousedown', dragStart, { passive: false });
fab.addEventListener('mouseup', dragEnd, { passive: false });
fab.addEventListener('mousemove', drag, { passive: false });
}
initAudio() {
const el = document.getElementById('site-audio');
@@ -1082,23 +1127,50 @@ class UIManager {
this.audio = el;
this.audio.loop = true;
// 检查是否在24小时内用户暂停过音乐
const shouldRemainPaused = this.shouldMusicRemainPaused();
const tryPlay = () => {
// 页面加载完成后根据条件决定是否播放
window.addEventListener('load', () => {
// 检查是否在24小时内用户暂停过音乐
const shouldRemainPaused = this.shouldMusicRemainPaused();
// 如果不应该保持暂停状态,则尝试播放
if (!shouldRemainPaused) {
let userInteracted = true;
this.audio.play().catch(() => {
// 静默处理播放失败
userInteracted = false;
});
// 添加用户交互检查,避免浏览器阻止自动播放
const attemptAutoplay = () => {
// 检查是否已有用户交互
if (this.userInteracted === false) {
// 添加一次性用户交互监听器
const enableAudio = () => {
this.userInteracted = true;
setTimeout(() => {
this.audio.play().catch(() => {
});
}, 1000);
document.removeEventListener('click', enableAudio);
document.removeEventListener('touchstart', enableAudio);
document.removeEventListener('keydown', enableAudio);
document.removeEventListener('mousemove', enableAudio);
};
document.addEventListener('click', enableAudio, { once: true });
document.addEventListener('touchstart', enableAudio, { once: true });
document.addEventListener('keydown', enableAudio, { once: true });
document.addEventListener('mousemove', enableAudio, { once: true });
}
};
requestAnimationFrame(attemptAutoplay);
}
};
tryPlay();
});
}
updateCustomStyles(container, theme) {
// 确保容器具有正确的主题类
container.classList.remove('atk-theme-day', 'atk-theme-night');
container.classList.add(`atk-theme-${theme || 'day'}`);
// 更新自定义元素的主题样式
const customElements = container.querySelectorAll('.atk-expand-btn, .atk-pagination .atk-page-item');
customElements.forEach(el => {
@@ -1107,4 +1179,4 @@ class UIManager {
});
}
}
}

View File

@@ -1,202 +1,181 @@
// 配置文件 - 提取自各个JavaScript文件的关键配置
// 创建日期: 2025-11-20
const SiteConfig = {
// bj.js 配置
stars: {
count: 300,
refreshInterval: 50
},
(function() {
const SiteConfig = {
// bj.js 配置
stars: {
count: 300,
refreshInterval: 50
},
// main.js 配置
animation: {
elementUp: {
delay: 0,
increment: 150
}
},
background: {
imagePaths: [
"/images/bj/1.jpg",
"/images/bj/2.jpg",
"/images/bj/3.jpg",
"/images/bj/4.jpg",
"/images/bj/5.jpg",
"/images/bj/6.jpg",
"/images/bj/7.jpg"
]
},
hitokoto: {
apiUrl: 'https://v1.hitokoto.cn?c=c&c=d&c=i&c=k'
},
// main.js 配置
animation: {
elementUp: {
delay: 0,
increment: 150
}
},
// about.js 配置
github: {
username: 'listener-He'
},
blog: {
rssUrl: 'https://blog.hehouhui.cn/api/rss'
},
background: {
imagePaths: [
"/images/bj/1.webp",
"/images/bj/2.webp",
"/images/bj/3.webp",
"/images/bj/4.webp",
"/images/bj/5.webp",
"/images/bj/6.webp",
"/images/bj/7.webp"
]
},
// 通用缓存键与TTL毫秒
cacheKeys: {
github: { key: 'gh_data_v2', ttlMs: 36000000 },
blog: { key: 'blog_data_v2', ttlMs: 3600000 },
theme: { key: 'theme_v2', ttlMs: 3600000 }
},
techStack: [
{ name: 'Java', category: 'core', weight: 5 },
{ name: 'Spring Boot', category: 'backend', weight: 5 },
{ name: 'JavaScript', category: 'frontend', weight: 5 },
{ name: 'Python', category: 'core', weight: 4 },
{ name: 'WebFlux', category: 'backend', weight: 5 },
{ name: 'Reactor', category: 'backend', weight: 5 },
{ name: 'TypeScript', category: 'frontend', weight: 4 },
{ name: 'Spring Cloud', category: 'backend', weight: 4 },
{ name: 'Go', category: 'core', weight: 3 },
{ name: 'MySQL', category: 'data', weight: 4 },
{ name: 'Redis', category: 'data', weight: 4 },
{ name: 'MongoDB', category: 'data', weight: 3 },
{ name: 'Docker', category: 'ops', weight: 4 },
{ name: 'Kubernetes', category: 'ops', weight: 3 },
{ name: 'OpenAI API', category: 'ai', weight: 3 },
{ name: 'LangChain', category: 'ai', weight: 3 },
{ name: 'TensorFlow', category: 'ai', weight: 2 },
{ name: 'PyTorch', category: 'ai', weight: 2 },
{ name: 'Elasticsearch', category: 'data', weight: 3 },
{ name: 'RabbitMQ', category: 'data', weight: 2 },
{ name: 'RocketMQ', category: 'data', weight: 2 },
{ name: 'Kafka', category: 'data', weight: 2 },
{ name: 'Jenkins', category: 'ops', weight: 3 },
{ name: 'Git', category: 'ops', weight: 4 },
{ name: 'Linux', category: 'ops', weight: 3 },
{ name: 'AWS', category: 'ops', weight: 2 },
{ name: 'Nginx', category: 'ops', weight: 2 },
{ name: 'Spring Security', category: 'backend', weight: 3 },
{ name: 'MyBatis', category: 'backend', weight: 3 },
{ name: 'JPA', category: 'backend', weight: 2 },
{ name: 'Dubbo', category: 'backend', weight: 2 },
{ name: 'Netty', category: 'backend', weight: 2 },
{ name: 'Transformers', category: 'ai', weight: 2 },
{ name: 'Scikit-learn', category: 'ai', weight: 2 },
{ name: 'Ollama', category: 'ai', weight: 1 },
{ name: 'Dify', category: 'ai', weight: 1 },
{ name: 'Spring AI', category: 'ai', weight: 1 },
{ name: 'ClickHouse', category: 'data', weight: 1 },
{ name: 'Postgresql', category: 'data', weight: 1 },
{ name: "Hexo", category: "frontend", weight: 5},
{ name: "NextJs", category: "frontend", weight: 1},
{ name: "HuggingFace", category: "ai", weight: 1},
{ name: "Vue", category: "frontend", weight: 3},
{ name: "React", category: "frontend", weight: 1},
{ name: "R2dbc", category: "data", weight: 1},
{ name: "Proto", category: "core", weight: 1},
{ name: "Mqtt", category: "core", weight: 2},
{ name: "Grpc", category: "core", weight: 1},
{ name: "Figma", category: "frontend", weight: 1}
],
hitokoto: {
apiUrl: 'https://v1.hitokoto.cn?c=c&c=d&c=i&c=k'
},
// about.js 配置
github: {
username: 'listener-He'
},
// 默认数据当API或RSS不可用时使用
defaults: {
repos: [
{name: "yunxiao-LLM-reviewer", desc: "AI Code Reviewer based on LLM", stars: 9, url: "#"},
{name: "hexo-theme-stellar", desc: "Comprehensive Hexo theme", stars: 5, url: "#"},
{name: "Universal-IoT-Java", desc: "IoT Platform Demo", stars: 2, url: "#"}
blog: {
rssUrl: 'https://blog.hehouhui.cn/api/rss'
},
// 通用缓存键与TTL毫秒
cacheKeys: {
github: { key: 'gh_data', ttlMs: 36000000 },
blog: { key: 'blog_data', ttlMs: 3600000 },
theme: { key: 'theme', ttlMs: 3600000 }
},
techStack: [
{ name: 'Java', category: 'core', weight: 5 },
{ name: 'Spring Boot', category: 'backend', weight: 5 },
{ name: 'JavaScript', category: 'frontend', weight: 5 },
{ name: 'Python', category: 'core', weight: 4 },
{ name: 'WebFlux', category: 'backend', weight: 5 },
{ name: 'Reactor', category: 'backend', weight: 5 },
{ name: 'TypeScript', category: 'frontend', weight: 4 },
{ name: 'Spring Cloud', category: 'backend', weight: 4 },
{ name: 'Go', category: 'core', weight: 3 },
{ name: 'MySQL', category: 'data', weight: 4 },
{ name: 'Redis', category: 'data', weight: 4 },
{ name: 'MongoDB', category: 'data', weight: 3 },
{ name: 'Docker', category: 'ops', weight: 4 },
{ name: 'Kubernetes', category: 'ops', weight: 3 },
{ name: 'OpenAI API', category: 'ai', weight: 3 },
{ name: 'LangChain', category: 'ai', weight: 3 },
{ name: 'TensorFlow', category: 'ai', weight: 2 },
{ name: 'PyTorch', category: 'ai', weight: 2 },
{ name: 'Elasticsearch', category: 'data', weight: 3 },
{ name: 'RabbitMQ', category: 'data', weight: 2 },
{ name: 'RocketMQ', category: 'data', weight: 2 },
{ name: 'Kafka', category: 'data', weight: 2 },
{ name: 'Jenkins', category: 'ops', weight: 3 },
{ name: 'Git', category: 'ops', weight: 4 },
{ name: 'Linux', category: 'ops', weight: 3 },
{ name: 'AWS', category: 'ops', weight: 2 },
{ name: 'Nginx', category: 'ops', weight: 2 },
{ name: 'Spring Security', category: 'backend', weight: 3 },
{ name: 'MyBatis', category: 'backend', weight: 3 },
{ name: 'JPA', category: 'backend', weight: 2 },
{ name: 'Dubbo', category: 'backend', weight: 2 },
{ name: 'Netty', category: 'backend', weight: 2 },
{ name: 'Transformers', category: 'ai', weight: 2 },
{ name: 'Scikit-learn', category: 'ai', weight: 2 },
{ name: 'Ollama', category: 'ai', weight: 1 },
{ name: 'Dify', category: 'ai', weight: 1 },
{ name: 'Spring AI', category: 'ai', weight: 1 },
{ name: 'ClickHouse', category: 'data', weight: 1 },
{ name: 'Postgresql', category: 'data', weight: 1 },
{ name: "Hexo", category: "frontend", weight: 5},
{ name: "NextJs", category: "frontend", weight: 1},
{ name: "HuggingFace", category: "ai", weight: 1},
{ name: "Vue", category: "frontend", weight: 3},
{ name: "React", category: "frontend", weight: 1},
{ name: "R2dbc", category: "data", weight: 1},
{ name: "Proto", category: "core", weight: 1},
{ name: "Mqtt", category: "core", weight: 2},
{ name: "Grpc", category: "core", weight: 1},
{ name: "Figma", category: "frontend", weight: 1}
],
posts: [
{title: "Vector Database Guide", date: "2025-01-02", cat: "Tech", url: "#"},
{title: "Spring Boot 3.0 Features", date: "2024-12-30", cat: "Java", url: "#"},
{title: "Microservices Patterns", date: "2024-12-28", cat: "Arch", url: "#"}
],
user: { repos: 165, followers: 6, created: "2018-05-14" }
},
socialCards: {
rings: [130, 180, 230],
goldenAngle: 137.5,
baseSpeed: 16
},
artalk: {
server: 'https://artalk.hehouhui.cn',
site: 'Honesty的主页',
placeholder: '来说点什么吧...',
noComment: '暂无评论',
sendBtn: '发送'
},
// 站点统计配置
analytics: {
busuanzi: {
src: 'https://events.vercount.one/js',
site_pv_id: 'busuanzi_value_site_pv',
site_uv_id: 'busuanzi_value_site_uv',
formatter: true
},
baidu: {
src: 'https://hm.baidu.com/hm.js?ae2a009a75b13c21d5121ee51375ea4e',
id: 'ae2a009a75b13c21d5121ee51375ea4e'
},
google: {
src: 'https://www.googletagmanager.com/gtag/js',
id: 'G-DYWDEVKDP0'
},
tencent: {
src: 'https://sdk.51.la/js-sdk-pro.min.js',
id: '3OBGjwDdEIRS7XZ1',
ck: '3OBGjwDdEIRS7XZ1'
}
},
animationSettings: {
observerOptions: {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
},
itemObserverOptions: {
threshold: 0.15,
rootMargin: '0px 0px -20px 0px'
}
},
// 开发环境配置
dev: {
isLocal: (typeof location !== 'undefined') ? (location.hostname.indexOf('localhost') > -1 || location.hostname.indexOf('127.0.0.1') > -1) : false
}
};
if (Array.isArray(SiteConfig.techStack)) {
const categoryGradientMap = {
core: 7,
backend: 4,
data: 9,
ops: 10,
ai: 3
// 默认数据当API或RSS不可用时使用
defaults: {
repos: [
{name: "yunxiao-LLM-reviewer", desc: "AI Code Reviewer based on LLM", stars: 9, url: "#"},
{name: "hexo-theme-stellar", desc: "Comprehensive Hexo theme", stars: 5, url: "#"},
{name: "Universal-IoT-Java", desc: "IoT Platform Demo", stars: 2, url: "#"}
],
posts: [
{title: "Vector Database Guide", date: "2025-01-02", cat: "Tech", url: "#"},
{title: "Spring Boot 3.0 Features", date: "2024-12-30", cat: "Java", url: "#"},
{title: "Microservices Patterns", date: "2024-12-28", cat: "Arch", url: "#"}
],
user: { repos: 165, followers: 6, created: "2018-05-14" }
},
socialCards: {
rings: [130, 180, 230],
goldenAngle: 137.5,
baseSpeed: 16
},
artalk: {
server: 'https://artalk.hehouhui.cn',
site: 'Honesty的主页',
placeholder: '来说点什么吧...',
noComment: '暂无评论',
sendBtn: '发送'
},
// 站点统计配置
analytics: {
busuanzi: {
src: '//cdn.busuanzi.cc/busuanzi/3.6.9/busuanzi.abbr.min.js',
site_pv_id: 'busuanzi_site_pv',
site_uv_id: 'busuanzi_site_uv',
formatter: true
},
baidu: {
src: 'https://hm.baidu.com/hm.js?ae2a009a75b13c21d5121ee51375ea4e',
id: 'ae2a009a75b13c21d5121ee51375ea4e'
},
google: {
src: 'https://www.googletagmanager.com/gtag/js',
id: 'G-DYWDEVKDP0'
},
tencent: {
src: 'https://sdk.51.la/js-sdk-pro.min.js',
id: '3OBGjwDdEIRS7XZ1',
ck: '3OBGjwDdEIRS7XZ1'
}
},
animationSettings: {
observerOptions: {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
},
itemObserverOptions: {
threshold: 0.15,
rootMargin: '0px 0px -20px 0px'
}
},
// 开发环境配置
dev: {
isLocal: (typeof location !== 'undefined') ? (location.hostname.indexOf('localhost') > -1 || location.hostname.indexOf('127.0.0.1') > -1) : false
}
};
const vividSet = [1, 4, 7, 8];
SiteConfig.techStack = SiteConfig.techStack.map((item) => {
const name = item.name || '';
const hash = Array.from(name).reduce((a, c) => a + c.charCodeAt(0), 0);
if (item.gradientId && Number.isFinite(Number(item.gradientId))) {
return { ...item, gradientId: Math.max(1, Math.min(10, Number(item.gradientId))) };
}
let base = categoryGradientMap[item.category] || ((hash % 10) + 1);
if (Number(item.weight) >= 5) {
base = vividSet[hash % vividSet.length];
}
return { ...item, gradientId: base };
});
}
// 导出配置
if (typeof module !== 'undefined' && module.exports) {
module.exports = SiteConfig;
} else if (typeof window !== 'undefined') {
window.SiteConfig = SiteConfig;
}
// 导出配置
if (typeof module !== 'undefined' && module.exports) {
module.exports = SiteConfig;
} else if (typeof window !== 'undefined') {
window.SiteConfig = SiteConfig;
}
})();

View File

@@ -49,18 +49,16 @@ $(document).ready(function () {
/**
* 自定义壁纸
*/
var imgUrls = JSON.parse(sessionStorage.getItem("imgUrls"));
var index = sessionStorage.getItem("index");
var imgUrls = null; //JSON.parse(sessionStorage.getItem("imgUrls"));
var $panel = $('#panel');
var date = new Date();
var dayOfWeek = date.getDay();
if (imgUrls == null) {
imgUrls = [];
index = 0;
SiteConfig.background.imagePaths.forEach(path => {
imgUrls.push(path);
});
sessionStorage.setItem("imgUrls", JSON.stringify(imgUrls));
//sessionStorage.setItem("imgUrls", JSON.stringify(imgUrls));
// sessionStorage.setItem("index", index);
} else {
// if (index == imgUrls.length)

View File

@@ -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); // 等待动画结束后隐藏
});
});

View File

@@ -1,93 +0,0 @@
// Service Worker for PWA
const CACHE_NAME = 'honesty-home-v1.0.1';
const urlsToCache = [
'./index.html',
'./about.html',
'./css/style.css',
'./css/about.css',
'./css/artalk.css',
'./js/config.js',
'./js/main.js',
'./js/about.js',
'./images/avatar.jpeg',
'./images/favicon.ico',
'./images/favicon.png',
'./images/logo.png',
'./images/INFJ.png',
'./images/kl.gif',
'./images/bj/1.jpg',
'./images/bj/2.jpg',
'./images/bj/3.jpg',
'./images/bj/4.jpg',
'./images/bj/5.jpg',
'./images/bj/6.jpg',
'./images/bj/7.jpg',
];
// 安装事件 - 缓存资源
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
// 获取事件 - 拦截网络请求
self.addEventListener('fetch', event => {
// 对于非GET请求或者不是同源请求直接跳过
if (event.request.method !== 'GET' || !event.request.url.startsWith(self.location.origin)) {
return;
}
event.respondWith(
caches.match(event.request)
.then(response => {
// 如果在缓存中找到响应,则返回缓存的资源
if (response) {
return response;
}
// 克隆请求,因为请求是一个流,只能被消费一次
const fetchRequest = event.request.clone();
// 如果没有在缓存中找到,则发起网络请求
return fetch(fetchRequest).then(response => {
// 检查响应是否有效
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// 克隆响应,因为响应是一个流,只能被消费一次
const responseToCache = response.clone();
// 打开缓存并将响应添加到缓存中
caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseToCache);
});
return response;
});
})
);
});
// 激活事件 - 清理旧缓存
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});

620
me.html Normal file
View File

@@ -0,0 +1,620 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>ETHEREAL | HAND PHYSICS</title>
<style>
:root {
--bg-color: #020202;
--ui-font: 'Helvetica Neue', 'Arial', sans-serif;
--serif-font: 'Times New Roman', serif;
}
body {
margin: 0;
overflow: hidden;
background-color: var(--bg-color);
font-family: var(--ui-font);
cursor: none; /* 隐藏鼠标 */
}
#canvas-container {
position: fixed;
inset: 0;
z-index: 1;
}
#input-video { display: none; }
/* 极简主义 UI */
#ui-layer {
position: fixed;
inset: 0;
z-index: 10;
pointer-events: none;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 40px;
mix-blend-mode: exclusion; /* 高级混合模式 */
}
.top-bar {
display: flex;
justify-content: space-between;
text-transform: uppercase;
font-size: 10px;
letter-spacing: 2px;
color: #fff;
opacity: 0.7;
}
.center-stage {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
width: 100%;
}
.art-title {
font-family: var(--serif-font);
font-size: 48px;
font-weight: 100;
letter-spacing: 12px;
color: #fff;
opacity: 0;
transition: opacity 2s ease;
}
.art-sub {
font-size: 10px;
letter-spacing: 6px;
color: #fff;
margin-top: 15px;
opacity: 0;
transition: opacity 2s 0.5s ease;
}
.visible { opacity: 1 !important; }
.debug-info {
position: absolute;
bottom: 40px;
left: 40px;
font-family: monospace;
font-size: 10px;
color: rgba(255,255,255,0.4);
line-height: 1.5;
}
/* 加载器 */
#loader {
position: fixed;
inset: 0;
background: #000;
z-index: 100;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 1s;
}
.loader-line {
width: 0%;
height: 1px;
background: #fff;
transition: width 0.5s;
}
</style>
<!-- 核心库 -->
<script src="https://cdn.bootcdn.net/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/EffectComposer.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/RenderPass.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/ShaderPass.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/UnrealBloomPass.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/CopyShader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/LuminosityHighPassShader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/simplex-noise@2.4.0/simplex-noise.min.js"></script>
<!-- AI 视觉库 -->
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/control_utils/control_utils.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js" crossorigin="anonymous"></script>
</head>
<body>
<div id="loader"><div class="loader-line" id="loader-bar"></div></div>
<div id="canvas-container"></div>
<video id="input-video" playsinline></video>
<div id="ui-layer">
<div class="top-bar">
<span id="fps-display">FPS: --</span>
<span id="theme-display">CALIBRATING REALITY...</span>
</div>
<div class="center-stage">
<div class="art-title" id="title">VOID</div>
<div class="art-sub" id="subtitle">INTERACTIVE FLUID DYNAMICS</div>
</div>
<div class="debug-info" id="hand-state">
HANDS: SEARCHING<br>
FORCE: 0.00
</div>
</div>
<script>
/**
* ============================================================================
* 1. 数学工具与配置 (Math & Config)
* ============================================================================
*/
const CONFIG = {
particleCount: 8000, // 粒子数量,适中以保证物理计算性能
particleSize: 1.8, // 基础大小
bloomStrength: 1.2, // 辉光强度
handForceRadius: 15.0, // 手掌影响半径
friction: 0.96, // 物理摩擦力 (越小停得越快)
returnSpeed: 0.008, // 回归原位的速度 (弹性)
noiseScale: 0.02, // 噪声纹理缩放
curlStrength: 0.5 // 旋度强度 (流体感)
};
const Simplex = new SimplexNoise();
// 颜色主题定义 (Palette)
const THEMES = {
DEFAULT: { name: 'VOID / 虚空', colors: [0x888888, 0xffffff, 0x444444] },
SPRING: { name: 'FLORA / 生机', colors: [0xff3366, 0xffdd00, 0x00ff88] },
SUMMER: { name: 'OCEAN / 碧海', colors: [0x00ffff, 0x0066ff, 0xffffff] },
AUTUMN: { name: 'EMBER / 余烬', colors: [0xff4400, 0xffaa00, 0x330000] },
WINTER: { name: 'FROST / 霜雪', colors: [0xaaccff, 0xffffff, 0x8899aa] },
LOVE: { name: 'PULSE / 悸动', colors: [0xff0044, 0xff88aa, 0x440011] },
SPIRIT: { name: 'SOUL / 灵光', colors: [0x00ffcc, 0xaa00ff, 0x0000ff] }
};
// 自动主题选择器
function getTheme() {
const m = new Date().getMonth() + 1;
const d = new Date().getDate();
if (m===2 && d===14) return THEMES.LOVE;
if (m===10 && d===31) return THEMES.SPIRIT;
if (m>=3 && m<=5) return THEMES.SPRING;
if (m>=6 && m<=8) return THEMES.SUMMER;
if (m>=9 && m<=11) return THEMES.AUTUMN;
return THEMES.WINTER;
}
const ACTIVE_THEME = getTheme();
/**
* ============================================================================
* 2. 物理核心 (Physics Core) - 重写为基于力的系统
* ============================================================================
*/
class PhysicsSystem {
constructor(scene) {
this.count = CONFIG.particleCount;
this.geometry = new THREE.BufferGeometry();
// 双重缓冲数据Current(当前), Target(回归目标), Velocity(速度)
this.positions = new Float32Array(this.count * 3);
this.origins = new Float32Array(this.count * 3); // 原始位置(用于回归)
this.velocities = new Float32Array(this.count * 3);
this.colors = new Float32Array(this.count * 3);
this.sizes = new Float32Array(this.count);
this.life = new Float32Array(this.count); // 粒子生命周期/闪烁偏移
this.initParticles();
// ShaderMaterial 提供高性能渲染和柔和的光点
this.material = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 },
pixelRatio: { value: window.devicePixelRatio },
baseSize: { value: CONFIG.particleSize }
},
vertexShader: `
attribute float size;
attribute vec3 color;
attribute float life;
varying vec3 vColor;
varying float vAlpha;
uniform float time;
uniform float pixelRatio;
uniform float baseSize;
void main() {
vColor = color;
// 粒子呼吸效果
float breath = 0.6 + 0.4 * sin(time * 2.0 + life * 10.0);
vAlpha = 0.5 + 0.5 * breath;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = baseSize * size * breath * pixelRatio * (300.0 / -mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
varying vec3 vColor;
varying float vAlpha;
void main() {
// 圆形绘制,边缘羽化
vec2 coord = gl_PointCoord - vec2(0.5);
float r = length(coord);
if (r > 0.5) discard;
// 核心亮,边缘暗
float glow = 1.0 - (r * 2.0);
glow = pow(glow, 1.5);
gl_FragColor = vec4(vColor, glow * vAlpha);
}
`,
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending
});
this.mesh = new THREE.Points(this.geometry, this.material);
scene.add(this.mesh);
}
initParticles() {
// 创建一个无序但均匀的云团
const c1 = new THREE.Color(ACTIVE_THEME.colors[0]);
const c2 = new THREE.Color(ACTIVE_THEME.colors[1]);
const c3 = new THREE.Color(ACTIVE_THEME.colors[2]);
for(let i=0; i<this.count; i++) {
const i3 = i * 3;
// 随机分布在一个宽阔的区域
const x = (Math.random() - 0.5) * 200;
const y = (Math.random() - 0.5) * 120;
const z = (Math.random() - 0.5) * 80;
this.positions[i3] = x; this.positions[i3+1] = y; this.positions[i3+2] = z;
this.origins[i3] = x; this.origins[i3+1] = y; this.origins[i3+2] = z;
// 颜色混合
const rand = Math.random();
let c;
if(rand < 0.33) c = c1;
else if(rand < 0.66) c = c2;
else c = c3;
this.colors[i3] = c.r; this.colors[i3+1] = c.g; this.colors[i3+2] = c.b;
this.sizes[i] = Math.random() * 1.5 + 0.5;
this.life[i] = Math.random();
}
this.geometry.setAttribute('position', new THREE.BufferAttribute(this.positions, 3));
this.geometry.setAttribute('color', new THREE.BufferAttribute(this.colors, 3));
this.geometry.setAttribute('size', new THREE.BufferAttribute(this.sizes, 1));
this.geometry.setAttribute('life', new THREE.BufferAttribute(this.life, 1));
}
// ★★★ 核心物理更新逻辑 ★★★
update(time, handData) {
this.material.uniforms.time.value = time;
// 获取手部数据 (如果没有手,数组为空)
const hands = handData.hands || [];
for(let i=0; i<this.count; i++) {
const i3 = i * 3;
const px = this.positions[i3];
const py = this.positions[i3+1];
const pz = this.positions[i3+2];
// 1. Curl Noise (旋度噪声) - 让粒子自然流动的关键
// 计算噪声场对速度的影响,模拟空气流动
const noiseScale = 0.015;
const n1 = Simplex.noise3D(px*noiseScale, py*noiseScale, time*0.2);
const n2 = Simplex.noise3D(px*noiseScale + 100, py*noiseScale, time*0.2);
const n3 = Simplex.noise3D(px*noiseScale + 200, py*noiseScale, time*0.2);
// 施加环境流动力
this.velocities[i3] += n1 * 0.02;
this.velocities[i3+1] += n2 * 0.02;
this.velocities[i3+2] += n3 * 0.02;
// 2. 弹性回归力 (Elasticity) - 让粒子即使被扰动也能慢慢飘回原位
// 除非被手“抓”住,否则它们有自己的家
const ox = this.origins[i3];
const oy = this.origins[i3+1];
const oz = this.origins[i3+2];
this.velocities[i3] += (ox - px) * CONFIG.returnSpeed;
this.velocities[i3+1] += (oy - py) * CONFIG.returnSpeed;
this.velocities[i3+2] += (oz - pz) * CONFIG.returnSpeed;
// 3. ★★★ 高级手势交互场 (Hand Interaction Field) ★★★
// 支持任意手势、任意数量的手
for (let h = 0; h < hands.length; h++) {
const hand = hands[h];
// 3.1 掌心力场 (Palm Force)
// 如果手张开:产生基于法线的推力 (Push)
// 如果手握拳:产生引力 (Gravity Well)
const dPx = px - hand.palm.x;
const dPy = py - hand.palm.y;
const dPz = pz - hand.palm.z;
const distSq = dPx*dPx + dPy*dPy + dPz*dPz;
// 交互半径
if (distSq < 1500) {
const dist = Math.sqrt(distSq);
const forceFactor = (1500 - distSq) / 1500; // 0 (边缘) -> 1 (中心)
if (hand.isFist) {
// 握拳:黑洞引力,且带有强烈的旋转
// 吸力
this.velocities[i3] -= dPx * 0.1 * forceFactor;
this.velocities[i3+1] -= dPy * 0.1 * forceFactor;
this.velocities[i3+2] -= dPz * 0.1 * forceFactor;
// 旋转力 (Cross Product with Up vector)
this.velocities[i3] += -dPy * 0.2 * forceFactor;
this.velocities[i3+1] += dPx * 0.2 * forceFactor;
} else {
// 张开:基于手掌法线方向的推力 (空气炮)
// 计算点乘,判断粒子是否在手掌前方
// 简单模拟:径向推开 + 手掌朝向的定向风
const pushStrength = 2.0 * hand.velocity; // 动得越快,风越大
this.velocities[i3] += hand.normal.x * pushStrength * forceFactor;
this.velocities[i3+1] += hand.normal.y * pushStrength * forceFactor;
this.velocities[i3+2] += hand.normal.z * pushStrength * forceFactor;
// 基础斥力 (防止穿模)
if (dist < 10) {
this.velocities[i3] += dPx * 0.5 * forceFactor;
this.velocities[i3+1] += dPy * 0.5 * forceFactor;
this.velocities[i3+2] += dPz * 0.5 * forceFactor;
}
}
}
// 3.2 指尖轨迹 (Finger Trails)
// 每一个指尖都是一个微小的扰动源,允许精细作画
for (let f = 0; f < 5; f++) {
const tip = hand.fingertips[f];
const dtx = px - tip.x;
const dty = py - tip.y;
const dtz = pz - tip.z;
const tipDistSq = dtx*dtx + dty*dty + dtz*dtz;
if (tipDistSq < 100) {
// 指尖产生湍流
this.velocities[i3] += (Math.random()-0.5) * 1.5;
this.velocities[i3+1] += (Math.random()-0.5) * 1.5;
this.velocities[i3+2] += (Math.random()-0.5) * 1.5;
}
}
}
// 4. 积分与阻尼
this.velocities[i3] *= CONFIG.friction;
this.velocities[i3+1] *= CONFIG.friction;
this.velocities[i3+2] *= CONFIG.friction;
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;
}
}
/**
* ============================================================================
* 3. 智能感知层 (Intelligent Perception)
* ============================================================================
*/
class HandInterface {
constructor() {
this.handData = { hands: [] };
// MediaPipe 初始化
this.hands = new Hands({locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`});
this.hands.setOptions({
maxNumHands: 2,
modelComplexity: 1,
minDetectionConfidence: 0.7,
minTrackingConfidence: 0.7
});
this.hands.onResults(this.processResults.bind(this));
// 摄像头启动
const video = document.getElementById('input-video');
const camera = new Camera(video, {
onFrame: async () => { await this.hands.send({image: video}); },
width: 640, height: 480
});
camera.start().then(() => {
// UI 更新
document.getElementById('loader-bar').style.width = '100%';
setTimeout(() => document.getElementById('loader').style.opacity = 0, 500);
setTimeout(() => {
document.getElementById('title').classList.add('visible');
document.getElementById('subtitle').classList.add('visible');
document.getElementById('loader').style.display = 'none';
}, 1000);
});
// 上一帧数据用于计算速度
this.prevHands = {};
}
processResults(results) {
const landmarks = results.multiHandLandmarks;
const worldLandmarks = results.multiHandWorldLandmarks; // 使用真实世界坐标计算法线
const currentHands = [];
const debugEl = document.getElementById('hand-state');
if (landmarks) {
landmarks.forEach((lm, index) => {
// 1. 坐标映射 (2D -> 3D Scene Space)
// 屏幕中心为 (0,0), 范围约 -60 到 60
const palmCenter = lm[9]; // 中指根部作为手掌中心
const pos = new THREE.Vector3(
(1.0 - palmCenter.x) * 140 - 70, // X 镜像翻转
-(palmCenter.y * 100 - 50), // Y 翻转
0 // Z 暂定为0后续可根据 lm.z 优化深度
);
// 2. 计算速度 (Velocity)
let velocity = 0;
if (this.prevHands[index]) {
const dist = pos.distanceTo(this.prevHands[index]);
velocity = Math.min(dist, 5.0); // 限制最大速度
}
// 3. 计算手势状态 (State Analysis)
// 3.1 握拳检测: 比较指尖(tip)到掌心(wrist/center)的距离
const wrist = lm[0];
let foldedFingers = 0;
const tips = [8, 12, 16, 20]; // 4 fingers (excluding thumb)
tips.forEach(t => {
// 简单判断: 如果指尖y坐标 低于 指根y坐标 (屏幕空间),或距离手腕太近
// 更稳健的方法是3D距离
const dTip = Math.hypot(lm[t].x - wrist.x, lm[t].y - wrist.y);
const dPip = Math.hypot(lm[t-2].x - wrist.x, lm[t-2].y - wrist.y);
if (dTip < dPip) foldedFingers++;
});
const isFist = foldedFingers >= 3;
// 3.2 手掌法线 (Normal Vector)
// 利用 P0(Wrist), P5(IndexBase), P17(PinkyBase) 计算平面法线
// 这里做一个简化估算:根据手掌移动方向和手腕-中指向量
const pWrist = new THREE.Vector3((1-lm[0].x), -lm[0].y, 0);
const pMiddle = new THREE.Vector3((1-lm[9].x), -lm[9].y, 0);
const dir = new THREE.Vector3().subVectors(pMiddle, pWrist).normalize();
// 默认法线朝向屏幕外 (0,0,1),结合方向旋转
const normal = new THREE.Vector3(0, 0, 1).add(dir.multiplyScalar(0.5)).normalize();
// 4. 收集指尖位置 (用于精细交互)
const fingertips = [4, 8, 12, 16, 20].map(i => {
return new THREE.Vector3(
(1.0 - lm[i].x) * 140 - 70,
-(lm[i].y * 100 - 50),
(lm[i].z || 0) * 50 // 尝试引入深度
);
});
currentHands.push({
id: index,
palm: pos,
velocity: velocity,
isFist: isFist,
normal: normal,
fingertips: fingertips
});
// 更新上一帧
this.prevHands[index] = pos.clone();
});
}
this.handData.hands = currentHands;
// 更新 UI Debug
if (currentHands.length > 0) {
const h = currentHands[0];
const stateStr = h.isFist ? "GRAVITY WELL (FIST)" : "WIND FORCE (OPEN)";
debugEl.innerHTML = `HANDS: DETECTED (${currentHands.length})<br>MODE: ${stateStr}<br>VEL: ${h.velocity.toFixed(2)}`;
} else {
debugEl.innerHTML = `HANDS: SEARCHING...<br>Please show your hands`;
}
}
}
/**
* ============================================================================
* 4. 渲染循环 (Main Loop)
* ============================================================================
*/
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x000000, 0.01);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 60; // 摄像机拉远,看到更多粒子
const renderer = new THREE.WebGLRenderer({ powerPreference: "high-performance", antialias: false });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.getElementById('canvas-container').appendChild(renderer.domElement);
// Post Processing (Bloom)
const composer = new THREE.EffectComposer(renderer);
composer.addPass(new THREE.RenderPass(scene, camera));
const bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
bloomPass.strength = CONFIG.bloomStrength;
bloomPass.radius = 0.5;
bloomPass.threshold = 0.1;
composer.addPass(bloomPass);
// Init Systems
const physics = new PhysicsSystem(scene);
const perception = new HandInterface();
// Update UI Text
document.getElementById('theme-display').innerText = `THEME: ${ACTIVE_THEME.name}`;
document.getElementById('title').innerText = ACTIVE_THEME.name.split('/')[0];
// Animation Loop
const clock = new THREE.Clock();
let frames = 0;
let lastTime = 0;
function animate() {
requestAnimationFrame(animate);
const time = clock.getElapsedTime();
const delta = clock.getDelta();
// FPS Counter
frames++;
if (time - lastTime >= 1) {
document.getElementById('fps-display').innerText = `FPS: ${frames}`;
frames = 0;
lastTime = time;
}
// Update Physics
physics.update(time, perception.handData);
// Subtle Camera Move
camera.position.x = Math.sin(time * 0.1) * 2;
camera.position.y = Math.cos(time * 0.1) * 2;
camera.lookAt(0,0,0);
composer.render();
}
// Resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
});
animate();
</script>
<script defer src="https://events.vercount.one/js"></script>
</body>
</html>