【新春创造营】春节灯笼油猴脚本🔥

:red_paper_lantern: 从灵感到现实:我是如何开发春节灯笼油猴脚本的

前言

最近快到春节了,想着能不能给浏览器也来点节日气氛。在 GitHub 上看到了一个很不错的灯笼项目,给了我很大启发。于是决定自己动手,开发一个功能更全面的春节灯笼油猴脚本。

经过几天的开发、调试和优化,终于完成了这个包含 17项增强功能 的完全版本!今天就来分享一下整个开发过程。


:light_bulb: 灵感来源

最初的想法很简单:给所有网页挂上灯笼,增加节日气氛

参考了 zggmd/spring-lantern 项目,发现:

  • 灯笼使用 CSS 动画实现摆动效果
  • 通过 box-shadow 实现发光效果
  • 鼠标靠近时灯笼会隐藏(不影响正常浏览)

但我觉得还可以做得更好——为什么不能让用户自己控制灯笼的显示、颜色、文字呢?


:bullseye: 项目定位

核心目标

  1. 给所有网站添加春节灯笼 - 自动适配任何网页
  2. 高度可定制化 - 让每个用户都能按自己的喜好调整
  3. 丝滑流畅的动画 - 使用 CSS3 动画,保证性能
  4. 简单易用的控制面板 - 所有设置一目了然

技术栈选择

  • 纯 JavaScript - 无依赖,轻量级
  • CSS3 Animations - GPU 加速,性能优秀
  • LocalStorage - 配置持久化,刷新页面不丢失
  • 事件委托 - 解决动态渲染的事件绑定问题

:rocket: 开发过程

第一阶段:基础功能实现

1. 灯笼的 CSS 实现

灯笼的形状完全用 CSS 绘制:

.spring_lantern__deng {
  position: relative;
  width: 120px;
  height: 90px;
  background: rgba(216, 0, 15, 0.8); /* 红色背景 */
  border-radius: 50% 50%;  /* 椭圆形 */
  animation: swing 3s infinite ease-in-out;  /* 摆动动画 */
  box-shadow: -5px 5px 50px 4px rgba(250, 108, 0, 1);  /* 发光效果 */
}

难点

  • 如何用 CSS 画出不规则的灯笼形状?
  • 如何实现自然的摆动效果?

解决方案

  • 使用 border-radius: 50% 50% 创建椭圆
  • 多层嵌套 div 实现灯笼内部结构
  • transform-origin: 50% -100px 让摆动支点在顶部悬挂处

2. 摆动动画的优化

最初的动画比较生硬,后来改进了:

@keyframes swing {
  0% { transform: rotate(-10deg) }
  50% { transform: rotate(10deg) }
  100% { transform: rotate(-10deg) }
}

优化点

  • 使用 cubic-bezier 缓动函数,让动画更自然
  • 不同灯笼设置不同的动画时长(3s、5s),增加随机感
  • 使用 will-change: transform 触发 GPU 加速

3. 鼠标交互 - 最大的技术挑战

需求:鼠标靠近灯笼时平滑淡出

第一版方案(有问题)

// 简单的 y 坐标判断
if (e.clientY < 220) {
  lantern.style.display = 'none';
}

问题

  • 动画不丝滑,突然消失
  • 鼠标离开后动画从头开始,不自然

最终方案

// 1. 检测鼠标是否在灯笼区域内
function isMouseNearLantern(x, y) {
  const distance = 180;  // 可调节的触发距离
  
  // 左侧灯笼区域
  const leftArea = { x: 0, y: 0, width: 200, height: distance };
  // 右侧灯笼区域  
  const rightArea = { x: window.innerWidth - 200, y: 0, width: 200, height: distance };
  
  return isInArea(x, y, leftArea) || isInArea(x, y, rightArea);
}

// 2. 使用 CSS class 切换,而不是 display
if (isNearLantern) {
  lanternItem.classList.add('spring_lantern__fade-out');
} else {
  lanternItem.classList.remove('spring_lantern__fade-out');
}

CSS 平滑过渡

.spring_lantern__fade-out {
  opacity: 0 !important;
  transform: scale(0.8) translateY(-20px) !important;
  transition: opacity 0.4s ease-out, 
              transform 0.4s ease-out;  /* 关键!丝滑过渡 */
}

核心技巧

  • 不使用 display: none(会重置动画)
  • 使用 opacity + transform 实现淡出
  • 动画从当前位置继续,不会重置

第二阶段:功能增强

1. 控制面板的设计

用户需要一个界面来调整各种设置。我设计了一个悬浮面板:

技术挑战

  • 如何在不影响原网页的情况下嵌入控制面板?
  • 如何让面板既美观又实用?

解决方案

// 使用高 z-index 确保面板在最上层
panel.style.zIndex = '999999';

// 使用事件委托处理所有开关点击
panel.addEventListener('click', (e) => {
  const switchEl = e.target.closest('.switch');
  if (!switchEl) return;
  
  const configKey = switchEl.dataset.config;
  config[configKey] = !config[configKey];
  applyConfig();
});

UI 设计亮点

  • 卡片式布局,分组清晰
  • 渐变色背景,节日氛围
  • 滑块实时预览
  • 开关按钮丝滑动画

2. 自定义文字功能

需求:用户想自己写灯笼上的字

实现

// 支持输入1-4个汉字
function updateLanternText(text) {
  const chars = text.split('');
  document.getElementById('left').textContent = chars[0] || '迎';
  document.getElementById('right').textContent = chars[1] || '春';
  
  // 如果是3-4个灯笼
  if (chars.length >= 3) {
    document.getElementById('center1').textContent = chars[2] || '福';
  }
  if (chars.length >= 4) {
    document.getElementById('center2').textContent = chars[3] || '寿';
  }
}

额外功能

  • 12个预设文字模板(迎春、福、喜、财源、吉祥等)
  • 点击模板快速应用
  • 输入框 + 应用按钮

3. 多灯笼支持

技术难点:动态生成2-4个灯笼

function createLanterns() {
  const count = config.lanternCount;  // 2/3/4
  const text = getFestivalText();
  const chars = text.split('');
  
  const configs = [
    { id: 'left', class: 'deng-box1', text: chars[0], pos: 'left' },
    { id: 'right', class: 'deng-box', text: chars[1], pos: 'right' },
  ];
  
  if (count >= 3) {
    configs.push({ id: 'center1', class: 'deng-box2', text: chars[2], pos: 'center' });
  }
  if (count >= 4) {
    configs.push({ id: 'center2', class: 'deng-box3', text: chars[3], pos: 'center' });
  }
  
  // 动态生成 HTML
  configs.forEach(cfg => {
    const div = document.createElement('div');
    div.innerHTML = `<div class="spring_lantern__${cfg.class}">...</div>`;
    container.appendChild(div);
  });
}

第三阶段:Bug 修复(重要!)

Bug #1: 右侧灯笼跑到屏幕左边 :cross_mark:

现象:刷新页面后,右侧的灯笼消失了

排查过程

  1. 检查 CSS 定位 - 看起来没问题
  2. 检查 HTML 结构 - 正常
  3. 使用开发者工具查看 - 发现灯笼在屏幕外

根本原因

/* 错误的 CSS */
.spring_lantern__container {
  position: fixed;
  top: -20px;
  /* ❌ 缺少 left/right/width */
}

.spring_lantern__deng-box {
  position: absolute;
  right: -20px;  /* 相对于什么定位?容器没有宽度!*/
}

解决方案

/* ✅ 正确的 CSS */
.spring_lantern__container {
  position: fixed;
  top: -20px;
  left: 0;
  right: 0;
  width: 100%;  /* 关键!*/
  height: 0;
}

.spring_lantern__deng-box {
  position: absolute;
  right: -20px;  /* 现在相对于整个屏幕 */
  top: 0;
}

教训:使用 position: absolute 时,父容器必须有明确的定位上下文!

Bug #2: 控制面板开关不生效 :cross_mark:

现象:点击开关没有任何反应

排查过程

  1. 检查事件监听器 - 已绑定
  2. 检查选择器 - 正确
  3. 添加 console.log - 没有输出

根本原因

// ❌ 错误的方式
panel.addEventListener('click', (e) => {
  const switchEl = e.target.closest('.switch');
  const action = switchEl.dataset.action;  // ❌ 没有 data-action!
  
  switch(action) {
    case 'show': ...
  }
});

// HTML 中
<div class="switch" data-action="show">  <!-- ✅ 这里是对的 -->

但实际上,我使用了 renderPanel() 重新生成 HTML,导致:

  • 旧的事件监听器还在
  • 但 HTML 元素已经被替换
  • 新元素没有 data-action 属性!

解决方案

// ✅ 使用 data-config 属性
panel.addEventListener('click', (e) => {
  const switchEl = e.target.closest('.switch');
  const configKey = switchEl.dataset.config;  // ✅ 直接映射到 config
  
  config[configKey] = !config[configKey];
  applyConfig();
  
  // 立即更新视觉状态
  setTimeout(() => {
    const updatedSwitch = panel.querySelector(`[data-config="${configKey}"]`);
    updatedSwitch.classList.toggle('on', config[configKey]);
  }, 0);
});

// HTML 中
<div class="switch" data-config="showLanterns">  <!-- ✅ 直接对应 config.showLanterns -->

核心技巧:使用事件委托 + data 属性,避免动态渲染时事件丢失


第四阶段:高级功能实现

1. 5种颜色主题

设计思路

  • 预定义5种配色方案
  • 主题切换时重新生成 CSS
const colorThemes = {
  traditional: {
    name: '传统红',
    suiLight: '#dc8f03',
    suiDark: '#ffa500',
    r1: 'rgba(216, 0, 15, 0.8)',
    bgGradient: 'linear-gradient(135deg, #d8000f 0%, #ff4d4d 100%)',
  },
  gold: {
    name: '金色年华',
    suiLight: '#ffd700',
    suiDark: '#daa520',
    // ...
  },
  // ... purple, blue, rainbow
};

技术亮点

  • 动态生成 CSS,无需刷新页面
  • 使用模板字符串插入颜色值
  • 切换主题后立即生效

2. 节日自动切换

功能:根据日期自动更换灯笼文字

function getFestivalText() {
  if (!config.autoTheme) return config.customText;
  
  const now = new Date();
  const month = now.getMonth() + 1;
  const day = now.getDate();
  
  // 春节
  if (month === 1 && day >= 1 && day <= 15) return '迎春';
  // 元宵节
  if (month === 1 && day === 15) return '汤圆';
  // 端午节
  if (month === 6 && day >= 18 && day <= 25) return '安康';
  // 中秋节
  if (month === 9 && day >= 8 && day <= 15) return '团圆';
  // 国庆
  if (month === 10 && day >= 1 && day <= 7) return '国泰';
  
  return config.customText;
}

3. 春节倒计时

技术点:计算距离下一个春节的天数

function getSpringCountdown() {
  const now = new Date();
  const year = now.getFullYear();
  
  // 春节日期表(简化版,实际需要农历转换)
  const springFestivals = {
    2025: '2025-01-29',
    2026: '2026-02-17',
    2027: '2027-02-06',
    // ...
  };
  
  let springDate = new Date(springFestivals[year] || `${year}-02-01`);
  if (now > springDate) {
    springDate.setFullYear(year + 1);
  }
  
  const diff = springDate - now;
  return Math.ceil(diff / (1000 * 60 * 60 * 24));
}

4. 网站黑名单

功能:某些网站不显示灯笼

// 黑名单配置
config.blacklist = ['github.com', 'stackoverflow.com'];

// 检测当前网站
function isBlacklisted() {
  const hostname = window.location.hostname;
  return config.blacklist.some(domain => {
    if (domain.startsWith('*.')) {
      return hostname.endsWith(domain.slice(2));
    }
    return hostname === domain;
  });
}

// 如果在黑名单,不渲染灯笼
if (isBlacklisted()) {
  console.log('Lantern hidden: blacklisted');
  return;  // 退出脚本
}

:artist_palette: 最终效果展示

功能列表(17项)

:artist_palette: 视觉外观

  • :white_check_mark: 5种颜色主题
  • :white_check_mark: 灯笼大小调节(0.5x - 1.5x)
  • :white_check_mark: 位置调节(上下左右)
  • :white_check_mark: 灯光闪烁效果

:performing_arts: 动画控制

  • :white_check_mark: 4种摆动模式
  • :white_check_mark: 摆动速度调节
  • :white_check_mark: 摆动幅度调节
  • :white_check_mark: 动画连续性保持

:memo: 内容功能

  • :white_check_mark: 支持2-4个灯笼
  • :white_check_mark: 12个预设文字
  • :white_check_mark: 自定义文字(1-4个汉字)
  • :white_check_mark: 春节倒计时
  • :white_check_mark: 节日自动切换

:gear: 交互增强

  • :white_check_mark: 鼠标距离调节
  • :white_check_mark: 双击灯笼切换
  • :white_check_mark: 键盘快捷键(Ctrl+Shift+L/S/M)

:globe_with_meridians: 实用功能

  • :white_check_mark: 网站黑名单
  • :white_check_mark: 时间段控制
  • :white_check_mark: 性能优化

:hammer_and_wrench: 技术实现亮点

1. 事件委托机制

问题:动态渲染 HTML 后,事件监听器失效

解决方案

// ❌ 传统方式(会失效)
document.querySelectorAll('.switch').forEach(el => {
  el.addEventListener('click', handler);  // renderPanel 后失效
});

// ✅ 事件委托(始终有效)
panel.addEventListener('click', (e) => {
  const switchEl = e.target.closest('.switch');
  if (switchEl) {
    const configKey = switchEl.dataset.config;
    // 处理逻辑...
  }
});

2. CSS 动态生成

问题:切换主题后如何立即生效?

解决方案

// 重新生成所有 CSS
function updateStyles() {
  const theme = getCurrentTheme();
  style.innerHTML = `
    .spring_lantern__deng {
      background: ${theme.r1};
      box-shadow: -5px 5px 50px 4px ${theme.deng_box_shadow};
      /* ... */
    }
    .spring_lantern__panel-btn {
      background: ${theme.bgGradient};
      /* ... */
    }
  `;
}

优势

  • 无需刷新页面
  • 立即生效
  • 支持实时预览

3. 状态持久化

// 保存配置
function saveConfig() {
  localStorage.setItem('spring_lantern_config_v2', JSON.stringify(config));
}

// 加载配置
const saved = localStorage.getItem('spring_lantern_config_v2');
if (saved) {
  config = { ...defaultConfig, ...JSON.parse(saved) };
}

4. 性能优化

/* GPU 加速 */
.spring_lantern__deng {
  will-change: transform;  /* 提示浏览器优化 */
}

/* 平滑过渡 */
.spring_lantern__deng {
  transition: opacity 0.4s ease-out, 
              transform 0.4s ease-out;
}

:package: 发布到 GitHub

仓库地址

:globe_with_meridians: GitHub - 9527wow/spring-lantern-enhanced: 🏮 春节灯笼油猴脚本 - 给所有网站挂上春节灯笼效果,支持完全自定义

文件结构

spring-lantern-enhanced/
├── spring-lantern-fixed.user.js  # 主脚本(54KB)
├── README.md                      # 项目说明
└── LICENSE                        # MIT 许可证

安装方式

方式一:直接安装

  1. 访问:GitHub - 9527wow/spring-lantern-enhanced: 🏮 春节灯笼油猴脚本 - 给所有网站挂上春节灯笼效果,支持完全自定义
  2. 点击 spring-lantern-fixed.user.js
  3. 点击 “Raw” 按钮
  4. Tampermonkey 会自动弹出安装提示

方式二:通过 Release

https://github.com/9527wow/spring-lantern-enhanced/releases/tag/v2.0.1

:thought_balloon: 开发心得

遇到的坑

  1. CSS 定位问题 :star::star::star::star::star:

    • 教训:position: absolute 必须配合父容器的正确布局
  2. 事件监听器失效 :star::star::star::star::star:

    • 教训:动态渲染的 HTML 必须使用事件委托
  3. 动画连续性 :star::star::star::star:

    • 教训:不要用 display: none,要用 opacitytransform
  4. CSS 动画性能 :star::star::star:

    • 教训:使用 will-change 触发 GPU 加速

收获与成长

  1. CSS 动画能力提升

    • 深入理解 animation-play-state
    • 掌握复杂的 keyframe 动画
    • 学会了 transform-origin 的妙用
  2. JavaScript 工程化思维

    • 配置管理(defaultConfig + userConfig)
    • 状态持久化
    • 事件委托的深入理解
  3. 用户体验设计

    • 平滑的过渡动画
    • 直观的控制面板
    • 即时的预览反馈

:bullseye: 使用指南

快速上手

  1. 安装脚本 - 见上方安装方式
  2. 访问任意网站 - 灯笼会自动出现
  3. 点击右下角灯笼按钮 :red_paper_lantern: - 打开控制面板
  4. 自定义设置
    • 选择颜色主题
    • 调整灯笼大小和位置
    • 输入自定义文字
    • 开启/关闭各种效果

键盘快捷键

  • Ctrl+Shift+L - 显示/隐藏灯笼
  • Ctrl+Shift+S - 停止/开始摆动
  • Ctrl+Shift+M - 打开/关闭菜单

效果预览

默认配置下:

  • 左侧灯笼:显示第一个字(如"迎")
  • 右侧灯笼:显示第二个字(如"春")
  • 颜色:传统红色
  • 动画:轻轻摇摆
  • 鼠标靠近:平滑淡出

:crystal_ball: 未来规划

可以增加的功能

  1. :globe_with_meridians: 更多灯笼样式(宫灯、走马灯等)
  2. :artist_palette: 自定义颜色(完全自定义RGB值)
  3. :musical_note: 音效(灯笼摇摆时的轻微风铃声)
  4. :mobile_phone: 移动端适配(针对手机屏幕优化)
  5. :globe_showing_europe_africa: 多语言支持(英文、日文等)

:memo: 总结

这个项目从一个小想法,经过几天的开发、调试、优化,最终变成了一个功能完善的油猴脚本。

开发时间:约4天
代码行数:1891行(主脚本)
功能数量:17项
Bug修复:3个
版本迭代:v1.0 → v2.0.1

:folded_hands: 致谢

感谢以下项目的启发:


:glowing_star: 结尾

如果你也想让自己的浏览器充满节日氛围,欢迎试用这个脚本!

GitHub 仓库GitHub - 9527wow/spring-lantern-enhanced: 🏮 春节灯笼油猴脚本 - 给所有网站挂上春节灯笼效果,支持完全自定义

直接安装https://raw.githubusercontent.com/9527wow/spring-lantern-enhanced/main/spring-lantern-fixed.user.js

Star :star: 支持:GitHub - 9527wow/spring-lantern-enhanced: 🏮 春节灯笼油猴脚本 - 给所有网站挂上春节灯笼效果,支持完全自定义

欢迎反馈 Bug 和功能建议!:tada:



Happy Chinese New Year! :red_envelope::red_paper_lantern::sparkles:

1 个赞

赞赞赞!!!