Na⁺-K⁺ ATPase 3D结构模型
by EpicCoder88262 lines9.8 KB🛠️ Three.js (3D graphics)
<!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.