text

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BODYX - 3D Virtual Showroom</title>
    <style>
        /* --- 基础页面样式 (模拟你网页的极简风格) --- */
        body {
            margin: 0;
            padding: 0;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            background-color: #ffffff;
            color: #333;
            overflow: hidden; /* 隐藏滚动条,专注于3D体验 */
        }

        /* 顶部导航模拟 (简化版) */
        .navbar {
            position: absolute;
            top: 0; left: 0; right: 0;
            height: 70px;
            display: flex;
            align-items: center;
            padding: 0 40px;
            border-bottom: 1px solid #eee;
            z-index: 10;
            background: rgba(255,255,255,0.9);
            backdrop-filter: blur(10px);
        }
        .logo { font-weight: 900; font-size: 24px; font-style: italic; letter-spacing: -1px; }
        .logo span { color: #e11d48; } /* 品牌红 */

        /* --- 3D 展厅容器 --- */
        #showroom-container {
            position: absolute;
            top: 0; left: 0;
            width: 100%; height: 100%;
            z-index: 1;
        }

        /* --- 交互控制台 (底部悬浮) --- */
        .control-panel {
            position: absolute;
            bottom: 50px;
            left: 50%;
            transform: translateX(-50%);
            width: 600px;
            max-width: 90%;
            background: #ffffff;
            border: 1px solid #e5e7eb;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.05);
            padding: 24px 32px;
            z-index: 10;
            transition: all 0.3s ease;
        }

        .panel-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }
        
        .panel-title {
            font-size: 16px;
            font-weight: 600;
            color: #111;
            display: flex;
            align-items: center;
        }
        
        .panel-title::before {
            content: '';
            display: inline-block;
            width: 8px; height: 8px;
            background: #e11d48; /* 品牌红 */
            border-radius: 50%;
            margin-right: 10px;
        }

        .panel-value {
            font-size: 14px;
            color: #e11d48; /* 品牌红 */
            font-weight: bold;
        }

        /* 自定义滑块样式 */
        input[type=range] {
            -webkit-appearance: none;
            width: 100%;
            background: transparent;
            margin: 10px 0;
        }
        input[type=range]:focus {
            outline: none;
        }
        input[type=range]::-webkit-slider-runnable-track {
            width: 100%;
            height: 4px;
            cursor: pointer;
            background: #f3f4f6;
            border-radius: 2px;
        }
        input[type=range]::-webkit-slider-thumb {
            height: 20px;
            width: 20px;
            border-radius: 50%;
            background: #ffffff;
            border: 2px solid #e11d48;
            cursor: pointer;
            -webkit-appearance: none;
            margin-top: -8px;
            box-shadow: 0 2px 6px rgba(225, 29, 72, 0.2);
            transition: transform 0.1s;
        }
        input[type=range]::-webkit-slider-thumb:hover {
            transform: scale(1.1);
        }

        /* 提示文字 */
        .hint-text {
            text-align: center;
            font-size: 12px;
            color: #9ca3af;
            margin-top: 15px;
        }

        /* 器材标签 (3D空间映射到2D) */
        .tooltip {
            position: absolute;
            background: #111;
            color: white;
            padding: 8px 12px;
            border-radius: 6px;
            font-size: 12px;
            pointer-events: none;
            opacity: 0;
            transition: opacity 0.3s;
            transform: translate(-50%, -100%);
            margin-top: -15px;
            z-index: 5;
            white-space: nowrap;
        }
        .tooltip.active {
            opacity: 1;
        }
        .tooltip::after {
            content: '';
            position: absolute;
            bottom: -4px; left: 50%;
            transform: translateX(-50%);
            border-width: 5px 5px 0;
            border-style: solid;
            border-color: #111 transparent transparent transparent;
        }
    </style>
</head>
<body>

    <!-- 顶部导航 -->
    <div class="navbar">
        <div class="logo">BODY<span>X</span></div>
    </div>

    <!-- 3D 渲染区 -->
    <div id="showroom-container"></div>

    <!-- 交互控制台 -->
    <div class="control-panel">
        <div class="panel-header">
            <div class="panel-title" id="sliderLabel">阻力级别 (Resistance)</div>
            <div class="panel-value" id="sliderValue">50</div>
        </div>
        <!-- 滑块 -->
        <input type="range" id="paramSlider" min="0" max="100" value="50">
        <div class="hint-text">滑动滑块,视角自动漫游聚焦符合参数的器材</div>
    </div>

    <!-- 引入 Three.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>

    <script>
        // --- 1. 基础场景设置 ---
        const container = document.getElementById('showroom-container');
        const scene = new THREE.Scene();
        // 背景色:极简浅灰白
        scene.background = new THREE.Color(0xf8f9fa);
        // 添加一点雾化效果,让空间有深邃感
        scene.fog = new THREE.FogExp2(0xf8f9fa, 0.015);

        const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(0, 15, 35); // 初始高视角

        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        container.appendChild(renderer.domElement);

        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        controls.maxPolarAngle = Math.PI / 2 - 0.05; // 防止镜头穿入地下
        controls.enablePan = false; // 限制平移,保持中心视觉

        // --- 2. 灯光设置 (极简风格需要干净的阴影) ---
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
        scene.add(ambientLight);

        const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
        dirLight.position.set(10, 20, 10);
        dirLight.castShadow = true;
        dirLight.shadow.mapSize.width = 2048;
        dirLight.shadow.mapSize.height = 2048;
        dirLight.shadow.camera.near = 0.5;
        dirLight.shadow.camera.far = 50;
        dirLight.shadow.camera.left = -20;
        dirLight.shadow.camera.right = 20;
        dirLight.shadow.camera.top = 20;
        dirLight.shadow.camera.bottom = -20;
        scene.add(dirLight);

        // 补光,增加金属光泽
        const fillLight = new THREE.DirectionalLight(0xe11d48, 0.3); // 微弱的品牌红补光
        fillLight.position.set(-10, 5, -10);
        scene.add(fillLight);

        // --- 3. 空间与地面 ---
        // 创建极简的圆形展台
        const floorGeometry = new THREE.CylinderGeometry(25, 25, 0.5, 64);
        const floorMaterial = new THREE.MeshStandardMaterial({ 
            color: 0xffffff, 
            roughness: 0.1, 
            metalness: 0.1 
        });
        const floor = new THREE.Mesh(floorGeometry, floorMaterial);
        floor.position.y = -0.25;
        floor.receiveShadow = true;
        scene.add(floor);

        // 添加一些极简的网格辅助线增加科技感(可选,如果不想要可以注释掉)
        const gridHelper = new THREE.GridHelper(50, 50, 0xe5e7eb, 0xe5e7eb);
        gridHelper.position.y = 0.01;
        scene.add(gridHelper);

        // --- 4. 生成虚拟器材 (使用几何体拼凑替代真实模型) ---
        const equipmentData = [
            { name: '智能动感单车', type: 'bike', x: -8, z: -5, res: 20, int: 30, color: 0x333333 },
            { name: '旗舰综合训练架', type: 'rack', x: 0, z: 0, res: 80, int: 90, color: 0x111111 }, // 中央主推
            { name: '轻量化跑步机', type: 'treadmill', x: 8, z: -2, res: 50, int: 60, color: 0x555555 },
            { name: '高透风划船机', type: 'rower', x: -5, z: 8, res: 40, int: 80, color: 0x222222 },
            { name: '智能哑铃架', type: 'dumbbell', x: 7, z: 7, res: 90, int: 40, color: 0x444444 }
        ];

        const equipmentMeshes = [];
        const redMaterial = new THREE.MeshStandardMaterial({ color: 0xe11d48, roughness: 0.3, metalness: 0.8 });
        const darkMetal = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.5, metalness: 0.6 });
        const lightMetal = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.2, metalness: 0.9 });
        const glassMaterial = new THREE.MeshPhysicalMaterial({ color: 0xffffff, transmission: 0.9, opacity: 1, roughness: 0.1, metalness: 0, ior: 1.5, thickness: 0.5 });

        function createEquipment(data, index) {
            const group = new THREE.Group();
            group.position.set(data.x, 0, data.z);
            group.userData = data;
            group.userData.index = index;

            // 根据类型生成简单几何体组合
            if (data.type === 'bike') {
                // 飞轮
                const wheel = new THREE.Mesh(new THREE.CylinderGeometry(1.5, 1.5, 0.3, 32), redMaterial);
                wheel.rotation.z = Math.PI / 2;
                wheel.position.set(0, 1.5, -0.5);
                wheel.castShadow = true;
                group.add(wheel);
                // 车架
                const frame = new THREE.Mesh(new THREE.BoxGeometry(0.2, 2.5, 0.2), lightMetal);
                frame.position.set(0, 1.25, 0.5);
                frame.castShadow = true;
                group.add(frame);
                // 座椅
                const seat = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.2, 0.8), darkMetal);
                seat.position.set(0, 2.5, 0.5);
                seat.castShadow = true;
                group.add(seat);
            } else if (data.type === 'rack') {
                // 综合训练架 (中央旗舰,做大一点)
                const base = new THREE.Mesh(new THREE.BoxGeometry(4, 0.2, 4), darkMetal);
                base.position.y = 0.1;
                base.receiveShadow = true;
                group.add(base);
                // 四根立柱
                const pillarGeo = new THREE.BoxGeometry(0.3, 5, 0.3);
                const positions = [[-1.8, 2.5, -1.8], [1.8, 2.5, -1.8], [-1.8, 2.5, 1.8], [1.8, 2.5, 1.8]];
                positions.forEach(pos => {
                    const pillar = new THREE.Mesh(pillarGeo, lightMetal);
                    pillar.position.set(...pos);
                    pillar.castShadow = true;
                    group.add(pillar);
                });
                // 顶部横梁
                const topBar = new THREE.Mesh(new THREE.BoxGeometry(4, 0.3, 0.3), redMaterial);
                topBar.position.set(0, 5, -1.8);
                topBar.castShadow = true;
                group.add(topBar);
                // 高透风/轻量化面板
                const glassPanel = new THREE.Mesh(new THREE.PlaneGeometry(3, 4), glassMaterial);
                glassPanel.position.set(0, 2.5, -1.7);
                group.add(glassPanel);
                
                // 旗舰款添加红色聚光灯效果
                const spotLight = new THREE.SpotLight(0xe11d48, 2);
                spotLight.position.set(0, 10, 0);
                spotLight.target = group;
                spotLight.angle = Math.PI / 6;
                spotLight.penumbra = 0.5;
                scene.add(spotLight);
            } else if (data.type === 'treadmill') {
                // 跑步机
                const deck = new THREE.Mesh(new THREE.BoxGeometry(2, 0.3, 4), darkMetal);
                deck.position.set(0, 1, 0);
                deck.castShadow = true; deck.receiveShadow = true;
                group.add(deck);
                // 扶手
                const handrail = new THREE.Mesh(new THREE.BoxGeometry(2.5, 0.2, 0.2), lightMetal);
                handrail.position.set(0, 3, -1.8);
                handrail.castShadow = true;
                group.add(handrail);
                // 屏幕
                const screen = new THREE.Mesh(new THREE.BoxGeometry(1.5, 1, 0.1), glassMaterial);
                screen.position.set(0, 3.5, -1.7);
                screen.rotation.x = -0.2;
                group.add(screen);
            } else if (data.type === 'rower') {
                // 划船机
                const rail = new THREE.Mesh(new THREE.BoxGeometry(0.6, 0.2, 5), lightMetal);
                rail.position.set(0, 0.5, 0);
                rail.castShadow = true;
                group.add(rail);
                const seat = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.2, 0.8), darkMetal);
                seat.position.set(0, 0.7, 0.5);
                seat.castShadow = true;
                group.add(seat);
            } else if (data.type === 'dumbbell') {
                // 哑铃架
                const frame = new THREE.Mesh(new THREE.BoxGeometry(3, 1.5, 1), redMaterial);
                frame.position.set(0, 0.75, 0);
                frame.castShadow = true;
                group.add(frame);
            }

            scene.add(group);
            equipmentMeshes.push(group);
        }

        // 初始化生成所有器材
        equipmentData.forEach((data, index) => createEquipment(data, index));

        // --- 5. 交互逻辑:滑块控制 ---
        const slider = document.getElementById('paramSlider');
        const sliderValueText = document.getElementById('sliderValue');
        const sliderLabel = document.getElementById('sliderLabel');
        
        // 相机目标位置与视点
        let targetCameraPos = new THREE.Vector3(0, 15, 35);
        let targetLookAt = new THREE.Vector3(0, 0, 0);
        let currentFocusIndex = -1;

        // 滑块事件监听
        slider.addEventListener('input', (e) => {
            const val = parseInt(e.target.value);
            sliderValueText.innerText = val;

            // 切换评估维度 (左侧阻力,右侧智能互联)
            if (val < 50) {
                sliderLabel.innerText = "阻力级别 (Resistance)";
            } else {
                sliderLabel.innerText = "智能互联度 (Smart Connect)";
            }

            // 寻找最匹配的器材
            let bestMatchIndex = 0;
            let minDiff = 999;
            const currentParam = val;

            equipmentMeshes.forEach((mesh, index) => {
                const data = mesh.userData;
                // 动态判断是看阻力还是智能互联
                const targetValue = currentParam < 50 ? data.res : data.int;
                const diff = Math.abs(targetValue - currentParam);

                if (diff < minDiff) {
                    minDiff = diff;
                    bestMatchIndex = index;
                }
            });

            // 如果目标改变了,触发相机漫游
            if (currentFocusIndex !== bestMatchIndex) {
                currentFocusIndex = bestMatchIndex;
                focusOnEquipment(bestMatchIndex);
            }
        });

        function focusOnEquipment(index) {
            const targetMesh = equipmentMeshes[index];
            const data = targetMesh.userData;

            // 计算相机目标位置 (器材侧前方)
            // 保留 Y 轴高度,X 和 Z 根据器材位置偏移
            targetCameraPos.set(
                data.x * 1.5 + 5, 
                8, 
                data.z * 1.5 + 12
            );

            // 视点对准器材中心
            targetLookAt.set(data.x, 1.5, data.z);
            
            // 高亮当前选中的器材 (通过放大或改变材质颜色)
            equipmentMeshes.forEach((mesh, i) => {
                if (i === index) {
                    mesh.scale.set(1.1, 1.1, 1.1); // 放大高亮
                } else {
                    mesh.scale.set(1, 1, 1);
                }
            });
        }

        // --- 6. 动画循环 ---
        function animate() {
            requestAnimationFrame(animate);

            // 相机平滑过渡 (Lerp)
            camera.position.lerp(targetCameraPos, 0.05);
            controls.target.lerp(targetLookAt, 0.05);
            
            controls.update();
            renderer.render(scene, camera);
        }

        animate();

        // 初始化默认聚焦中央旗舰款
        focusOnEquipment(1); 

        // --- 7. 窗口自适应 ---
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });

        // 简单的悬浮提示 (将 3D 坐标转换为 2D 屏幕坐标)
        const tooltip = document.createElement('div');
        tooltip.className = 'tooltip';
        document.body.appendChild(tooltip);

        function updateTooltips() {
            if (currentFocusIndex >= 0) {
                const targetMesh = equipmentMeshes[currentFocusIndex];
                const vector = new THREE.Vector3();
                targetMesh.getWorldPosition(vector);
                vector.y += 3; // 向上偏移
                
                vector.project(camera);

                const x = (vector.x * .5 + .5) * window.innerWidth;
                const y = (-(vector.y * .5) + .5) * window.innerHeight;

                tooltip.style.left = x + 'px';
                tooltip.style.top = y + 'px';
                tooltip.innerText = targetMesh.userData.name;
                tooltip.classList.add('active');
            } else {
                tooltip.classList.remove('active');
            }
        }

        // 在渲染循环中更新 Tooltip 位置
        function renderLoop() {
            requestAnimationFrame(renderLoop);
            updateTooltips();
            renderer.render(scene, camera);
        }
        renderLoop();

    </script>
</body>
</html>