(\ _ /)
( ・-・)
/っ
天气卡火爆那段时间糊的,可以比 新建文件 贴进去少点步骤。
吃灰了一整年的 7 系 Serv00:
- 数据存储在浏览器 LocalStorage,刷新不丢失
- URL 后缀加上
?edit时即为编辑状态,一般是先进入编辑页 - ↑ 所以渲染之后要回到编辑页就点浏览器左上 ← 或 快捷键 Alt + ←
测 试 源 码 ![]()
Claude 4.5 の 莫比乌斯环
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>酷炫莫比乌斯环 - Three.js</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
overflow: hidden;
background: #000;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
canvas {
display: block;
}
.info {
position: fixed;
top: 20px;
left: 20px;
color: white;
background: rgba(0, 0, 0, 0.7);
padding: 20px;
border-radius: 15px;
font-size: 14px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
z-index: 10;
}
.info h3 {
margin: 0 0 10px 0;
font-size: 18px;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4, #45b7d1);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.info p {
margin: 5px 0;
opacity: 0.9;
}
.controls {
position: fixed;
bottom: 20px;
right: 20px;
display: flex;
gap: 10px;
z-index: 10;
}
.btn {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
padding: 12px 24px;
border-radius: 25px;
cursor: pointer;
font-size: 14px;
backdrop-filter: blur(10px);
transition: all 0.3s;
}
.btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(255, 255, 255, 0.3);
}
</style>
</head>
<body>
<div class="info">
<h3>✨ 3D 莫比乌斯环</h3>
<p>🖱️ 拖动旋转 | 滚轮缩放</p>
<p>🌈 动态彩虹材质</p>
<p>💫 粒子特效环绕</p>
</div>
<div class="controls">
<button class="btn" id="toggleBtn">⏸️ 暂停</button>
<button class="btn" id="resetBtn">🔄 重置</button>
<button class="btn" id="effectBtn">✨ 切换特效</button>
</div>
<!-- 使用 importmap 方式引入 Three.js -->
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 场景设置
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0a0a);
scene.fog = new THREE.Fog(0x0a0a0a, 10, 50);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 3, 8);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
// 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.autoRotate = true;
controls.autoRotateSpeed = 1.5;
// 创建莫比乌斯环几何体
function createMobiusGeometry(radius = 3, width = 1.2, segments = 200, strips = 40) {
const geometry = new THREE.BufferGeometry();
const vertices = [];
const indices = [];
const uvs = [];
const colors = [];
for (let i = 0; i <= segments; i++) {
const u = (i / segments) * Math.PI * 2;
for (let j = 0; j <= strips; j++) {
const v = ((j / strips) - 0.5) * width;
// 莫比乌斯环参数方程
const x = (radius + v * Math.cos(u / 2)) * Math.cos(u);
const y = (radius + v * Math.cos(u / 2)) * Math.sin(u);
const z = v * Math.sin(u / 2);
vertices.push(x, y, z);
uvs.push(i / segments, j / strips);
// 彩虹色
const hue = (i / segments);
const color = new THREE.Color().setHSL(hue, 1, 0.6);
colors.push(color.r, color.g, color.b);
}
}
// 生成索引
for (let i = 0; i < segments; i++) {
for (let j = 0; j < strips; j++) {
const a = i * (strips + 1) + j;
const b = a + strips + 1;
indices.push(a, b, a + 1);
indices.push(b, b + 1, a + 1);
}
}
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
geometry.computeVertexNormals();
return geometry;
}
// 创建莫比乌斯环
const mobiusGeometry = createMobiusGeometry();
const mobiusMaterial = new THREE.MeshPhongMaterial({
vertexColors: true,
side: THREE.DoubleSide,
shininess: 100,
specular: 0x444444,
emissive: 0x111111
});
const mobius = new THREE.Mesh(mobiusGeometry, mobiusMaterial);
scene.add(mobius);
// 添加发光边缘
const edgeGeometry = new THREE.EdgesGeometry(mobiusGeometry, 15);
const edgeMaterial = new THREE.LineBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.3
});
const edges = new THREE.LineSegments(edgeGeometry, edgeMaterial);
mobius.add(edges);
// 粒子系统
const particlesGeometry = new THREE.BufferGeometry();
const particlesCount = 2000;
const positions = new Float32Array(particlesCount * 3);
const particleColors = new Float32Array(particlesCount * 3);
for (let i = 0; i < particlesCount; i++) {
const u = Math.random() * Math.PI * 2;
const v = (Math.random() - 0.5) * 1.5;
const radius = 3 + Math.random() * 2;
positions[i * 3] = (radius + v * Math.cos(u / 2)) * Math.cos(u);
positions[i * 3 + 1] = (radius + v * Math.cos(u / 2)) * Math.sin(u);
positions[i * 3 + 2] = v * Math.sin(u / 2);
const color = new THREE.Color().setHSL(Math.random(), 1, 0.7);
particleColors[i * 3] = color.r;
particleColors[i * 3 + 1] = color.g;
particleColors[i * 3 + 2] = color.b;
}
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
particlesGeometry.setAttribute('color', new THREE.BufferAttribute(particleColors, 3));
const particlesMaterial = new THREE.PointsMaterial({
size: 0.05,
vertexColors: true,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending
});
const particles = new THREE.Points(particlesGeometry, particlesMaterial);
scene.add(particles);
// 灯光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambientLight);
const pointLight1 = new THREE.PointLight(0xff00ff, 1, 100);
pointLight1.position.set(5, 5, 5);
scene.add(pointLight1);
const pointLight2 = new THREE.PointLight(0x00ffff, 1, 100);
pointLight2.position.set(-5, -5, 5);
scene.add(pointLight2);
// 动态光源
const movingLight = new THREE.PointLight(0xffffff, 1.5, 50);
scene.add(movingLight);
// 控制变量
let autoRotate = true;
let effectMode = 0;
let time = 0;
// 动画循环
function animate() {
requestAnimationFrame(animate);
time += 0.01;
// 更新粒子动画
const positions = particles.geometry.attributes.position.array;
for (let i = 0; i < particlesCount; i++) {
positions[i * 3 + 1] += Math.sin(time + i * 0.1) * 0.002;
}
particles.geometry.attributes.position.needsUpdate = true;
particles.rotation.y = time * 0.1;
// 动态光源运动
movingLight.position.x = Math.sin(time) * 8;
movingLight.position.z = Math.cos(time) * 8;
movingLight.position.y = Math.sin(time * 0.7) * 3;
// 特效模式
if (effectMode === 1) {
mobius.rotation.x = Math.sin(time * 0.3) * 0.2;
mobius.rotation.z = Math.cos(time * 0.5) * 0.2;
}
controls.update();
renderer.render(scene, camera);
}
// 按钮事件
document.getElementById('toggleBtn').addEventListener('click', function() {
autoRotate = !autoRotate;
controls.autoRotate = autoRotate;
this.textContent = autoRotate ? '⏸️ 暂停' : '▶️ 播放';
});
document.getElementById('resetBtn').addEventListener('click', () => {
camera.position.set(0, 3, 8);
controls.reset();
controls.autoRotate = true;
autoRotate = true;
effectMode = 0;
document.getElementById('toggleBtn').textContent = '⏸️ 暂停';
});
document.getElementById('effectBtn').addEventListener('click', function() {
effectMode = (effectMode + 1) % 2;
this.textContent = effectMode === 0 ? '✨ 切换特效' : '🌀 波动模式';
if (effectMode === 0) {
mobius.rotation.x = 0;
mobius.rotation.z = 0;
}
});
// 窗口调整
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
animate();
</script>
</body>
</html>