🎮ArcadeLab

Na⁺-K⁺ ATPase 3D结构模型

by EpicCoder88
262 lines9.8 KB🛠️ Three.js (3D graphics)
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Na⁺-K⁺ ATPase 3D结构模型</title>
    <style>
        body { margin: 0; overflow: hidden; background: #0a0a1a; font-family: sans-serif; }
        #info { position: absolute; top: 10px; left: 10px; color: #fff; z-index: 100; pointer-events: none; }
        #info h2 { margin: 0 0 5px 0; font-size: 18px; color: #4af; }
        #info p { margin: 0; font-size: 12px; color: #888; }
        .label { position: absolute; color: #fff; font-size: 12px; background: rgba(0,0,0,0.6); padding: 2px 6px; border-radius: 4px; pointer-events: none; }
    </style>
</head>
<body>
    <div id="info">
        <h2>Na⁺-K⁺ ATPase (钠钾泵) 3D模型</h2>
        <p>鼠标拖拽旋转 | 滚轮缩放</p>
    </div>
    <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>
        // ===== 场景初始化 =====
        const scene = new THREE.Scene();
        scene.fog = new THREE.Fog(0x0a0a1a, 20, 50);

        const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(8, 8, 15);

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

        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        controls.autoRotate = true;
        controls.autoRotateSpeed = 0.8;

        // ===== 灯光 =====
        const ambientLight = new THREE.AmbientLight(0x404060);
        scene.add(ambientLight);

        const dirLight = new THREE.DirectionalLight(0xffffff, 1);
        dirLight.position.set(10, 20, 10);
        dirLight.castShadow = true;
        scene.add(dirLight);

        const backLight = new THREE.PointLight(0x4466ff, 0.5);
        backLight.position.set(-10, -5, -10);
        scene.add(backLight);

        // ===== 细胞膜(脂质双分子层) =====
        const membraneGeo = new THREE.BoxGeometry(14, 0.6, 14);
        const membraneMat = new THREE.MeshPhongMaterial({
            color: 0x3355aa,
            transparent: true,
            opacity: 0.12,
            side: THREE.DoubleSide
        });
        const membrane = new THREE.Mesh(membraneGeo, membraneMat);
        membrane.position.y = 0;
        scene.add(membrane);

        // 磷脂头部(上层)
        const headGeo = new THREE.SphereGeometry(0.15, 8, 8);
        const headMat = new THREE.MeshPhongMaterial({ color: 0x5588cc, emissive: 0x223355 });
        for (let i = 0; i < 80; i++) {
            const head = new THREE.Mesh(headGeo, headMat);
            head.position.set(
                (Math.random() - 0.5) * 13,
                0.35,
                (Math.random() - 0.5) * 13
            );
            scene.add(head);
        }
        // 磷脂头部(下层)
        for (let i = 0; i < 80; i++) {
            const head = new THREE.Mesh(headGeo, headMat);
            head.position.set(
                (Math.random() - 0.5) * 13,
                -0.35,
                (Math.random() - 0.5) * 13
            );
            scene.add(head);
        }

        // ===== α-亚基(核心主体) =====
        const alphaGroup = new THREE.Group();
        
        // 跨膜螺旋(10个圆柱体)
        const helixGeo = new THREE.CylinderGeometry(0.22, 0.22, 7, 8);
        const helixMat = new THREE.MeshPhongMaterial({ color: 0x44aa44, shininess: 60 });
        
        const helixPositions = [
            [1.5, 0, 0], [0.8, 0, 1.3], [-0.5, 0, 1.5],
            [-1.4, 0, 0.6], [-1.4, 0, -0.6], [-0.5, 0, -1.5],
            [0.8, 0, -1.3], [1.5, 0, 0], [1.0, 0, 0.9], [0.2, 0, -1.0]
        ];
        
        helixPositions.forEach(pos => {
            const helix = new THREE.Mesh(helixGeo, helixMat);
            helix.position.set(pos[0], 0, pos[2]);
            helix.castShadow = true;
            alphaGroup.add(helix);
        });

        // 细胞质侧结构域(A域、N域、P域)
        const domainGeo = new THREE.SphereGeometry(1.2, 16, 16);
        const aDomainMat = new THREE.MeshPhongMaterial({ color: 0xe6b800, emissive: 0x664400 });
        const nDomainMat = new THREE.MeshPhongMaterial({ color: 0xe67e22, emissive: 0x663300 });
        const pDomainMat = new THREE.MeshPhongMaterial({ color: 0xd35400, emissive: 0x551500 });

        const aDomain = new THREE.Mesh(domainGeo, aDomainMat);
        aDomain.position.set(-1.5, -4, 0);
        aDomain.scale.set(0.8, 0.6, 0.8);
        alphaGroup.add(aDomain);

        const nDomain = new THREE.Mesh(domainGeo, nDomainMat);
        nDomain.position.set(0.5, -4.2, 1);
        nDomain.scale.set(0.9, 0.7, 0.9);
        alphaGroup.add(nDomain);

        const pDomain = new THREE.Mesh(domainGeo, pDomainMat);
        pDomain.position.set(1.2, -4.5, -0.5);
        pDomain.scale.set(0.7, 0.5, 0.7);
        alphaGroup.add(pDomain);

        scene.add(alphaGroup);

        // ===== β-亚基(辅助亚基) =====
        const betaGroup = new THREE.Group();
        
        const betaHelix = new THREE.Mesh(
            new THREE.CylinderGeometry(0.2, 0.2, 7, 8),
            new THREE.MeshPhongMaterial({ color: 0x8e44ad, shininess: 60 })
        );
        betaHelix.position.set(3, 0, 0);
        betaGroup.add(betaHelix);

        const betaDomain = new THREE.Mesh(
            new THREE.SphereGeometry(1.0, 16, 16),
            new THREE.MeshPhongMaterial({ color: 0x9b59b6, emissive: 0x331144 })
        );
        betaDomain.position.set(3, 4.5, 0);
        betaDomain.scale.set(1.2, 1.0, 1.2);
        betaGroup.add(betaDomain);

        scene.add(betaGroup);

        // ===== γ-亚基(小调节亚基) =====
        const gammaHelix = new THREE.Mesh(
            new THREE.CylinderGeometry(0.15, 0.15, 6, 8),
            new THREE.MeshPhongMaterial({ color: 0xc0392b, emissive: 0x441100 })
        );
        gammaHelix.position.set(-3, 0, 1);
        scene.add(gammaHelix);

        // ===== 离子 =====
        // Na⁺ 离子(金色,从下往上)
        const naGeo = new THREE.SphereGeometry(0.25, 12, 12);
        const naMat = new THREE.MeshPhongMaterial({ color: 0xf1c40f, emissive: 0x554400 });
        
        const naPositions = [
            [-0.5, 5, 0.5],
            [0, 6, 0],
            [0.5, 5.5, -0.5]
        ];
        naPositions.forEach(pos => {
            const na = new THREE.Mesh(naGeo, naMat);
            na.position.set(pos[0], pos[1], pos[2]);
            scene.add(na);
        });

        // K⁺ 离子(蓝色,从上往下)
        const kGeo = new THREE.SphereGeometry(0.3, 12, 12);
        const kMat = new THREE.MeshPhongMaterial({ color: 0x3498db, emissive: 0x002244 });
        
        const kPositions = [
            [1.5, -5.5, 0.5],
            [1, -6.5, -0.5]
        ];
        kPositions.forEach(pos => {
            const k = new THREE.Mesh(kGeo, kMat);
            k.position.set(pos[0], pos[1], pos[2]);
            scene.add(k);
        });

        // ===== ATP 分子 =====
        const atpGeo = new THREE.BoxGeometry(0.8, 0.5, 0.8);
        const atpMat = new THREE.MeshPhongMaterial({ color: 0xe74c3c, emissive: 0x551100 });
        const atp = new THREE.Mesh(atpGeo, atpMat);
        atp.position.set(3, -4, -2);
        scene.add(atp);

        // ===== 标注(CSS2D 替代方案:用 Sprite) =====
        function createLabel(text, color) {
            const canvas = document.createElement('canvas');
            canvas.width = 512;
            canvas.height = 128;
            const ctx = canvas.getContext('2d');
            ctx.fillStyle = 'rgba(0,0,0,0.7)';
            ctx.roundRect(0, 0, 512, 128, 20);
            ctx.fill();
            ctx.fillStyle = color || '#ffffff';
            ctx.font = 'bold 48px Arial';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillText(text, 256, 64);
            const texture = new THREE.CanvasTexture(canvas);
            const material = new THREE.SpriteMaterial({ map: texture, depthTest: false, transparent: true });
            const sprite = new THREE.Sprite(material);
            sprite.scale.set(3, 0.75, 1);
            return sprite;
        }

        const labelAlpha = createLabel('α-亚基', '#44aa44');
        labelAlpha.position.set(0, 1, 0);
        scene.add(labelAlpha);

        const labelBeta = createLabel('β-亚基', '#9b59b6');
        labelBeta.position.set(3, 1, 0);
        scene.add(labelBeta);

        const labelGamma = createLabel('γ-亚基', '#c0392b');
        labelGamma.position.set(-3, 1, 1);
        scene.add(labelGamma);

        const labelNa = createLabel('Na⁺', '#f1c40f');
        labelNa.position.set(-0.5, 6.5, 0.5);
        scene.add(labelNa);

        const labelK = createLabel('K⁺', '#3498db');
        labelK.position.set(1, -7, 0);
        scene.add(labelK);

        const labelATP = createLabel('ATP', '#e74c3c');
        labelATP.position.set(3, -5, -2);
        scene.add(labelATP);

        const labelMembrane = createLabel('细胞膜', '#5588cc');
        labelMembrane.position.set(0, 0, 7);
        scene.add(labelMembrane);

        // ===== 动画循环 =====
        function animate() {
            requestAnimationFrame(animate);
            controls.update();
            renderer.render(scene, camera);
        }
        animate();

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

Game Source: Na⁺-K⁺ ATPase 3D结构模型

Creator: EpicCoder88

Libraries: three

Complexity: complex (262 lines, 9.8 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: na-k-atpase-3d-epiccoder88" to link back to the original. Then publish at arcadelab.ai/publish.