查看: 97|回复: 2

帮我写一个动态的球型标签代码

[复制链接]

98

主题

95

回帖

2419

积分

管理员

积分
2419
QQ
发表于 2025-9-12 13:11:28 | 显示全部楼层 |阅读模式

# 动态球型标签云实现

我将为您创建一个动态的球型标签云,标签会围绕球体表面旋转,并具有交互效果。

## 实现思路
1. 使用CSS 3D变换创建球体结构
2. 使用JavaScript计算标签在球体表面的位置
3. 添加鼠标交互效果,使球体随鼠标移动旋转
4. 实现标签的随机颜色和悬停效果

下面是完整的实现代码:

```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>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            overflow: hidden;
        }
        
        .container {
            position: relative;
            width: 100%;
            max-width: 1200px;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-wrap: wrap;
            padding: 20px;
        }
        
        h1 {
            width: 100%;
            text-align: center;
            color: white;
            margin-bottom: 30px;
            text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
            font-size: 2.5rem;
        }
        
        .sphere-container {
            position: relative;
            width: 400px;
            height: 400px;
            perspective: 1000px;
        }
        
        .sphere {
            position: relative;
            width: 100%;
            height: 100%;
            transform-style: preserve-3d;
            transition: transform 0.1s ease-out;
        }
        
        .tag {
            position: absolute;
            top: 50%;
            left: 50%;
            padding: 8px 16px;
            border-radius: 20px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
            box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
            backface-visibility: hidden;
            transform-origin: 0 0;
            text-align: center;
            user-select: none;
        }
        
        .tag:hover {
            transform: scale(1.5) !important;
            z-index: 10;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);
        }
        
        .controls {
            margin-left: 50px;
            background: rgba(255, 255, 255, 0.1);
            padding: 20px;
            border-radius: 15px;
            backdrop-filter: blur(5px);
            color: white;
            max-width: 300px;
        }
        
        .controls h2 {
            margin-bottom: 20px;
            text-align: center;
        }
        
        .control-group {
            margin-bottom: 15px;
        }
        
        label {
            display: block;
            margin-bottom: 5px;
        }
        
        input[type="range"] {
            width: 100%;
        }
        
        .color-preview {
            display: inline-block;
            width: 20px;
            height: 20px;
            border-radius: 4px;
            vertical-align: middle;
            margin-left: 10px;
        }
        
        .instructions {
            margin-top: 30px;
            font-size: 0.9rem;
            line-height: 1.5;
        }
        
        @media (max-width: 900px) {
            .container {
                flex-direction: column;
            }
            
            .controls {
                margin-left: 0;
                margin-top: 30px;
                max-width: 400px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>动态球型标签云</h1>
        
        <div class="sphere-container">
            <div class="sphere" id="sphere"></div>
        </div>
        
        <div class="controls">
            <h2>控制面板</h2>
            
            <div class="control-group">
                <label for="rotationSpeed">旋转速度: <span id="speedValue">2</span></label>
                <input type="range" id="rotationSpeed" min="0" max="5" step="0.1" value="2">
            </div>
            
            <div class="control-group">
                <label for="tagColor">标签颜色:
                    <span class="color-preview" id="colorPreview"></span>
                </label>
                <input type="color" id="tagColor" value="#4A90E2">
            </div>
            
            <div class="control-group">
                <label for="tagCount">标签数量: <span id="countValue">20</span></label>
                <input type="range" id="tagCount" min="5" max="40" value="20">
            </div>
            
            <button id="resetButton">重置位置</button>
            
            <div class="instructions">
                <p>使用说明:</p>
                <ul>
                    <li>将鼠标悬停在球体上并移动可以旋转球体</li>
                    <li>点击标签可以将其固定/取消固定</li>
                    <li>使用上方的控制面板调整效果</li>
                </ul>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 配置
            const sphere = document.getElementById('sphere');
            const sphereContainer = document.querySelector('.sphere-container');
            const rotationSpeedSlider = document.getElementById('rotationSpeed');
            const speedValue = document.getElementById('speedValue');
            const tagColorPicker = document.getElementById('tagColor');
            const colorPreview = document.getElementById('colorPreview');
            const tagCountSlider = document.getElementById('tagCount');
            const countValue = document.getElementById('countValue');
            const resetButton = document.getElementById('resetButton');
            
            let tags = [];
            let radius = 150;
            let autoRotate = true;
            let rotationSpeed = 2;
            let tagColor = '#4A90E2';
            let tagCount = 20;
            let mouseX = 0;
            let mouseY = 0;
            let mouseDown = false;
            let fixedTags = [];
            
            // 初始化
            function init() {
                createTags();
                updateColorPreview();
                animate();
               
                // 事件监听
                sphereContainer.addEventListener('mousemove', handleMouseMove);
                sphereContainer.addEventListener('mousedown', () => mouseDown = true);
                sphereContainer.addEventListener('mouseup', () => mouseDown = false);
                sphereContainer.addEventListener('mouseleave', () => mouseDown = false);
               
                rotationSpeedSlider.addEventListener('input', function() {
                    rotationSpeed = parseFloat(this.value);
                    speedValue.textContent = rotationSpeed.toFixed(1);
                });
               
                tagColorPicker.addEventListener('input', function() {
                    tagColor = this.value;
                    updateTagColors();
                    updateColorPreview();
                });
               
                tagCountSlider.addEventListener('input', function() {
                    tagCount = parseInt(this.value);
                    countValue.textContent = tagCount;
                    createTags();
                });
               
                resetButton.addEventListener('click', resetSpherePosition);
            }
            
            // 创建标签
            function createTags() {
                // 清除现有标签
                sphere.innerHTML = '';
                tags = [];
                fixedTags = [];
               
                // 示例标签文本
                const tagTexts = [
                    'JavaScript', 'HTML5', 'CSS3', 'React', 'Vue.js',
                    'Node.js', 'Python', 'Java', 'PHP', 'Ruby',
                    'Swift', 'Go', 'Rust', 'TypeScript', 'Angular',
                    'jQuery', 'Bootstrap', 'SASS', 'LESS', 'Webpack',
                    'Git', 'Docker', 'Kubernetes', 'AWS', 'Azure',
                    'MongoDB', 'MySQL', 'PostgreSQL', 'Redis', 'Firebase',
                    'GraphQL', 'REST API', 'UI/UX', 'Responsive Design', 'Agile',
                    'Machine Learning', 'Data Science', 'AI', 'IoT', 'Blockchain'
                ];
               
                for (let i = 0; i < tagCount; i++) {
                    const tag = document.createElement('div');
                    tag.className = 'tag';
                    tag.textContent = tagTexts[i % tagTexts.length];
                    tag.style.backgroundColor = tagColor;
                    
                    // 随机位置
                    const phi = Math.acos(-1 + (2 * i) / tagCount);
                    const theta = Math.sqrt(tagCount * Math.PI) * phi;
                    
                    // 存储初始位置
                    const x = radius * Math.cos(theta) * Math.sin(phi);
                    const y = radius * Math.sin(theta) * Math.sin(phi);
                    const z = radius * Math.cos(phi);
                    
                    tags.push({
                        element: tag,
                        x: x,
                        y: y,
                        z: z,
                        phi: phi,
                        theta: theta,
                        fixed: false
                    });
                    
                    sphere.appendChild(tag);
                    
                    // 点击事件 - 固定/取消固定标签
                    tag.addEventListener('click', function(e) {
                        e.stopPropagation();
                        const index = tags.findIndex(t => t.element === this);
                        if (index !== -1) {
                            tags[index].fixed = !tags[index].fixed;
                            if (tags[index].fixed) {
                                fixedTags.push(index);
                                this.style.boxShadow = '0 0 15px 5px rgba(255, 255, 255, 0.7)';
                            } else {
                                fixedTags = fixedTags.filter(idx => idx !== index);
                                this.style.boxShadow = '0 3px 10px rgba(0, 0, 0, 0.2)';
                            }
                        }
                    });
                }
               
                updateTagPositions();
            }
            
            // 更新标签位置
            function updateTagPositions() {
                tags.forEach(tag => {
                    tag.element.style.transform = `translate3d(${tag.x}px, ${tag.y}px, ${tag.z}px)`;
                    
                    // 根据z坐标调整标签大小和透明度,创造深度感
                    const scale = 0.8 + (tag.z + radius) / (2 * radius) * 0.7;
                    const opacity = 0.6 + (tag.z + radius) / (2 * radius) * 0.4;
                    
                    tag.element.style.opacity = opacity;
                    tag.element.style.fontSize = `${scale * 0.9}rem`;
                    tag.element.style.zIndex = Math.floor(scale * 100);
                });
            }
            
            // 处理鼠标移动
            function handleMouseMove(e) {
                if (!mouseDown) return;
               
                const rect = sphereContainer.getBoundingClientRect();
                const centerX = rect.left + rect.width / 2;
                const centerY = rect.top + rect.height / 2;
               
                mouseX = (e.clientX - centerX) * 0.0005;
                mouseY = (e.clientY - centerY) * 0.0005;
               
                rotateSphere(mouseX, mouseY);
            }
            
            // 旋转球体
            function rotateSphere(deltaX, deltaY) {
                tags.forEach(tag => {
                    if (tag.fixed) return; // 跳过固定的标签
                    
                    // 旋转计算
                    const x = tag.x;
                    const y = tag.y * Math.cos(deltaX) - tag.z * Math.sin(deltaX);
                    const z = tag.y * Math.sin(deltaX) + tag.z * Math.cos(deltaX);
                    
                    const x2 = x * Math.cos(deltaY) + z * Math.sin(deltaY);
                    const y2 = y;
                    const z2 = -x * Math.sin(deltaY) + z * Math.cos(deltaY);
                    
                    tag.x = x2;
                    tag.y = y2;
                    tag.z = z2;
                });
               
                updateTagPositions();
            }
            
            // 动画循环
            function animate() {
                requestAnimationFrame(animate);
               
                if (autoRotate && !mouseDown) {
                    rotateSphere(0.001 * rotationSpeed, 0.0005 * rotationSpeed);
                }
            }
            
            // 更新标签颜色
            function updateTagColors() {
                tags.forEach(tag => {
                    tag.element.style.backgroundColor = tagColor;
                });
            }
            
            // 更新颜色预览
            function updateColorPreview() {
                colorPreview.style.backgroundColor = tagColor;
            }
            
            // 重置球体位置
            function resetSpherePosition() {
                createTags();
            }
            
            // 初始化应用
            init();
        });
    </script>
</body>
</html>
```

## 功能说明

这个动态球型标签云具有以下功能:

1. **3D球体效果**:标签分布在3D球体表面,具有深度感和透视效果
2. **自动旋转**:球体会自动缓慢旋转展示所有标签
3. **鼠标交互**:可以用鼠标拖动旋转球体,从不同角度查看标签
4. **标签交互**:点击标签可以将其固定或取消固定
5. **自定义设置**:
   - 调整旋转速度
   - 更改标签颜色
   - 调整标签数量
   - 重置球体位置

## 技术实现

- 使用CSS 3D变换实现球体效果
- 使用JavaScript计算每个标签在球体表面的位置(使用球面坐标公式)
- 根据标签的Z坐标调整透明度和大小,增强3D效果
- 使用requestAnimationFrame实现平滑动画

您可以直接复制上面的代码到HTML文件中运行,或根据需要进行修改和扩展。

0

主题

47

回帖

110

积分

VIP会员

积分
110
发表于 2025-9-12 19:37:57 | 显示全部楼层
# 动态球型标签云实现

我将为您创建一个动态的球型标签云,标签会围绕球体表面旋转,并具有交互效果。

## 实现思路
1. 使用CSS 3D变换创建球体结构
2. 使用JavaScript计算标签在球体表面的位置
3. 添加鼠标交互效果,使球体随鼠标移动旋转
4. 实现标签的随机颜色和悬停效果

下面是完整的实现代码:

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>动态球型标签云</title>
  7.     <style>
  8.         * {
  9.             margin: 0;
  10.             padding: 0;
  11.             box-sizing: border-box;
  12.         }
  13.         
  14.         body {
  15.             display: flex;
  16.             justify-content: center;
  17.             align-items: center;
  18.             min-height: 100vh;
  19.             background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
  20.             font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  21.             overflow: hidden;
  22.         }
  23.         
  24.         .container {
  25.             position: relative;
  26.             width: 100%;
  27.             max-width: 1200px;
  28.             display: flex;
  29.             justify-content: center;
  30.             align-items: center;
  31.             flex-wrap: wrap;
  32.             padding: 20px;
  33.         }
  34.         
  35.         h1 {
  36.             width: 100%;
  37.             text-align: center;
  38.             color: white;
  39.             margin-bottom: 30px;
  40.             text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
  41.             font-size: 2.5rem;
  42.         }
  43.         
  44.         .sphere-container {
  45.             position: relative;
  46.             width: 400px;
  47.             height: 400px;
  48.             perspective: 1000px;
  49.         }
  50.         
  51.         .sphere {
  52.             position: relative;
  53.             width: 100%;
  54.             height: 100%;
  55.             transform-style: preserve-3d;
  56.             transition: transform 0.1s ease-out;
  57.         }
  58.         
  59.         .tag {
  60.             position: absolute;
  61.             top: 50%;
  62.             left: 50%;
  63.             padding: 8px 16px;
  64.             border-radius: 20px;
  65.             font-weight: bold;
  66.             cursor: pointer;
  67.             transition: all 0.3s ease;
  68.             box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
  69.             backface-visibility: hidden;
  70.             transform-origin: 0 0;
  71.             text-align: center;
  72.             user-select: none;
  73.             color: white;
  74.         }
  75.         
  76.         .tag:hover {
  77.             transform: scale(1.5) !important;
  78.             z-index: 10;
  79.             box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);
  80.         }
  81.         
  82.         .controls {
  83.             margin-left: 50px;
  84.             background: rgba(255, 255, 255, 0.1);
  85.             padding: 20px;
  86.             border-radius: 15px;
  87.             backdrop-filter: blur(5px);
  88.             color: white;
  89.             max-width: 300px;
  90.         }
  91.         
  92.         .controls h2 {
  93.             margin-bottom: 20px;
  94.             text-align: center;
  95.         }
  96.         
  97.         .control-group {
  98.             margin-bottom: 15px;
  99.         }
  100.         
  101.         label {
  102.             display: block;
  103.             margin-bottom: 5px;
  104.         }
  105.         
  106.         input {
  107.             width: 100%;
  108.         }
  109.         
  110.         .color-preview {
  111.             display: inline-block;
  112.             width: 20px;
  113.             height: 20px;
  114.             border-radius: 4px;
  115.             vertical-align: middle;
  116.             margin-left: 10px;
  117.         }
  118.         
  119.         .instructions {
  120.             margin-top: 30px;
  121.             font-size: 0.9rem;
  122.             line-height: 1.5;
  123.         }
  124.         
  125.         button {
  126.             background: #4A90E2;
  127.             color: white;
  128.             border: none;
  129.             padding: 10px 15px;
  130.             border-radius: 5px;
  131.             cursor: pointer;
  132.             width: 100%;
  133.             font-weight: bold;
  134.             margin-top: 10px;
  135.             transition: background 0.3s;
  136.         }
  137.         
  138.         button:hover {
  139.             background: #3a7bc8;
  140.         }
  141.         
  142.         @media (max-width: 900px) {
  143.             .container {
  144.                 flex-direction: column;
  145.             }
  146.             
  147.             .controls {
  148.                 margin-left: 0;
  149.                 margin-top: 30px;
  150.                 max-width: 400px;
  151.             }
  152.         }
  153.     </style>
  154. </head>
  155. <body>
  156.     <div class="container">
  157.         <h1>动态球型标签云</h1>
  158.         
  159.         <div class="sphere-container">
  160.             <div class="sphere" id="sphere"></div>
  161.         </div>
  162.         
  163.         <div class="controls">
  164.             <h2>控制面板</h2>
  165.             
  166.             <div class="control-group">
  167.                 <label for="rotationSpeed">旋转速度: <span id="speedValue">2</span></label>
  168.                 <input type="range" id="rotationSpeed" min="0" max="5" step="0.1" value="2">
  169.             </div>
  170.             
  171.             <div class="control-group">
  172.                 <label for="tagColor">标签颜色:
  173.                     <span class="color-preview" id="colorPreview"></span>
  174.                 </label>
  175.                 <input type="color" id="tagColor" value="#4A90E2">
  176.             </div>
  177.             
  178.             <div class="control-group">
  179.                 <label for="tagCount">标签数量: <span id="countValue">20</span></label>
  180.                 <input type="range" id="tagCount" min="5" max="40" value="20">
  181.             </div>
  182.             
  183.             <button id="resetButton">重置位置</button>
  184.             
  185.             <div class="instructions">
  186.                 <p>使用说明:</p>
  187.                 <ul>
  188.                     <li>将鼠标悬停在球体上并移动可以旋转球体</li>
  189.                     <li>点击标签可以将其固定/取消固定</li>
  190.                     <li>使用上方的控制面板调整效果</li>
  191.                 </ul>
  192.             </div>
  193.         </div>
  194.     </div>

  195.     <script>
  196.         document.addEventListener('DOMContentLoaded', function() {
  197.             // 配置
  198.             const sphere = document.getElementById('sphere');
  199.             const sphereContainer = document.querySelector('.sphere-container');
  200.             const rotationSpeedSlider = document.getElementById('rotationSpeed');
  201.             const speedValue = document.getElementById('speedValue');
  202.             const tagColorPicker = document.getElementById('tagColor');
  203.             const colorPreview = document.getElementById('colorPreview');
  204.             const tagCountSlider = document.getElementById('tagCount');
  205.             const countValue = document.getElementById('countValue');
  206.             const resetButton = document.getElementById('resetButton');
  207.             
  208.             let tags = [];
  209.             let radius = 150;
  210.             let autoRotate = true;
  211.             let rotationSpeed = 2;
  212.             let tagColor = '#4A90E2';
  213.             let tagCount = 20;
  214.             let mouseX = 0;
  215.             let mouseY = 0;
  216.             let mouseDown = false;
  217.             let fixedTags = [];
  218.             
  219.             // 初始化
  220.             function init() {
  221.                 createTags();
  222.                 updateColorPreview();
  223.                 animate();
  224.                
  225.                 // 事件监听
  226.                 sphereContainer.addEventListener('mousemove', handleMouseMove);
  227.                 sphereContainer.addEventListener('mousedown', () => mouseDown = true);
  228.                 sphereContainer.addEventListener('mouseup', () => mouseDown = false);
  229.                 sphereContainer.addEventListener('mouseleave', () => mouseDown = false);
  230.                
  231.                 rotationSpeedSlider.addEventListener('input', function() {
  232.                     rotationSpeed = parseFloat(this.value);
  233.                     speedValue.textContent = rotationSpeed.toFixed(1);
  234.                 });
  235.                
  236.                 tagColorPicker.addEventListener('input', function() {
  237.                     tagColor = this.value;
  238.                     updateTagColors();
  239.                     updateColorPreview();
  240.                 });
  241.                
  242.                 tagCountSlider.addEventListener('input', function() {
  243.                     tagCount = parseInt(this.value);
  244.                     countValue.textContent = tagCount;
  245.                     createTags();
  246.                 });
  247.                
  248.                 resetButton.addEventListener('click', resetSpherePosition);
  249.             }
  250.             
  251.             // 创建标签
  252.             function createTags() {
  253.                 // 清除现有标签
  254.                 sphere.innerHTML = '';
  255.                 tags = [];
  256.                 fixedTags = [];
  257.                
  258.                 // 示例标签文本
  259.                 const tagTexts = [
  260.                     "JavaScript", "HTML5", "CSS3", "React", "Vue.js",
  261.                     "Node.js", "Python", "Java", "PHP", "Ruby",
  262.                     "Git", "Docker", "AWS", "MongoDB", "MySQL",
  263.                     "WebGL", "Three.js", "Sass", "LESS", "TypeScript",
  264.                     "Angular", "jQuery", "Bootstrap", "Webpack", "GraphQL",
  265.                     "Redis", "Nginx", "Linux", "UX/UI", "API",
  266.                     "AI", "Machine Learning", "Data Science", "Cybersecurity", "Blockchain",
  267.                     "IoT", "AR/VR", "Cloud Computing", "DevOps", "Agile"
  268.                 ];
  269.                
  270.                 for (let i = 0; i < tagCount; i++) {
  271.                     const tag = document.createElement('div');
  272.                     tag.className = 'tag';
  273.                     tag.textContent = tagTexts[i % tagTexts.length];
  274.                     tag.style.backgroundColor = tagColor;
  275.                     
  276.                     // 随机位置
  277.                     const phi = Math.acos(-1 + (2 * i) / tagCount);
  278.                     const theta = Math.sqrt(tagCount * Math.PI) * phi;
  279.                     
  280.                     // 存储初始位置
  281.                     const x = radius * Math.cos(theta) * Math.sin(phi);
  282.                     const y = radius * Math.sin(theta) * Math.sin(phi);
  283.                     const z = radius * Math.cos(phi);
  284.                     
  285.                     tags.push({
  286.                         element: tag,
  287.                         x: x,
  288.                         y: y,
  289.                         z: z,
  290.                         phi: phi,
  291.                         theta: theta,
  292.                         fixed: false
  293.                     });
  294.                     
  295.                     sphere.appendChild(tag);
  296.                     
  297.                     // 点击事件 - 固定/取消固定标签
  298.                     tag.addEventListener('click', function(e) {
  299.                         e.stopPropagation();
  300.                         const index = tags.findIndex(t => t.element === this);
  301.                         if (index !== -1) {
  302.                             tags[index].fixed = !tags[index].fixed;
  303.                             if (tags[index].fixed) {
  304.                                 fixedTags.push(index);
  305.                                 this.style.boxShadow = '0 0 15px 5px rgba(255, 255, 255, 0.7)';
  306.                             } else {
  307.                                 fixedTags = fixedTags.filter(idx => idx !== index);
  308.                                 this.style.boxShadow = '0 3px 10px rgba(0, 0, 0, 0.2)';
  309.                             }
  310.                         }
  311.                     });
  312.                 }
  313.                
  314.                 updateTagPositions();
  315.             }
  316.             
  317.             // 更新标签位置
  318.             function updateTagPositions() {
  319.                 tags.forEach(tag => {
  320.                     tag.element.style.transform = `translate3d(${tag.x}px, ${tag.y}px, ${tag.z}px)`;
  321.                     
  322.                     // 根据z坐标调整标签大小和透明度,创造深度感
  323.                     const scale = 0.8 + (tag.z + radius) / (2 * radius) * 0.7;
  324.                     const opacity = 0.6 + (tag.z + radius) / (2 * radius) * 0.4;
  325.                     
  326.                     tag.element.style.opacity = opacity;
  327.                     tag.element.style.fontSize = `${scale * 0.9}rem`;
  328.                     tag.element.style.zIndex = Math.floor(scale * 100);
  329.                 });
  330.             }
  331.             
  332.             // 处理鼠标移动
  333.             function handleMouseMove(e) {
  334.                 if (!mouseDown) return;
  335.                
  336.                 const rect = sphereContainer.getBoundingClientRect();
  337.                 const centerX = rect.left + rect.width / 2;
  338.                 const centerY = rect.top + rect.height / 2;
  339.                
  340.                 mouseX = (e.clientX - centerX) * 0.0005;
  341.                 mouseY = (e.clientY - centerY) * 0.0005;
  342.                
  343.                 rotateSphere(mouseX, mouseY);
  344.             }
  345.             
  346.             // 旋转球体
  347.             function rotateSphere(deltaX, deltaY) {
  348.                 tags.forEach(tag => {
  349.                     if (tag.fixed) return; // 跳过固定的标签
  350.                     
  351.                     // 旋转计算
  352.                     const x = tag.x;
  353.                     const y = tag.y * Math.cos(deltaX) - tag.z * Math.sin(deltaX);
  354.                     const z = tag.y * Math.sin(deltaX) + tag.z * Math.cos(deltaX);
  355.                     
  356.                     const x2 = x * Math.cos(deltaY) + z * Math.sin(deltaY);
  357.                     const y2 = y;
  358.                     const z2 = -x * Math.sin(deltaY) + z * Math.cos(deltaY);
  359.                     
  360.                     tag.x = x2;
  361.                     tag.y = y2;
  362.                     tag.z = z2;
  363.                 });
  364.                
  365.                 updateTagPositions();
  366.             }
  367.             
  368.             // 动画循环
  369.             function animate() {
  370.                 requestAnimationFrame(animate);
  371.                
  372.                 if (autoRotate && !mouseDown) {
  373.                     rotateSphere(0.001 * rotationSpeed, 0.0005 * rotationSpeed);
  374.                 }
  375.             }
  376.             
  377.             // 更新标签颜色
  378.             function updateTagColors() {
  379.                 tags.forEach(tag => {
  380.                     tag.element.style.backgroundColor = tagColor;
  381.                 });
  382.             }
  383.             
  384.             // 更新颜色预览
  385.             function updateColorPreview() {
  386.                 colorPreview.style.backgroundColor = tagColor;
  387.             }
  388.             
  389.             // 重置球体位置
  390.             function resetSpherePosition() {
  391.                 createTags();
  392.             }
  393.             
  394.             // 初始化应用
  395.             init();
  396.         });
  397.     </script>
  398. </body>
  399. </html>
复制代码


## 功能说明

这个动态球型标签云具有以下功能:

1. **3D球体效果**:标签分布在3D球体表面,具有深度感和透视效果
2. **自动旋转**:球体会自动缓慢旋转展示所有标签
3. **鼠标交互**:可以用鼠标拖动旋转球体,从不同角度查看标签
4. **标签交互**:点击标签可以将其固定或取消固定
5. **自定义设置**:
   - 调整旋转速度
   - 更改标签颜色
   - 调整标签数量
   - 重置球体位置

## 技术实现

- 使用CSS 3D变换实现球体效果
- 使用JavaScript计算每个标签在球体表面的位置(使用球面坐标公式)
- 根据标签的Z坐标调整透明度和大小,增强3D效果
- 使用requestAnimationFrame实现平滑动画

您可以直接复制上面的代码到HTML文件中运行,或根据需要进行修改和扩展。
回复

使用道具 举报

0

主题

45

回帖

94

积分

注册会员

积分
94
发表于 2025-12-26 23:13:17 | 显示全部楼层
# 动态球型标签云实现

我将为您创建一个动态的球型标签云,标签会围绕球体表面旋转,并具有交互效果。

## 实现思路
1. 使用CSS 3D变换创建球体结构
2. 使用JavaScript计算标签在球体表面的位置
3. 添加鼠标交互效果,使球体随鼠标移动旋转
4. 实现标签的随机颜色和悬停效果

下面是完整的实现代码:

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>动态球型标签云</title>
  7.     <style>
  8.         * {
  9.             margin: 0;
  10.             padding: 0;
  11.             box-sizing: border-box;
  12.         }
  13.         
  14.         body {
  15.             display: flex;
  16.             justify-content: center;
  17.             align-items: center;
  18.             min-height: 100vh;
  19.             background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
  20.             font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  21.             overflow: hidden;
  22.         }
  23.         
  24.         .container {
  25.             position: relative;
  26.             width: 100%;
  27.             max-width: 1200px;
  28.             display: flex;
  29.             justify-content: center;
  30.             align-items: center;
  31.             flex-wrap: wrap;
  32.             padding: 20px;
  33.         }
  34.         
  35.         h1 {
  36.             width: 100%;
  37.             text-align: center;
  38.             color: white;
  39.             margin-bottom: 30px;
  40.             text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
  41.             font-size: 2.5rem;
  42.         }
  43.         
  44.         .sphere-container {
  45.             position: relative;
  46.             width: 400px;
  47.             height: 400px;
  48.             perspective: 1000px;
  49.         }
  50.         
  51.         .sphere {
  52.             position: relative;
  53.             width: 100%;
  54.             height: 100%;
  55.             transform-style: preserve-3d;
  56.             transition: transform 0.1s ease-out;
  57.         }
  58.         
  59.         .tag {
  60.             position: absolute;
  61.             top: 50%;
  62.             left: 50%;
  63.             padding: 8px 16px;
  64.             border-radius: 20px;
  65.             font-weight: bold;
  66.             cursor: pointer;
  67.             transition: all 0.3s ease;
  68.             box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
  69.             backface-visibility: hidden;
  70.             transform-origin: 0 0;
  71.             text-align: center;
  72.             user-select: none;
  73.         }
  74.         
  75.         .tag:hover {
  76.             transform: scale(1.5) !important;
  77.             z-index: 10;
  78.             box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);
  79.         }
  80.         
  81.         .controls {
  82.             margin-left: 50px;
  83.             background: rgba(255, 255, 255, 0.1);
  84.             padding: 20px;
  85.             border-radius: 15px;
  86.             backdrop-filter: blur(5px);
  87.             color: white;
  88.             max-width: 300px;
  89.         }
  90.         
  91.         .controls h2 {
  92.             margin-bottom: 20px;
  93.             text-align: center;
  94.         }
  95.         
  96.         .control-group {
  97.             margin-bottom: 15px;
  98.         }
  99.         
  100.         label {
  101.             display: block;
  102.             margin-bottom: 5px;
  103.         }
  104.         
  105.         input {
  106.             width: 100%;
  107.         }
  108.         
  109.         .color-preview {
  110.             display: inline-block;
  111.             width: 20px;
  112.             height: 20px;
  113.             border-radius: 4px;
  114.             vertical-align: middle;
  115.             margin-left: 10px;
  116.         }
  117.         
  118.         .instructions {
  119.             margin-top: 30px;
  120.             font-size: 0.9rem;
  121.             line-height: 1.5;
  122.         }
  123.         
  124.         @media (max-width: 900px) {
  125.             .container {
  126.                 flex-direction: column;
  127.             }
  128.             
  129.             .controls {
  130.                 margin-left: 0;
  131.                 margin-top: 30px;
  132.                 max-width: 400px;
  133.             }
  134.         }
  135.     </style>
  136. </head>
  137. <body>
  138.     <div class="container">
  139.         <h1>动态球型标签云</h1>
  140.         
  141.         <div class="sphere-container">
  142.             <div class="sphere" id="sphere"></div>
  143.         </div>
  144.         
  145.         <div class="controls">
  146.             <h2>控制面板</h2>
  147.             
  148.             <div class="control-group">
  149.                 <label for="rotationSpeed">旋转速度: <span id="speedValue">2</span></label>
  150.                 <input type="range" id="rotationSpeed" min="0" max="5" step="0.1" value="2">
  151.             </div>
  152.             
  153.             <div class="control-group">
  154.                 <label for="tagColor">标签颜色:
  155.                     <span class="color-preview" id="colorPreview"></span>
  156.                 </label>
  157.                 <input type="color" id="tagColor" value="#4A90E2">
  158.             </div>
  159.             
  160.             <div class="control-group">
  161.                 <label for="tagCount">标签数量: <span id="countValue">20</span></label>
  162.                 <input type="range" id="tagCount" min="5" max="40" value="20">
  163.             </div>
  164.             
  165.             <button id="resetButton">重置位置</button>
  166.             
  167.             <div class="instructions">
  168.                 <p>使用说明:</p>
  169.                 <ul>
  170.                     <li>将鼠标悬停在球体上并移动可以旋转球体</li>
  171.                     <li>点击标签可以将其固定/取消固定</li>
  172.                     <li>使用上方的控制面板调整效果</li>
  173.                 </ul>
  174.             </div>
  175.         </div>
  176.     </div>

  177.     <script>
  178.         document.addEventListener('DOMContentLoaded', function() {
  179.             // 配置
  180.             const sphere = document.getElementById('sphere');
  181.             const sphereContainer = document.querySelector('.sphere-container');
  182.             const rotationSpeedSlider = document.getElementById('rotationSpeed');
  183.             const speedValue = document.getElementById('speedValue');
  184.             const tagColorPicker = document.getElementById('tagColor');
  185.             const colorPreview = document.getElementById('colorPreview');
  186.             const tagCountSlider = document.getElementById('tagCount');
  187.             const countValue = document.getElementById('countValue');
  188.             const resetButton = document.getElementById('resetButton');
  189.             
  190.             let tags = [];
  191.             let radius = 150;
  192.             let autoRotate = true;
  193.             let rotationSpeed = 2;
  194.             let tagColor = '#4A90E2';
  195.             let tagCount = 20;
  196.             let mouseX = 0;
  197.             let mouseY = 0;
  198.             let mouseDown = false;
  199.             let fixedTags = [];
  200.             
  201.             // 初始化
  202.             function init() {
  203.                 createTags();
  204.                 updateColorPreview();
  205.                 animate();
  206.                
  207.                 // 事件监听
  208.                 sphereContainer.addEventListener('mousemove', handleMouseMove);
  209.                 sphereContainer.addEventListener('mousedown', () => mouseDown = true);
  210.                 sphereContainer.addEventListener('mouseup', () => mouseDown = false);
  211.                 sphereContainer.addEventListener('mouseleave', () => mouseDown = false);
  212.                
  213.                 rotationSpeedSlider.addEventListener('input', function() {
  214.                     rotationSpeed = parseFloat(this.value);
  215.                     speedValue.textContent = rotationSpeed.toFixed(1);
  216.                 });
  217.                
  218.                 tagColorPicker.addEventListener('input', function() {
  219.                     tagColor = this.value;
  220.                     updateTagColors();
  221.                     updateColorPreview();
  222.                 });
  223.                
  224.                 tagCountSlider.addEventListener('input', function() {
  225.                     tagCount = parseInt(this.value);
  226.                     countValue.textContent = tagCount;
  227.                     createTags();
  228.                 });
  229.                
  230.                 resetButton.addEventListener('click', resetSpherePosition);
  231.             }
  232.             
  233.             // 创建标签
  234.             function createTags() {
  235.                 // 清除现有标签
  236.                 sphere.innerHTML = '';
  237.                 tags = [];
  238.                 fixedTags = [];
  239.                
  240.                 // 示例标签文本
  241.                 const tagTexts = [
  242.                     "JavaScript", "HTML5", "CSS3", "React", "Vue.js",
  243.                     "Node.js", "Python", "Java", "PHP", "C#",
  244.                     "TypeScript", "Angular", "Sass", "Webpack", "Git",
  245.                     "Docker", "MySQL", "MongoDB", "API", "REST",
  246.                     "GraphQL", "AWS", "Azure", "Linux", "Agile",
  247.                     "UI/UX", "Responsive", "Animation", "3D", "Canvas",
  248.                     "SVG", "WebGL", "Security", "Testing", "DevOps",
  249.                     "Mobile", "PWA", "AI", "Machine Learning", "Data"
  250.                 ];
  251.                
  252.                 for (let i = 0; i < tagCount; i++) {
  253.                     const tag = document.createElement('div');
  254.                     tag.className = 'tag';
  255.                     tag.textContent = tagTexts[i % tagTexts.length];
  256.                     tag.style.backgroundColor = tagColor;
  257.                     
  258.                     // 随机位置
  259.                     const phi = Math.acos(-1 + (2 * i) / tagCount);
  260.                     const theta = Math.sqrt(tagCount * Math.PI) * phi;
  261.                     
  262.                     // 存储初始位置
  263.                     const x = radius * Math.cos(theta) * Math.sin(phi);
  264.                     const y = radius * Math.sin(theta) * Math.sin(phi);
  265.                     const z = radius * Math.cos(phi);
  266.                     
  267.                     tags.push({
  268.                         element: tag,
  269.                         x: x,
  270.                         y: y,
  271.                         z: z,
  272.                         phi: phi,
  273.                         theta: theta,
  274.                         fixed: false
  275.                     });
  276.                     
  277.                     sphere.appendChild(tag);
  278.                     
  279.                     // 点击事件 - 固定/取消固定标签
  280.                     tag.addEventListener('click', function(e) {
  281.                         e.stopPropagation();
  282.                         const index = tags.findIndex(t => t.element === this);
  283.                         if (index !== -1) {
  284.                             tags[index].fixed = !tags[index].fixed;
  285.                             if (tags[index].fixed) {
  286.                                 fixedTags.push(index);
  287.                                 this.style.boxShadow = '0 0 15px 5px rgba(255, 255, 255, 0.7)';
  288.                             } else {
  289.                                 fixedTags = fixedTags.filter(idx => idx !== index);
  290.                                 this.style.boxShadow = '0 3px 10px rgba(0, 0, 0, 0.2)';
  291.                             }
  292.                         }
  293.                     });
  294.                 }
  295.                
  296.                 updateTagPositions();
  297.             }
  298.             
  299.             // 更新标签位置
  300.             function updateTagPositions() {
  301.                 tags.forEach(tag => {
  302.                     tag.element.style.transform = `translate3d(${tag.x}px, ${tag.y}px, ${tag.z}px)`;
  303.                     
  304.                     // 根据z坐标调整标签大小和透明度,创造深度感
  305.                     const scale = 0.8 + (tag.z + radius) / (2 * radius) * 0.7;
  306.                     const opacity = 0.6 + (tag.z + radius) / (2 * radius) * 0.4;
  307.                     
  308.                     tag.element.style.opacity = opacity;
  309.                     tag.element.style.fontSize = `${scale * 0.9}rem`;
  310.                     tag.element.style.zIndex = Math.floor(scale * 100);
  311.                 });
  312.             }
  313.             
  314.             // 处理鼠标移动
  315.             function handleMouseMove(e) {
  316.                 if (!mouseDown) return;
  317.                
  318.                 const rect = sphereContainer.getBoundingClientRect();
  319.                 const centerX = rect.left + rect.width / 2;
  320.                 const centerY = rect.top + rect.height / 2;
  321.                
  322.                 mouseX = (e.clientX - centerX) * 0.0005;
  323.                 mouseY = (e.clientY - centerY) * 0.0005;
  324.                
  325.                 rotateSphere(mouseX, mouseY);
  326.             }
  327.             
  328.             // 旋转球体
  329.             function rotateSphere(deltaX, deltaY) {
  330.                 tags.forEach(tag => {
  331.                     if (tag.fixed) return; // 跳过固定的标签
  332.                     
  333.                     // 旋转计算
  334.                     const x = tag.x;
  335.                     const y = tag.y * Math.cos(deltaX) - tag.z * Math.sin(deltaX);
  336.                     const z = tag.y * Math.sin(deltaX) + tag.z * Math.cos(deltaX);
  337.                     
  338.                     const x2 = x * Math.cos(deltaY) + z * Math.sin(deltaY);
  339.                     const y2 = y;
  340.                     const z2 = -x * Math.sin(deltaY) + z * Math.cos(deltaY);
  341.                     
  342.                     tag.x = x2;
  343.                     tag.y = y2;
  344.                     tag.z = z2;
  345.                 });
  346.                
  347.                 updateTagPositions();
  348.             }
  349.             
  350.             // 动画循环
  351.             function animate() {
  352.                 requestAnimationFrame(animate);
  353.                
  354.                 if (autoRotate && !mouseDown) {
  355.                     rotateSphere(0.001 * rotationSpeed, 0.0005 * rotationSpeed);
  356.                 }
  357.             }
  358.             
  359.             // 更新标签颜色
  360.             function updateTagColors() {
  361.                 tags.forEach(tag => {
  362.                     tag.element.style.backgroundColor = tagColor;
  363.                 });
  364.             }
  365.             
  366.             // 更新颜色预览
  367.             function updateColorPreview() {
  368.                 colorPreview.style.backgroundColor = tagColor;
  369.             }
  370.             
  371.             // 重置球体位置
  372.             function resetSpherePosition() {
  373.                 createTags();
  374.             }
  375.             
  376.             // 初始化应用
  377.             init();
  378.         });
  379.     </script>
  380. </body>
  381. </html>
复制代码


## 功能说明

这个动态球型标签云具有以下功能:

1. **3D球体效果**:标签分布在3D球体表面,具有深度感和透视效果
2. **自动旋转**:球体会自动缓慢旋转展示所有标签
3. **鼠标交互**:可以用鼠标拖动旋转球体,从不同角度查看标签
4. **标签交互**:点击标签可以将其固定或取消固定
5. **自定义设置**:
   - 调整旋转速度
   - 更改标签颜色
   - 调整标签数量
   - 重置球体位置

## 技术实现

- 使用CSS 3D变换实现球体效果
- 使用JavaScript计算每个标签在球体表面的位置(使用球面坐标公式)
- 根据标签的Z坐标调整透明度和大小,增强3D效果
- 使用requestAnimationFrame实现平滑动画

您可以直接复制上面的代码到HTML文件中运行,或根据需要进行修改和扩展。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关注公众号

相关侵权、举报、投诉及建议等,请发 E-mail:admin@discuz.vip

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖
关注公众号
QQ客服返回顶部