点击计数与粒子动画

76 阅读5分钟

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>炫酷点击动画效果分析</title>
    <style>
        /* 全局样式设置 */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        /* 页面主体样式 */
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background: linear-gradient(135deg, #1a1a2e, #16213e); /* 深色渐变背景 */
            color: white;
            overflow: hidden; /* 隐藏溢出内容 */
            position: relative; /* 相对定位用于子元素绝对定位 */
        }
        
        /* 内容容器样式 */
        .container {
            max-width: 900px;
            padding: 2rem;
            text-align: center;
            z-index: 10; /* 确保内容在动画元素之上 */
        }
        
        /* 标题样式 */
        h1 {
            font-size: 3.5rem;
            margin-bottom: 1.5rem;
            text-shadow: 0 0 15px rgba(0, 247, 255, 0.7); /* 发光效果 */
            background: linear-gradient(90deg, #ff6b6b, #4ecdc4, #ffe66d); /* 渐变文字 */
            -webkit-background-clip: text; /* 文本裁剪背景 */
            background-clip: text;
            -webkit-text-fill-color: transparent; /* 透明文本显示背景渐变 */
            animation: glow 2s ease-in-out infinite alternate; /* 发光动画 */
        }
        
        /* 描述文本样式 */
        .description {
            font-size: 1.4rem;
            line-height: 1.7;
            margin-bottom: 2rem;
            color: #e0e0e0;
            max-width: 800px;
        }
        
        /* 特性卡片容器 */
        .features {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            gap: 1.5rem;
            margin: 2rem 0;
        }
        
        /* 单个特性卡片样式 */
        .feature-card {
            background: rgba(255, 255, 255, 0.08); /* 半透明白色背景 */
            border-radius: 15px;
            padding: 1.5rem;
            width: 250px;
            backdrop-filter: blur(10px); /* 背景模糊效果 */
            border: 1px solid rgba(255, 255, 255, 0.1); /* 半透明边框 */
            transition: transform 0.3s ease, box-shadow 0.3s ease; /* 平滑过渡效果 */
        }
        
        /* 卡片悬停效果 */
        .feature-card:hover {
            transform: translateY(-8px); /* 上浮效果 */
            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); /* 阴影增强 */
        }
        
        /* 特性标题 */
        .feature-card h3 {
            color: #4ecdc4; /* 主题色 */
            margin-bottom: 0.8rem;
            font-size: 1.5rem;
        }
        
        /* 特性描述 */
        .feature-card p {
            color: #b0b0b0;
            font-size: 1.1rem;
        }
        
        /* 技术说明区域 */
        .instructions {
            background: rgba(0, 0, 0, 0.4); /* 半透明黑色背景 */
            border-radius: 15px;
            padding: 2rem;
            margin: 2rem auto;
            max-width: 700px;
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        
        /* 说明标题 */
        .instructions h2 {
            color: #ffe66d; /* 强调色 */
            margin-bottom: 1.2rem;
            font-size: 2rem;
        }
        
        /* 无序列表样式 */
        .instructions ul {
            text-align: left;
            font-size: 1.2rem;
            line-height: 1.8;
            padding-left: 1.5rem;
        }
        
        /* 列表项样式 */
        .instructions li {
            margin-bottom: 0.8rem;
        }
        
        /* 高亮文本样式 */
        .highlight {
            color: #ff6b6b; /* 高亮色 */
            font-weight: bold;
        }
        
        /* 点击提示样式 */
        .click-prompt {
            font-size: 1.8rem;
            margin-top: 2rem;
            padding: 1.2rem 2rem;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 50px;
            display: inline-block;
            animation: pulse 2s infinite; /* 脉动动画 */
            cursor: pointer;
        }
        
        /* 可点击区域 */
        .click-area {
            position: fixed; /* 固定定位覆盖整个屏幕 */
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 5; /* 位于内容下方但高于背景 */
        }
        
        /* ================= 动画元素样式 ================= */
        /* 粒子容器 */
        .custom-tips {
            position: absolute;
            width: 1em;
            height: 1em;
            margin-left: -.5em;
            margin-top: -.5em;
            left: 0;
            top: 0;
            /* 使用CSS变量定位 */
            transform: translate(var(--left, 50%), var(--top, 50%));
            pointer-events: none; /* 防止干扰点击事件 */
        }
        
        /* 单个粒子样式 */
        .custom-tips-dot {
            position: absolute;
            inset: 0; /* 填充整个容器 */
            display: flex;
            justify-content: center;
            align-items: center;
            opacity: 0; /* 初始透明 */
            /* 水平移动动画 */
            animation: custom-x 1s var(--d, 0s) linear forwards;
        }
        
        /* 粒子内容(emoji) */
        .custom-tips-dot::before {
            content: attr(emoji, '🎉'); /* 从属性获取emoji */
            font-size: 1.8rem;
            /* 垂直下落+旋转动画 */
            animation: custom-y 1s var(--d, 0s) cubic-bezier(0.56, -1.35, 0.85, 0.36) forwards;
        }
        
        /* 数字计数器样式 */
        .custom-num {
            position: absolute;
            left: 0;
            top: 0;
            display: flex;
            width: 2em;
            height: 2em;
            font-size: 2em;
            color: #fff;
            justify-content: center;
            align-items: center;
            margin-left: -1em;
            margin-top: -2em;
            font-weight: bold;
            text-shadow: 4px 4px 0 rgba(255, 0, 0); /* 红色阴影 */
            /* 使用CSS变量定位 */
            transform: translate(var(--left), var(--top));
            pointer-events: none;
        }
        
        /* 数字内容 */
        .custom-num::before {
            content: '+' attr(num); /* 从属性获取数字 */
            opacity: 0;
            /* 缩放动画 */
            animation: count-shark 1s var(--d, 0s);
        }
        
        /* ================= 关键帧动画定义 ================= */
        /* 数字计数器动画 */
        @keyframes count-shark {
            0%, 100% { 
                opacity: 0; 
                transform: scale(.4); /* 缩小 */
            }
            30%, 70% { 
                opacity: 1; 
                transform: scale(1); /* 正常大小 */
            }
        }
        
        /* 水平移动动画 */
        @keyframes custom-x {
            0% { 
                opacity: 0; 
                transform: translateX(0%) 
            }
            10%, 90% { 
                opacity: 1; 
            }
            100% { 
                opacity: 0; 
                /* 使用CSS变量控制移动距离 */
                transform: translateX(var(--x, 300%)); 
            }
        }
        
        /* 垂直下落+旋转动画 */
        @keyframes custom-y {
            100% { 
                /* 下落50vh高度并旋转一圈 */
                transform: translateY(50vh) rotate(1turn); 
            }
        }
        
        /* 文字发光动画 */
        @keyframes glow {
            from { text-shadow: 0 0 10px rgba(0, 247, 255, 0.7); }
            to { text-shadow: 0 0 25px rgba(0, 247, 255, 0.9), 0 0 40px rgba(0, 247, 255, 0.6); }
        }
        
        /* 点击提示脉动动画 */
        @keyframes pulse {
            0% { 
                transform: scale(1); 
                box-shadow: 0 0 0 0 rgba(78, 205, 196, 0.7); 
            }
            70% { 
                transform: scale(1.02); 
                box-shadow: 0 0 0 15px rgba(78, 205, 196, 0); 
            }
            100% { 
                transform: scale(1); 
                box-shadow: 0 0 0 0 rgba(78, 205, 196, 0); 
            }
        }
        
        /* 页脚样式 */
        footer {
            position: absolute;
            bottom: 1rem;
            width: 100%;
            text-align: center;
            font-size: 1.1rem;
            color: rgba(255, 255, 255, 0.6);
        }
    </style>
</head>
<body>

    <script>
        /**
         * 创建粒子元素
         * @param {Array} emojis - 可用的emoji数组
         * @returns {DocumentFragment} 包含所有粒子的文档片段
         */
        function createDots(emojis) {
            // 创建文档片段(优化性能)
            const temp = document.createDocumentFragment();
            
            // 随机选择部分emoji并打乱顺序
            // 1. 随机选择emoji数量(1到数组长度)
            // 2. 使用sort()和Math.random()实现洗牌算法
            const random_emojis = emojis
                .slice(0, Math.ceil(Math.random() * emojis.length))
                .sort(() => Math.random() - 0.5);
            
            // 为每个emoji创建粒子元素
            random_emojis.forEach(emoji => {
                // 创建粒子容器
                const dot = document.createElement('div');
                dot.className = 'custom-tips-dot';
                
                // 设置emoji属性(CSS中通过attr()获取)
                dot.setAttribute('emoji', emoji);
                
                // 设置随机动画延迟(0-0.5秒)
                dot.style.setProperty('--d', `${Math.random() * 0.5}s`);
                
                // 设置随机水平位移方向(-500% 到 500%)
                dot.style.setProperty('--x', `${(Math.random() - 0.5) * 1000}%`);
                
                // 添加到文档片段
                temp.appendChild(dot);
                
                // 动画结束事件处理:移除元素
                dot.addEventListener('animationend', () => {
                    // 如果是容器中最后一个粒子,移除整个容器
                    if (dot?.parentNode?.childElementCount <= 1) {
                        dot.parentNode.remove();
                    } else {
                        // 否则只移除当前粒子
                        dot.remove();
                    }
                });
            })
            
            return temp;
        }

        /**
         * 创建计数器元素
         * @returns {HTMLElement} 计数器元素
         */
        function createNum() {
            // 查找现有的计数器元素
            const current = document.querySelector('.custom-num');
            let num = 1; // 默认从1开始
            
            // 如果已有计数器,则递增数字并移除旧元素
            if (current) {
                num = parseInt(current.getAttribute('num')) + 1;
                current.remove();
            }
            
            // 创建新的计数器元素
            const numDiv = document.createElement('div');
            numDiv.className = 'custom-num';
            numDiv.setAttribute('num', num); // 设置数字属性
            
            // 动画结束后移除元素
            numDiv.addEventListener('animationend', () => {
                numDiv.remove();
            });
            
            return numDiv;
        }

        // 点击事件监听
        document.querySelector('.click-area').addEventListener('click', (ev) => {
            // 获取点击位置坐标
            const { clientX, clientY } = ev;
            
            // 创建粒子容器
            const tips = document.createElement('div');
            
            // 设置粒子容器位置(通过CSS变量)
            tips.style.setProperty('--left', `${clientX}px`);
            tips.style.setProperty('--top', `${clientY}px`);
            tips.className = 'custom-tips';
            
            // 可用的emoji数组
            const emojis = ['🎉', '😘', '🎊', '🤡', '🥳', '🤪', '💗', '✨', '🌟', '💥', '🔥', '🌈'];
            
            // 创建粒子并添加到容器
            const dots = createDots(emojis);
            tips.appendChild(dots);
            
            // 将粒子容器添加到页面
            document.body.appendChild(tips);
            
            // 创建并添加计数器
            document.body.appendChild(createNum());
        });
    </script>
</body>
</html>