恋爱时间倒计时网页设计与实现方案

一、项目概述

本项目旨在创建一个高度可定制的恋爱时间倒计时网页,支持纪念日日期设置、背景主题切换、个性化文案定制等功能,并通过localStorage保存用户配置。技术栈将采用HTML5、Tailwind CSS v3和原生JavaScript,结合Canvas粒子动画实现视觉吸引力。

二、核心功能设计

  1. 双模式计时系统

    • 正计时:记录恋爱天数(支持精确到秒级更新)
    • 倒计时:重要纪念日提醒(如100天、周年纪念)
  2. 个性化定制面板

    • 日期选择器:支持公历/农历切换
    • 背景设置:纯色渐变、粒子动画、自定义图片上传
    • 主题系统:预设3套浪漫主题(粉紫渐变/星空蓝/蜜桃粉)
    • 文案定制:主标题、副标题自定义
    • 字体选择:3种字体风格(手写体/衬线体/无衬线体)
  3. 视觉动效设计

    • 爱心粒子背景:鼠标交互时粒子聚合为爱心形状
    • 时间数字动画:数字变化时的平滑过渡效果
    • 纪念日里程碑:特殊天数(如520天)的烟花特效

三、技术实现方案

  1. 倒计时核心逻辑
javascript 复制代码
// 高精度计时实现(避免setInterval延迟问题)
function startCountdown(targetDate) {
  const updateTimer = () => {
    const now = new Date().getTime();
    const diff = targetDate - now;
    
    // 时间计算逻辑
    const days = Math.floor(diff / (1000 * 60 * 60 * 24));
    // ...小时/分钟/秒计算
    
    // DOM更新
    updateDOM(days, hours, minutes, seconds);
    
    if (diff <= 0) {
      // 倒计时结束逻辑
      return;
    }
    
    // 动态调整下一次执行时间(修正定时器误差)
    const nextUpdate = Math.max(1000, diff % 1000);
    setTimeout(updateTimer, nextUpdate);
  };
  
  updateTimer();
}
  1. 粒子背景实现(基于Canvas API)
javascript 复制代码
class ParticleBackground {
  constructor(canvasId) {
    this.canvas = document.getElementById(canvasId);
    this.ctx = this.canvas.getContext('2d');
    this.particles = [];
    this.resizeCanvas();
    this.initParticles(120); // 创建120个粒子
    this.animate();
  }
  
  initParticles(count) {
    for (let i = 0; i < count; i++) {
      this.particles.push({
        x: Math.random() * this.canvas.width,
        y: Math.random() * this.canvas.height,
        size: Math.random() * 3 + 1,
        speedX: (Math.random() - 0.5) * 0.5,
        speedY: (Math.random() - 0.5) * 0.5,
        color: this.getRandomColor()
      });
    }
  }
  
  // 爱心形状鼠标交互
  handleMouseMove(e) {
    // 粒子向鼠标位置聚集形成爱心路径
  }
  
  animate() {
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    // 更新粒子位置和连线
    requestAnimationFrame(() => this.animate());
  }
}
  1. 用户配置持久化
javascript 复制代码
// 配置数据结构
const DEFAULT_CONFIG = {
  anniversaryDate: '2023-01-01',
  title: '我们的恋爱时光',
  subtitle: '记录每一刻心动',
  theme: 'pink-love',
  backgroundType: 'particle',
  customBackground: '',
  font: 'handwriting'
};

// 保存配置到localStorage
function saveConfig(config) {
  try {
    localStorage.setItem('loveCounterConfig', JSON.stringify(config));
  } catch (e) {
    console.error('配置保存失败:', e);
  }
}

// 加载配置
function loadConfig() {
  const saved = localStorage.getItem('loveCounterConfig');
  return saved ? JSON.parse(saved) : DEFAULT_CONFIG;
}

四、UI/UX设计规范

  1. 色彩系统

    • 主色调:#FF6B8B(浪漫粉)
    • 辅助色:#8A2BE2(梦幻紫)、#FFD700(香槟金)
    • 中性色:#F9F9F9(背景)、#333333(文字)
  2. 响应式布局

    • 移动端:单列布局,配置项折叠为底部抽屉
    • 平板:双列布局,左侧配置+右侧预览
    • 桌面端:三栏布局,增加快捷操作区
  3. 交互反馈

    • 配置变更时实时预览
    • 操作成功的微动画提示
    • 错误状态的友好提示

五、项目结构

bash 复制代码
love-counter/
├── index.html         # 主页面
├── src/
│   ├── css/
│   │   └── styles.css # Tailwind自定义样式
│   ├── js/
│   │   ├── countdown.js  # 计时逻辑
│   │   ├── particle.js   # 粒子背景
│   │   ├── config.js     # 配置管理
│   │   └── ui.js         # UI交互
│   └── assets/
│       └── fonts/        # 自定义字体
├── tailwind.config.js    # 主题配置
└── README.md             # 项目文档

六、优化与兼容性

  1. 性能优化

    • Canvas动画节流(requestAnimationFrame)
    • 图片懒加载与压缩
    • 配置项变更防抖处理
  2. 浏览器兼容

    • 支持Chrome/Edge/Safari最新版
    • 降级处理IE浏览器(简化动画效果)
  3. 可访问性

    • 语义化HTML结构
    • 键盘导航支持
    • 颜色对比度符合WCAG标准

源代码

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>恋爱时间倒计时</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        love: {
                            pink: '#FF6B8B',
                            purple: '#8A2BE2',
                            gold: '#FFD700',
                            light: '#FFF0F3',
                            dark: '#333333'
                        }
                    },
                    fontFamily: {
                        handwriting: ['Segoe Script', 'Brush Script MT', 'cursive'],
                        serif: ['Georgia', 'Cambria', 'serif'],
                        sans: ['Inter', 'system-ui', 'sans-serif']
                    },
                    animation: {
                        'heartbeat': 'heartbeat 1.5s ease-in-out infinite',
                        'fade-in': 'fadeIn 0.5s ease-out forwards',
                        'slide-up': 'slideUp 0.5s ease-out forwards'
                    },
                    keyframes: {
                        heartbeat: {
                            '0%, 100%': { transform: 'scale(1)' },
                            '50%': { transform: 'scale(1.2)' }
                        },
                        fadeIn: {
                            '0%': { opacity: '0' },
                            '100%': { opacity: '1' }
                        },
                        slideUp: {
                            '0%': { transform: 'translateY(20px)', opacity: '0' },
                            '100%': { transform: 'translateY(0)', opacity: '1' }
                        }
                    }
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .text-shadow {
                text-shadow: 0 2px 4px rgba(0,0,0,0.1);
            }
            .particle-bg {
                position: absolute;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                z-index: 0;
            }
            .config-panel {
                transform: translateX(0);
                transition: transform 0.3s ease-in-out;
            }
            .config-panel.hidden {
                transform: translateX(100%);
            }
            @media (max-width: 768px) {
                .config-panel {
                    transform: translateY(100%);
                    height: 80vh;
                    border-radius: 1rem 1rem 0 0;
                }
                .config-panel.hidden {
                    transform: translateY(100%);
                }
                .config-panel.active {
                    transform: translateY(0);
                }
            }
        }
    </style>
</head>
<body class="font-sans bg-love-light text-love-dark overflow-x-hidden">
    <!-- 粒子背景画布 -->
    <canvas id="particleCanvas" class="particle-bg"></canvas>

    <!-- 主容器 -->
    <div class="relative min-h-screen flex flex-col items-center justify-center p-4 z-10">
        <!-- 头部标题区 -->
        <header class="text-center mb-8 animate-fade-in">
            <h1 id="mainTitle" class="text-[clamp(2rem,5vw,3.5rem)] font-handwriting font-bold text-love-pink text-shadow">
                我们的恋爱时光
            </h1>
            <p id="subTitle" class="text-[clamp(1rem,2vw,1.5rem)] text-gray-600 mt-2">
                记录每一刻心动
            </p>
        </header>

        <!-- 倒计时显示区 -->
        <div class="w-full max-w-3xl mx-auto bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl p-6 md:p-10 animate-slide-up">
            <div class="flex flex-col md:flex-row justify-around items-center gap-6">
                <!-- 正计时显示 -->
                <div class="text-center">
                    <div id="loveDays" class="text-[clamp(2.5rem,8vw,5rem)] font-bold text-love-purple">0</div>
                    <div class="text-gray-500">恋爱天数</div>
                </div>
                
                <!-- 分隔符 -->
                <div class="hidden md:block h-20 w-px bg-gray-300"></div>
                
                <!-- 倒计时显示 -->
                <div class="grid grid-cols-4 gap-2 md:gap-4 w-full md:w-auto">
                    <div class="text-center">
                        <div id="countdownDays" class="text-[clamp(1.5rem,5vw,2.5rem)] font-bold text-love-pink">00</div>
                        <div class="text-xs md:text-sm text-gray-500">天</div>
                    </div>
                    <div class="text-center">
                        <div id="countdownHours" class="text-[clamp(1.5rem,5vw,2.5rem)] font-bold text-love-pink">00</div>
                        <div class="text-xs md:text-sm text-gray-500">时</div>
                    </div>
                    <div class="text-center">
                        <div id="countdownMinutes" class="text-[clamp(1.5rem,5vw,2.5rem)] font-bold text-love-pink">00</div>
                        <div class="text-xs md:text-sm text-gray-500">分</div>
                    </div>
                    <div class="text-center">
                        <div id="countdownSeconds" class="text-[clamp(1.5rem,5vw,2.5rem)] font-bold text-love-pink">00</div>
                        <div class="text-xs md:text-sm text-gray-500">秒</div>
                    </div>
                </div>
            </div>
            
            <div class="mt-6 text-center text-gray-500 text-sm">
                <span id="nextAnniversary">距离下一个纪念日还有:</span>
                <span id="anniversaryName" class="font-medium text-love-purple">恋爱100天</span>
            </div>
        </div>

        <!-- 配置按钮 -->
        <button id="configBtn" class="fixed bottom-6 right-6 bg-love-pink text-white rounded-full p-3 shadow-lg z-20 hover:bg-love-purple transition-colors">
            <i class="fa fa-cog text-xl"></i>
        </button>
    </div>

    <!-- 配置面板 -->
    <div id="configPanel" class="config-panel fixed top-0 right-0 w-full md:w-80 h-full bg-white shadow-2xl z-30 p-6 overflow-y-auto">
        <div class="flex justify-between items-center mb-6">
            <h2 class="text-xl font-bold text-love-pink">个性化设置</h2>
            <button id="closeConfigBtn" class="text-gray-500 hover:text-gray-700">
                <i class="fa fa-times text-xl"></i>
            </button>
        </div>
        
        <form id="configForm" class="space-y-6">
            <!-- 日期设置 -->
            <div class="space-y-2">
                <label class="block text-sm font-medium text-gray-700">恋爱开始日期</label>
                <input type="date" id="startDate" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-love-pink focus:border-love-pink">
            </div>
            
            <!-- 纪念日设置 -->
            <div class="space-y-2">
                <label class="block text-sm font-medium text-gray-700">下一个纪念日</label>
                <div class="flex gap-2">
                    <input type="date" id="anniversaryDate" class="flex-1 p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-love-pink focus:border-love-pink">
                </div>
                <input type="text" id="anniversaryNameInput" placeholder="纪念日名称(如:恋爱100天)" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-love-pink focus:border-love-pink">
            </div>
            
            <!-- 标题设置 -->
            <div class="space-y-2">
                <label class="block text-sm font-medium text-gray-700">主标题</label>
                <input type="text" id="titleInput" placeholder="输入主标题" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-love-pink focus:border-love-pink">
            </div>
            
            <!-- 背景设置 -->
            <div class="space-y-2">
                <label class="block text-sm font-medium text-gray-700">背景类型</label>
                <div class="grid grid-cols-3 gap-2">
                    <button type="button" data-bg-type="gradient" class="bg-gradient-to-br from-love-pink to-love-purple h-10 rounded-lg border-2 border-transparent focus:border-love-pink"></button>
                    <button type="button" data-bg-type="particle" class="bg-gray-100 h-10 rounded-lg border-2 border-transparent focus:border-love-pink"></button>
                    <label class="relative h-10 rounded-lg bg-gray-100 flex items-center justify-center cursor-pointer border-2 border-transparent focus-within:border-love-pink">
                        <i class="fa fa-upload text-gray-400"></i>
                        <input type="file" id="bgImageUpload" accept="image/*" class="absolute inset-0 opacity-0 cursor-pointer">
                    </label>
                </div>
            </div>
            
            <!-- 主题设置 -->
            <div class="space-y-2">
                <label class="block text-sm font-medium text-gray-700">主题颜色</label>
                <div class="flex gap-2">
                    <button type="button" data-theme="pink" class="w-full h-10 bg-love-pink rounded-lg border-2 border-transparent focus:border-black"></button>
                    <button type="button" data-theme="purple" class="w-full h-10 bg-love-purple rounded-lg border-2 border-transparent focus:border-black"></button>
                    <button type="button" data-theme="gold" class="w-full h-10 bg-love-gold rounded-lg border-2 border-transparent focus:border-black"></button>
                </div>
            </div>
            
            <!-- 字体设置 -->
            <div class="space-y-2">
                <label class="block text-sm font-medium text-gray-700">字体选择</label>
                <select id="fontSelect" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-love-pink focus:border-love-pink">
                    <option value="sans">无衬线体</option>
                    <option value="serif">衬线体</option>
                    <option value="handwriting">手写体</option>
                </select>
            </div>
            
            <!-- 保存按钮 -->
            <button type="submit" class="w-full bg-love-pink hover:bg-love-pink/90 text-white font-medium py-2 px-4 rounded-lg transition-colors">
                保存设置
            </button>
        </form>
    </div>

    <!-- 移动端配置按钮 -->
    <button id="mobileConfigBtn" class="fixed bottom-6 right-6 md:hidden bg-love-pink text-white rounded-full p-3 shadow-lg z-20">
        <i class="fa fa-cog text-xl"></i>
    </button>

    <script src="https://cdn.jsdelivr.net/particles.js/2.0.0/particles.min.js"></script>
    <script>
        // 全局变量
        let config = {
            startDate: '',
            anniversaryDate: '',
            anniversaryName: '恋爱100天',
            title: '我们的恋爱时光',
            subtitle: '记录每一刻心动',
            bgType: 'gradient',
            bgImage: '',
            theme: 'pink',
            font: 'sans'
        };
        let particleSystem = null;
        let countdownInterval = null;

        // DOM元素
        const elements = {
            loveDays: document.getElementById('loveDays'),
            countdownDays: document.getElementById('countdownDays'),
            countdownHours: document.getElementById('countdownHours'),
            countdownMinutes: document.getElementById('countdownMinutes'),
            countdownSeconds: document.getElementById('countdownSeconds'),
            mainTitle: document.getElementById('mainTitle'),
            subTitle: document.getElementById('subTitle'),
            anniversaryName: document.getElementById('anniversaryName'),
            startDate: document.getElementById('startDate'),
            anniversaryDate: document.getElementById('anniversaryDate'),
            anniversaryNameInput: document.getElementById('anniversaryNameInput'),
            titleInput: document.getElementById('titleInput'),
            fontSelect: document.getElementById('fontSelect'),
            configPanel: document.getElementById('configPanel'),
            configBtn: document.getElementById('configBtn'),
            closeConfigBtn: document.getElementById('closeConfigBtn'),
            mobileConfigBtn: document.getElementById('mobileConfigBtn'),
            configForm: document.getElementById('configForm'),
            bgImageUpload: document.getElementById('bgImageUpload'),
            particleCanvas: document.getElementById('particleCanvas')
        };

        // 初始化
        function init() {
            // 加载配置
            loadConfig();
            // 设置表单值
            updateFormValues();
            // 初始化粒子背景
            initParticleSystem();
            // 更新UI
            updateUI();
            // 启动倒计时
            startCountdown();
            // 添加事件监听
            addEventListeners();
        }

        // 加载配置
        function loadConfig() {
            const savedConfig = localStorage.getItem('loveCounterConfig');
            if (savedConfig) {
                config = JSON.parse(savedConfig);
            } else {
                // 默认日期设置为今天
                const today = new Date();
                const defaultDate = new Date(today.setDate(today.getDate() - 1)).toISOString().split('T')[0];
                config.startDate = defaultDate;
                
                // 默认纪念日设置为30天后
                const anniversary = new Date(today.setDate(today.getDate() + 30)).toISOString().split('T')[0];
                config.anniversaryDate = anniversary;
                
                saveConfig();
            }
        }

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

        // 更新表单值
        function updateFormValues() {
            elements.startDate.value = config.startDate;
            elements.anniversaryDate.value = config.anniversaryDate;
            elements.anniversaryNameInput.value = config.anniversaryName;
            elements.titleInput.value = config.title;
            elements.subTitle.textContent = config.subtitle;
            elements.fontSelect.value = config.font;
            
            // 设置选中的主题
            document.querySelector(`[data-theme="${config.theme}"]`).classList.add('border-black');
            // 设置选中的背景类型
            document.querySelector(`[data-bg-type="${config.bgType}"]`).classList.add('border-love-pink');
        }

        // 初始化粒子系统
        function initParticleSystem() {
            if (window.particlesJS && config.bgType === 'particle') {
                particlesJS('particleCanvas', {
                    "particles": {
                        "number": { "value": 80, "density": { "enable": true, "value_area": 800 } },
                        "color": { "value": "#FF6B8B" },
                        "shape": { "type": "circle" },
                        "opacity": { "value": 0.5, "random": true },
                        "size": { "value": 3, "random": true },
                        "line_linked": {
                            "enable": true,
                            "distance": 150,
                            "color": "#FF6B8B",
                            "opacity": 0.2,
                            "width": 1
                        },
                        "move": {
                            "enable": true,
                            "speed": 1,
                            "direction": "none",
                            "random": true,
                            "straight": false,
                            "out_mode": "out",
                            "bounce": false
                        }
                    },
                    "interactivity": {
                        "detect_on": "canvas",
                        "events": {
                            "onhover": { "enable": true, "mode": "grab" },
                            "onclick": { "enable": true, "mode": "push" },
                            "resize": true
                        },
                        "modes": {
                            "grab": { "distance": 140, "line_linked": { "opacity": 0.8 } },
                            "push": { "particles_nb": 3 }
                        }
                    },
                    "retina_detect": true
                });
            }
        }

        // 更新UI
        function updateUI() {
            // 更新标题
            elements.mainTitle.textContent = config.title;
            elements.subTitle.textContent = config.subtitle;
            elements.anniversaryName.textContent = config.anniversaryName;
            
            // 更新字体
            document.body.className = `font-${config.font}`;
            
            // 更新主题颜色
            document.documentElement.style.setProperty('--theme-color', 
                config.theme === 'pink' ? '#FF6B8B' : 
                config.theme === 'purple' ? '#8A2BE2' : '#FFD700');
            
            // 更新背景
            updateBackground();
            
            // 计算并更新恋爱天数
            updateLoveDays();
            
            // 计算并更新倒计时
            updateCountdown();
        }

        // 更新背景
        function updateBackground() {
            const body = document.body;
            
            // 清除之前的背景设置
            body.style.backgroundImage = '';
            body.className = body.className.replace(/bg-\S+/g, '');
            
            if (config.bgType === 'gradient') {
                body.classList.add('bg-gradient-to-br', 
                    config.theme === 'pink' ? 'from-love-pink/20 to-love-purple/20' :
                    config.theme === 'purple' ? 'from-love-purple/20 to-indigo-500/20' :
                    'from-love-gold/20 to-yellow-300/20');
            } else if (config.bgType === 'particle') {
                body.classList.add('bg-gray-100');
                if (!particleSystem) {
                    initParticleSystem();
                }
            } else if (config.bgType === 'image' && config.bgImage) {
                body.style.backgroundImage = `url(${config.bgImage})`;
                body.classList.add('bg-cover', 'bg-center');
            }
        }

        // 更新恋爱天数
        function updateLoveDays() {
            const start = new Date(config.startDate);
            const now = new Date();
            const diffTime = Math.abs(now - start);
            const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
            elements.loveDays.textContent = diffDays;
        }

        // 更新倒计时
        function updateCountdown() {
            const now = new Date();
            const anniversary = new Date(config.anniversaryDate);
            
            // 如果纪念日已过,设置为明年
            if (anniversary < now) {
                anniversary.setFullYear(anniversary.getFullYear() + 1);
            }
            
            const diffTime = anniversary - now;
            
            // 计算天、时、分、秒
            const days = Math.floor(diffTime / (1000 * 60 * 60 * 24));
            const hours = Math.floor((diffTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
            const minutes = Math.floor((diffTime % (1000 * 60 * 60)) / (1000 * 60));
            const seconds = Math.floor((diffTime % (1000 * 60)) / 1000);
            
            // 更新DOM
            elements.countdownDays.textContent = days.toString().padStart(2, '0');
            elements.countdownHours.textContent = hours.toString().padStart(2, '0');
            elements.countdownMinutes.textContent = minutes.toString().padStart(2, '0');
            elements.countdownSeconds.textContent = seconds.toString().padStart(2, '0');
        }

        // 启动倒计时
        function startCountdown() {
            // 立即更新一次
            updateLoveDays();
            updateCountdown();
            
            // 清除之前的定时器
            if (countdownInterval) {
                clearInterval(countdownInterval);
            }
            
            // 设置定时器
            countdownInterval = setInterval(() => {
                updateLoveDays();
                updateCountdown();
            }, 1000);
        }

        // 添加事件监听
        function addEventListeners() {
            // 配置面板切换
            elements.configBtn.addEventListener('click', () => {
                elements.configPanel.classList.remove('hidden');
                elements.configPanel.classList.add('active');
            });
            
            elements.closeConfigBtn.addEventListener('click', () => {
                elements.configPanel.classList.add('hidden');
                elements.configPanel.classList.remove('active');
            });
            
            elements.mobileConfigBtn.addEventListener('click', () => {
                elements.configPanel.classList.toggle('active');
            });
            
            // 背景类型选择
            document.querySelectorAll('[data-bg-type]').forEach(btn => {
                btn.addEventListener('click', () => {
                    // 移除所有选中状态
                    document.querySelectorAll('[data-bg-type]').forEach(b => 
                        b.classList.remove('border-love-pink'));
                    // 设置当前选中状态
                    btn.classList.add('border-love-pink');
                    config.bgType = btn.dataset.bgType;
                    updateBackground();
                });
            });
            
            // 主题选择
            document.querySelectorAll('[data-theme]').forEach(btn => {
                btn.addEventListener('click', () => {
                    // 移除所有选中状态
                    document.querySelectorAll('[data-theme]').forEach(b => 
                        b.classList.remove('border-black'));
                    // 设置当前选中状态
                    btn.classList.add('border-black');
                    config.theme = btn.dataset.theme;
                    updateUI();
                });
            });
            
            // 图片上传
            elements.bgImageUpload.addEventListener('change', (e) => {
                const file = e.target.files[0];
                if (file) {
                    const reader = new FileReader();
                    reader.onload = (event) => {
                        config.bgImage = event.target.result;
                        config.bgType = 'image';
                        // 更新背景类型选中状态
                        document.querySelectorAll('[data-bg-type]').forEach(b => 
                            b.classList.remove('border-love-pink'));
                        document.querySelector(`[data-bg-type="image"]`).classList.add('border-love-pink');
                        updateBackground();
                    };
                    reader.readAsDataURL(file);
                }
            });
            
            // 表单提交
            elements.configForm.addEventListener('submit', (e) => {
                e.preventDefault();
                
                // 更新配置
                config.startDate = elements.startDate.value;
                config.anniversaryDate = elements.anniversaryDate.value;
                config.anniversaryName = elements.anniversaryNameInput.value;
                config.title = elements.titleInput.value;
                config.font = elements.fontSelect.value;
                
                // 保存配置
                saveConfig();
                
                // 更新UI
                updateUI();
                
                // 关闭配置面板
                elements.configPanel.classList.add('hidden');
                elements.configPanel.classList.remove('active');
                
                // 显示保存成功提示
                alert('设置已保存!');
            });
        }

        // 页面加载完成后初始化
        window.addEventListener('DOMContentLoaded', init);

        // 窗口大小变化时调整粒子系统
        window.addEventListener('resize', () => {
            if (particleSystem) {
                particleSystem.resize();
            }
        });
    </script>
</body>
</html>
相关推荐
90后的晨仔6 分钟前
👂《侦听器(watch)》— 监听数据变化执行副作用逻辑
前端·vue.js
曾经的三心草7 分钟前
微服务的编程测评系统6-管理员登录前端-前端路由优化
前端·微服务·状态模式
Point19 分钟前
[LeetCode] 最长连续序列
前端·javascript·算法
rookiesx23 分钟前
安装本地python文件到site-packages
开发语言·前端·python
支撑前端荣耀24 分钟前
九、把异常当回事,代码才靠谱
前端
LotteChar32 分钟前
HTML:从 “小白” 到 “标签侠” 的修炼手册
前端·html
趣多多代言人34 分钟前
20分钟学会TypeScript
前端·javascript·typescript
90后的晨仔34 分钟前
⚙️ 《响应式原理》— Vue 是怎么做到自动更新的?
前端·vue.js
结城34 分钟前
深入掌握CSS Grid布局:每个属性详解与实战示例
前端·css
寒..40 分钟前
网络安全第三次作业
前端·css·html