Three.js 跳跃动画教程
2026/7/24 18:12:30 网站建设 项目流程

跳跃动画 ·Jump Animate· ▶ 在线运行案例

  • 案例合集:三维可视化功能案例(threehub.cn)
  • 开源仓库github地址:https://github.com/z2586300277/three-cesium-examples
  • 400个案例代码:网盘链接

你将学到什么

  • ShaderMaterial 自定义着色器实现核心视觉效果
  • OrbitControls 相机轨道交互
  • requestAnimationFrame渲染循环与resize自适应

效果说明

本案例演示跳跃动画效果:基于 WebGL 实现「跳跃动画」可视化效果,附完整可运行源码;核心用到 ShaderMaterial、OrbitControls。建议先打开文首在线案例查看动态画面,再对照下方源码逐步理解。

核心概念

  • Scene / Camera / WebGLRenderer构成最小渲染闭环;大场景可开logarithmicDepthBuffer缓解 Z-fighting。
  • ShaderMaterial通过uniforms+ 自定义 GLSL 控制逐像素/逐点效果;透明粒子常配合depthTest: false
  • OrbitControls提供轨道旋转/缩放;开启enableDamping后需在 animate 中controls.update()

实现步骤

  • 搭建 Scene、PerspectiveCamera、WebGLRenderer,挂载 canvas 并处理resize
  • 定义 uniforms / onBeforeCompile 或 ShaderMaterial,编写 GLSL 与材质参数
  • 创建 OrbitControls(及 Raycaster 等交互控件,若源码包含)
  • requestAnimationFrame循环中更新状态并 render(Cesium 为viewer.render或自动渲染)
  • 代码要点

    import * as THREE from 'three'

    import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' import { GUI } from 'dat.gui'

    const box = document.getElementById('box')

    const scene = new THREE.Scene()

    const camera = new THREE.PerspectiveCamera(50, box.clientWidth / box.clientHeight, 0.1, 2000)

    camera.position.set(10, 10, 10)

    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, logarithmicDepthBuffer: true })

    renderer.setSize(box.clientWidth, box.clientHeight)

    box.appendChild(renderer.domElement)

    new OrbitControls(camera, renderer.domElement)

    scene.add(new THREE.GridHelper(10, 10))

    // uniforms const uniforms = { points: { value: [ new THREE.Vector3(-30, 2.0, 0), new THREE.Vector3(0, 5.0, 0), new THREE.Vector3(30, 10.0, 0) ] }, // 目标点(用于插值过渡) targetPoints: { value: [ new THREE.Vector3(-30, 2.0, 0), new THREE.Vector3(0, 5.0, 0), new THREE.Vector3(30, 10.0, 0) ] }, baseInfluenceRadius: { value: 5.0 }, heightToRadiusRatio: { value: 2.0 }, falloffPower: { value: 2.0 }, baseHeight: { value: 0.0 }, maxHeight: { value: 10.0 }, color1: { value: new THREE.Color(0x0066ff) }, color2: { value: new THREE.Color(0xff3300) }, }

    // 顶点着色器 const vertexShader =uniform vec3 points[3]; uniform float baseInfluenceRadius; uniform float heightToRadiusRatio; uniform float falloffPower; uniform float baseHeight; uniform float maxHeight;

    varying float vHeightRatio;

    float calculateInfluence(vec3 point, vec3 vertex) { float distance2D = length(point.xz - vertex.xz); float dynamicRadius = baseInfluenceRadius + point.y * heightToRadiusRatio; dynamicRadius = max(dynamicRadius, baseInfluenceRadius); if (distance2D > dynamicRadius) return 0.0; float normalizedDistance = distance2D / dynamicRadius; float attenuation = pow(1.0 - normalizedDistance, falloffPower); return attenuation * point.y; }

    void main() { vec4 worldPosition = modelMatrix * vec4(position, 1.0); float heightOffset = 0.0; heightOffset += calculateInfluence(points[0], worldPosition.xyz); heightOffset += calculateInfluence(points[1], worldPosition.xyz); heightOffset += calculateInfluence(points[2], worldPosition.xyz); vHeightRatio = clamp(heightOffset / maxHeight, 0.0, 1.0); vec3 newPosition = position; newPosition.y += heightOffset + baseHeight; gl_Position = projectionMatrixmodelViewMatrixvec4(newPosition, 1.0); }

    // 片段着色器 const fragmentShader =uniform vec3 color1; uniform vec3 color2; varying float vHeightRatio;

    void main() { vec3 color = mix(color1, color2, vHeightRatio); gl_FragColor = vec4(color, 1.0); }

    // 创建网格 const geometry = new THREE.BoxGeometry(200, 10, 1, 200, 1, 1).rotateX(-Math.PI / 2) const material = new THREE.ShaderMaterial({ uniforms, vertexShader, fragmentShader, side: THREE.DoubleSide, wireframe: true }) scene.add(new THREE.Mesh(geometry, material))

    // GUI 参数 const params = { baseInfluenceRadius: 5.0, heightToRadiusRatio: 2.0, falloffPower: 2.0, baseHeight: 0.0, maxHeight: 10.0, autoUpdate: true, interval: 1.0, wireframe: true, color1: '#0066ff', color2: '#ff3300', }

    // GUI 控制 const gui = new GUI() gui.add(params, 'baseInfluenceRadius', 1, 50).name('影响半径').onChange(v => uniforms.baseInfluenceRadius.value = v) gui.add(params, 'heightToRadiusRatio', 0, 10).name('高度放大系数').onChange(v => uniforms.heightToRadiusRatio.value = v) gui.add(params, 'falloffPower', 0.1, 10).name('衰减指数').onChange(v => uniforms.falloffPower.value = v) gui.add(params, 'baseHeight', -5, 5).name('基础高度').onChange(v => uniforms.baseHeight.value = v) gui.add(params, 'maxHeight', 1, 30).name('最大高度').onChange(v => uniforms.maxHeight.value = v) gui.add(params, 'autoUpdate').name('自动更新') gui.add(params, 'interval', 0.3, 5).name('更新间隔(秒)') gui.add(params, 'wireframe').name('线框模式').onChange(v => material.wireframe = v) gui.addColor(params, 'color1').name('低点颜色').onChange(v => uniforms.color1.value.set(v)) gui.addColor(params, 'color2').name('高点颜色').onChange(v => uniforms.color2.value.set(v))

    // 随机生成目标点 function randomizeTargets() { for (let i = 0; i < 3; i++) { uniforms.targetPoints.value[i].set( Math.random() * 200 - 100, Math.random() * params.maxHeight, 0 ) } }

    // 定时更新目标 let timer = 0 const clock = new THREE.Clock()

    function animate() { requestAnimationFrame(animate) const delta = clock.getDelta()

    // 定时更新目标点 if (params.autoUpdate) { timer += delta if (timer >= params.interval) { timer = 0 randomizeTargets() } }

    // 平滑插值到目标点 for (let i = 0; i < 3; i++) { uniforms.points.value[i].lerp(uniforms.targetPoints.value[i], delta * 3) }

    renderer.render(scene, camera) } animate()

    window.onresize = () => { renderer.setSize(box.clientWidth, box.clientHeight) camera.aspect = box.clientWidth / box.clientHeight camera.updateProjectionMatrix() }

    完整源码:GitHub

    小结

    • 本文提供跳跃动画完整 Three.js 源码与在线 Demo,建议先运行案例再改 uniform/参数做二次实验
    • 更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询