这个页面采用极简的 HTML 结构,完全依赖 JavaScript 和 Three.js 创建复杂的 3D 视觉效果。CSS 样式主要用于确保 Canvas 元素的全屏显示和居中定位,而所有的视觉效果(粒子、发光、动画)都通过 Three.js 的渲染能力和自定义着色器实现。整体设计注重视觉冲击力和用户交互体验,通过粒子系统、动态光影和流畅动画创造出一个引人入胜的沉浸式 3D 环境。
大家复制代码时,可能会因格式转换出现错乱,导致样式失效。建议先少量复制代码进行测试,若未能解决问题,私信回复源码两字,我会发送完整的压缩包给你。
演示效果


HTML&CSS
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #000;
}
canvas {
width: 100%;
height: 100vh;
display: block;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"three/addons/controls/OrbitControls.js": "https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x000000, 0.01);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({
antialias: true,
powerPreference: "high-performance"
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.rotateSpeed = 0.5;
controls.minDistance = 10;
controls.maxDistance = 30;
camera.position.z = 15;
camera.position.y = 5;
controls.target.set(0, 0, 0);
controls.update();
const pointMaterialShader = {
vertexShader: `
attribute float size;
varying vec3 vColor;
varying float vDistance;
uniform float time;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
vDistance = -mvPosition.z;
float pulse = sin(time * 2.0 + length(position)) * 0.15 + 1.0;
vec3 pos = position;
pos.x += sin(time + position.z * 0.5) * 0.05;
pos.y += cos(time + position.x * 0.5) * 0.05;
pos.z += sin(time + position.y * 0.5) * 0.05;
mvPosition = modelViewMatrix * vec4(pos, 1.0);
gl_PointSize = size * (300.0 / -mvPosition.z) * pulse;
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
varying vec3 vColor;
varying float vDistance;
uniform float time;
void main() {
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
float r = dot(cxy, cxy);
if (r > 1.0) discard;
float glow = exp(-r * 2.5);
float outerGlow = exp(-r * 1.5) * 0.3;
vec3 finalColor = vColor * (1.2 + sin(time * 0.5) * 0.1);
finalColor += vec3(0.2, 0.4, 0.6) * outerGlow;
float distanceFade = 1.0 - smoothstep(0.0, 50.0, vDistance);
float intensity = mix(0.7, 1.0, distanceFade);
gl_FragColor = vec4(finalColor * intensity, (glow + outerGlow) * distanceFade);
}
`
};
function createSpiralSphere(radius, particleCount, colors) {
const geometry = new THREE.BufferGeometry();
const positions = [];
const particleColors = [];
const sizes = [];
for (let i = 0; i < particleCount; i++) {
const phi = Math.acos(-1 + (2 * i) / particleCount);
const theta = Math.sqrt(particleCount * Math.PI) * phi;
const x = radius * Math.sin(phi) * Math.cos(theta);
const y = radius * Math.sin(phi) * Math.sin(theta);
const z = radius * Math.cos(phi);
positions.push(x, y, z);
const colorPos = i / particleCount;
const color1 = colors[Math.floor(colorPos * (colors.length - 1))];
const color2 = colors[Math.ceil(colorPos * (colors.length - 1))];
const mixRatio = (colorPos * (colors.length - 1)) % 1;
const finalColor = new THREE.Color().lerpColors(color1, color2, mixRatio);
particleColors.push(finalColor.r, finalColor.g, finalColor.b);
sizes.push(Math.random() * 0.15 + 0.08);
}
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(particleColors, 3));
geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1));
const material = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 }
},
vertexShader: pointMaterialShader.vertexShader,
fragmentShader: pointMaterialShader.fragmentShader,
vertexColors: true,
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending
});
return new THREE.Points(geometry, material);
}
function createOrbitRings(radius, count, thickness) {
const group = new THREE.Group();
for (let i = 0; i < count; i++) {
const ringGeometry = new THREE.BufferGeometry();
const positions = [];
const colors = [];
const sizes = [];
const particleCount = 3000;
for (let j = 0; j < particleCount; j++) {
const angle = (j / particleCount) * Math.PI * 2;
const radiusVariation = radius + (Math.random() - 0.5) * thickness;
const x = Math.cos(angle) * radiusVariation;
const y = (Math.random() - 0.5) * thickness;
const z = Math.sin(angle) * radiusVariation;
positions.push(x, y, z);
const hue = (i / count) * 0.7 + (j / particleCount) * 0.3;
const color = new THREE.Color().setHSL(hue, 1, 0.6);
color.multiplyScalar(1.2);
colors.push(color.r, color.g, color.b);
sizes.push(Math.random() * 0.12 + 0.06);
}
ringGeometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
ringGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
ringGeometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1));
const material = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 }
},
vertexShader: pointMaterialShader.vertexShader,
fragmentShader: pointMaterialShader.fragmentShader,
vertexColors: true,
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending
});
const ring = new THREE.Points(ringGeometry, material);
ring.rotation.x = Math.random() * Math.PI;
ring.rotation.y = Math.random() * Math.PI;
group.add(ring);
}
return group;
}
const sphereColors = [
new THREE.Color(0x00ffff).multiplyScalar(1.2),
new THREE.Color(0xff1493).multiplyScalar(1.1),
new THREE.Color(0x4169e1).multiplyScalar(1.2),
new THREE.Color(0xff69b4).multiplyScalar(1.1),
new THREE.Color(0x00bfff).multiplyScalar(1.2)
];
const coreSphere = createSpiralSphere(4, 25000, sphereColors);
const orbitRings = createOrbitRings(5.8, 6, 0.4);
const mainGroup = new THREE.Group();
mainGroup.scale.set(1.2, 1.2, 1.2);
mainGroup.add(coreSphere);
mainGroup.add(orbitRings);
scene.add(mainGroup);
let time = 0;
function animate() {
requestAnimationFrame(animate);
time += 0.002;
coreSphere.material.uniforms.time.value = time;
orbitRings.children.forEach(ring => {
ring.material.uniforms.time.value = time;
});
coreSphere.rotation.y += 0.001;
coreSphere.rotation.x = Math.sin(time * 0.5) * 0.15;
orbitRings.children.forEach((ring, index) => {
const dynamicSpeed = 0.001 * (Math.sin(time * 0.2) + 2.0) * (index + 1);
ring.rotation.z += dynamicSpeed;
ring.rotation.x += dynamicSpeed * 0.6;
ring.rotation.y += dynamicSpeed * 0.4;
});
const breathe = 1 + Math.sin(time * 1.5) * 0.1;
coreSphere.scale.set(breathe, breathe, breathe);
controls.update();
renderer.render(scene, camera);
}
window.addEventListener('resize', () => {
const width = window.innerWidth;
const height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
});
animate();
</script>
</body>
</html>
代码解析
全局样式
css
* {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #000;
}
重置所有元素的内外边距
隐藏溢出内容(防止滚动条出现)
设置黑色背景(与 3D 场景保持一致)
Canvas 样式
css
canvas {
width: 100%;
height: 100vh;
display: block;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
全屏尺寸显示(覆盖整个视口)
固定定位,确保在页面滚动时保持居中
使用 transform 实现精确居中定位
全屏沉浸式体验
整个页面被黑色背景覆盖
Canvas 元素无缝填充整个视口
无任何 UI 元素干扰,专注于 3D 内容
高性能渲染优化
javascript
renderer = new THREE.WebGLRenderer({
antialias: true,
powerPreference: "high-performance"
});
启用抗锯齿以提升视觉质量
优先使用高性能 GPU 渲染模式
适配设备像素比以在高 DPI 屏幕上保持清晰度
响应式设计
javascript
window.addEventListener('resize', () => {
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
});
监听窗口大小变化
动态调整相机视口和渲染器尺寸
确保 3D 场景在任何屏幕尺寸下都能正确显示
视觉效果优化
使用 AdditiveBlending 混合模式创建粒子发光效果
设置 depthWrite: false 避免粒子间的深度冲突
通过自定义着色器实现动态光效和脉动效果
交互体验
javascript
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
轨道控制器允许用户旋转、缩放和平移场景
阻尼效果提供流畅的惯性感
限制最小 / 最大距离防止视角过近或过远
各位互联网搭子,要是这篇文章成功引起了你的注意,别犹豫,关注、点赞、评论、分享走一波,让我们把这份默契延续下去,一起在知识的海洋里乘风破浪!