效果示例

引入 Three.js
首先,在 工程中中引入 Three.js 库:
css
npm install three
npm i --save-dev @types/three
实现步骤
- 初始化场景、相机与渲染器
- 创建场景
new THREE.Scene()
- 添加雾化效果
new THREE.FogExp2
- 创建透视相机
new THREE.PerspectiveCamera
- 渲染器
new THREE.WebGLRenderer
- 添加相机控件
OrbitControls
- 创建场景
ts
scene = new THREE.Scene()
scene.fog = new THREE.FogExp2(0x000033, 0.1)
camera = new THREE.PerspectiveCamera(
65,
container.value.clientWidth / container.value.clientHeight,
0.1,
1000
)
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
})
renderer.setSize(container.value.clientWidth, container.value.clientHeight)
renderer.setPixelRatio(window.devicePixelRatio)
container.value.appendChild(renderer.domElement)
controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
controls.dampingFactor = 0.05
controls.minDistance = 1.8
controls.maxDistance = 5
- 创建地球模型
- 创建三维球体
new THREE.SphereGeometry
- 加载贴图地球
new THREE.TextureLoader
- 添加高光
new THREE.MeshPhongMaterial
- 球体外围添加一层云层贴图
- 创建三维球体
ts
const geometry = new THREE.SphereGeometry(1, 128, 128)
const textureLoader = new THREE.TextureLoader()
const earthTexture = textureLoader.load(earth_atmos)
const material = new THREE.MeshPhongMaterial({
map: earthTexture,
specular: new THREE.Color(0xaaaaaa),
shininess: 15,
emissive: 0x000000, // 确保地球不自发光
emissiveIntensity: 0
})
earthMesh = new THREE.Mesh(geometry, material)
earthMesh.position.y = yOffest
scene.add(earthMesh)
// 添加云层
const cloudsTexture = textureLoader.load(earth_clouds)
const cloudsMaterial = new THREE.MeshPhongMaterial({
map: cloudsTexture,
transparent: true,
opacity: 0.6,
depthWrite: false
})
const cloudsMesh = new THREE.Mesh(
new THREE.SphereGeometry(1.01, 128, 128),
cloudsMaterial
)
cloudsMesh.position.y = yOffest
scene.add(cloudsMesh)
- 实现飞线
- 创建曲线路径
new THREE.QuadraticBezierCurve3
- 创建曲线材质
new THREE.ShaderMaterial
- 添加箭头
new THREE.ConeGeometry
- 添加文字
new THREE.CanvasTexture
- 创建曲线路径
ts
const curve = new THREE.QuadraticBezierCurve3(
startVec,
new THREE.Vector3().addVectors(startVec, endVec).multiplyScalar(0.5).normalize().multiplyScalar(1.2),
endVec
)
const createFlyLineMaterial = (
startColor: THREE.Color,
endColor: THREE.Color,
lineWidth: number
) => {
return new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 },
startColor: { value: startColor },
endColor: { value: endColor },
opacity: { value: 0.8 },
lineWidth: { value: lineWidth }
},
vertexShader: `
attribute float progress;
varying float vProgress;
void main() {
vProgress = progress;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float time;
uniform vec3 startColor;
uniform vec3 endColor;
uniform float opacity;
uniform float lineWidth;
varying float vProgress;
void main() {
vec3 color = mix(startColor, endColor, vProgress);
float glowIntensity = lineWidth * 0.5;
float glow = sin(5.0 * (vProgress - time * 0.3)) * 0.3 + 0.7;
glow = pow(glow, 2.0) * glowIntensity;
float alpha = mix(0.1, 1.0, vProgress) * opacity * glow;
gl_FragColor = vec4(color * (1.0 + glow * 0.3), alpha);
}
`,
transparent: true,
depthTest: false,
blending: THREE.AdditiveBlending,
linewidth: lineWidth
})
}
const createArrow = (color: THREE.Color) => {
const arrowGeometry = new THREE.ConeGeometry(0.004, 0.02, 8)
const arrowMaterial = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 0.9
})
const arrow = new THREE.Mesh(arrowGeometry, arrowMaterial)
arrow.rotation.x = Math.PI / 2
arrow.position.set(0, -100, 0)
return arrow
}
const texture = new THREE.CanvasTexture(canvas)
const spriteMaterial = new THREE.SpriteMaterial({
map: texture,
transparent: true
})
- 动画优化
- 添加云层自转
requestAnimationFrame
- 添加箭头动画
- 添加云层自转
ts
const animateEarth = () => {
cloudsMesh.rotation.y += 0.0007
animationId = requestAnimationFrame(animateEarth)
}
const animateFlyLines = () => {
const time = Date.now() * 0.0005
flyLines.forEach(flyLine => {
flyLine.material.uniforms.time.value = time
if (flyLine.arrow) {
flyLine.progress = (flyLine.progress + 0.0015) % 1
const point = flyLine.curve.getPoint(flyLine.progress)
const tangent = flyLine.curve.getTangent(flyLine.progress).normalize()
flyLine.arrow.position.copy(point)
flyLine.arrow.lookAt(point.clone().add(tangent))
flyLine.arrow.rotateX(Math.PI / 2)
const scale = 0.8 + Math.sin(flyLine.progress * Math.PI) * 0.5
flyLine.arrow.scale.set(scale, scale, scale)
}
})
}
附上完整代码地址 gitee.com/TriF/3d_ear...