Threejs 实现3D 地图(05)3d 地图进场动画和地图边缘动画

3d 地图进场特效以及地图边缘动画

代码仓库:

King/threejs-3d-map

地图边缘动画核心代码:

const initBorderPoint = () => {
  // 获取地图边界的左边(通过https://datav.aliyun.com/portal/school/atlas/area_generator#2.51/104.299012/33.447963 获取)
  let borderPoints3Array = borderMap.features[0].geometry.coordinates
  let borderPoints = []
  borderPoints3Array.forEach((coordinate) => {
    coordinate.forEach((rows) => {
      rows.forEach(v => {
        // 将坐标转化为 d3的坐标
        const [x_XYZ, y_XYZ] = d3.geoMercator().center([109, 34.5]).scale(1000).translate([0, 0])(v)
        borderPoints.push({x: x_XYZ, y: -y_XYZ})
        // 将转换后的坐标放入到一个数组存储起来(重点!!!)
        linePoints.push([x_XYZ, -y_XYZ, 32])
      })
    });
  });
  opacityGeometry = new THREE.BufferGeometry();
  // 将数组转化为一维数组
  positions = new Float32Array(linePoints.flat(1));
  // 然后3个一组
  opacityGeometry.setAttribute("position", new BufferAttribute(positions, 3));
  opacity = new Float32Array(positions.length).map(() => 0);
  opacityGeometry.setAttribute("aOpacity", new BufferAttribute(opacity, 1));
  // 控制 颜色和粒子大小
  const params = {
    pointSize: 5.0,
    pointColor: '#37ffff'
  }
  // 看不懂写死
  const vertexShader = `
    attribute float aOpacity;
    uniform float uSize;
    varying float vOpacity;

    void main(){
        gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);
        gl_PointSize = uSize;

        vOpacity=aOpacity;
    }
    `
  // 看不懂写死
  const fragmentShader = `
    varying float vOpacity;
    uniform vec3 uColor;

    float invert(float n){
        return 1.-n;
    }

    void main(){
      if(vOpacity <=0.2){
          discard;
      }
      vec2 uv=vec2(gl_PointCoord.x,invert(gl_PointCoord.y));
      vec2 cUv=2.*uv-1.;
      vec4 color=vec4(1./length(cUv));
      color*=vOpacity;
      color.rgb*=uColor;
      gl_FragColor=color;
    }
    `
  // 创建着色器
  const material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    transparent: true, // 设置透明
    uniforms: {
      uSize: {
        value: params.pointSize
      },
      uColor: {
        value: new THREE.Color(params.pointColor)
      }
    }
  })
  //  创建一个点
  opacityPoints = new THREE.Points(opacityGeometry, material)
  // 将这个点放入到场景中
  showScene.add(opacityPoints);
}
const render = () => {
  if (activeProvince === '四川省') {
    renderer.render(ScScene, camera)
    let info = document.querySelector('#info')
    info.style.display = 'none'
    showScene = ScScene
  } else {
    renderer.render(showScene, camera)
  }
  if (!activeProvince) {
    showScene = scene
    renderer.render(showScene, camera)
  }
  // 如果有地图边界所有坐标
  if (linePoints && opacity) {
    // 5234怎么来的:linePoints打印出来我这边有7134个  但是我这边有900个边界点我这边不想显示(也就是我视频下面那一坨很杂碎的地方)所以我截取了5234个
    //currentPos 也就是数组下标 只去前面5234个点
    if (currentPos > 5234) {
      // 移除边缘动画线
      // showScene.remove(opacityPoints)
      currentPos = 0;
    }
    // 20  就是运动的速度  值越大 移动越快
    currentPos += 20;
    // 忘记这是干嘛的
    for (let i = 0; i < 10; i++) {
      opacity[(currentPos - i) % linePoints.length] = 0;
    }
    // // 忘记这是干嘛的
    for (let i = 0; i < 100; i++) {
      opacity[(currentPos + i) % linePoints.length] = i / 50 > 2 ? 2 : i / 50;
    }
    // 让点 动起来
    if (opacityGeometry) {
      opacityGeometry.attributes.aOpacity.needsUpdate = true;
    }
  }
  // 渲染下一帧的时候会调用render函数
  requestAnimationFrame(render)
  tween.update()
  controls.update()
}

进场动画核心代码:

const tween = new Tween({x: 1000, y: 1000, z: 2000}) // 起始位置
    .to(camera.position, 2000) // 最终位置
    .easing(Easing.Quadratic.In) // 过度动画
    .onUpdate((object) => { // 改变摄像机位置
      camera.position.x = object.x
      camera.position.y = object.y
      camera.position.z = object.z
    })
    .start();

主要代码解析:

1、获取边缘地图的所有坐标  转换为 d3坐标

DataV.GeoAtlas地理小工具系列

 2、绘制着色器材质(ShaderMaterial)

three.js docs

3、将着色器材质按照地图边界的坐标跑动 

4、使用Tween动画库或者 gsap 动画库实现摄像机进场动画

下一篇:

Threejs 实现3D 地图(06)3d 地图飞线动画-优快云博客

### Three.js 地图实现概述 Three.js 是一种用于创建展示3D图形的强大JavaScript库[^2]。通过该库可以构建复杂的3D场景,其中包括地图的三维可视化效果。以下是关于如何利用 Three.js 创建 3D 地图的一些核心概念技术细节。 #### 使用 Three.js 构建基础 3D 地图 为了实现一个基本的 3D 地图,开发者通常需要完成以下几个方面的设置: 1. **初始化渲染器、相机场景** 需要先定义 `WebGLRenderer` 渲染器来显示最终的画面,并配置透视摄像机(PerspectiveCamera),以及建立一个空的场景对象 Scene 来容纳所有的几何体其他元素。 ```javascript const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.z = 5; const scene = new THREE.Scene(); ``` 2. **加载地形数据并生成网格模型** 可以使用外部地理空间数据文件(如 GeoJSON 或高度图纹理)作为输入源,将其转换成适合 Three.js 的顶点数组形式。之后调用 BufferGeometry 方法构造对应的多边形表面结构。 ```javascript function createTerrain() { let geometry = new THREE.PlaneBufferGeometry(10, 10, 100, 100); // 平面划分细分为100*100份 // 修改每个顶点的高度值模拟山丘起伏等地貌特征 for (let i = 0; i < geometry.attributes.position.count; ++i){ geometry.attributes.position.setY(i, Math.random()*0.5 - 0.25 ); } geometry.computeVertexNormals(); var material = new THREE.MeshStandardMaterial({ color: "#8B4513", wireframe:true }); return new THREE.Mesh(geometry,material); } const terrainMesh = createTerrain(); scene.add(terrainMesh); ``` 3. **应用材质贴图增强视觉表现力** 对于更真实的地球表面或者城市街区等复杂环境再现,则需引入卫星影像图片或者其他类型的纹理资源绑定到上述生成好的 Mesh 上去形成逼真的外观感受。 ```javascript const textureLoader = new THREE.TextureLoader(); const earthTexture = textureLoader.load('path_to_your_earth_texture.jpg'); const waterNormalMap = textureLoader.load('water_normal_map.png'); const earthMaterial = new THREE.MeshPhongMaterial({ map: earthTexture, bumpMap: waterNormalMap, bumpScale: 0.05 }); const sphereGeo = new THREE.SphereGeometry(1, 64, 64); const globe = new THREE.Mesh(sphereGeo, earthMaterial); scene.add(globe); ``` 4. **动态交互支持** 添加鼠标拖拽旋转视角控制插件 OrbitControls 提供更好的用户体验;另外还可以加入时间轴动画让某些特定部分随时间变化而改变状态比如昼夜交替效果等等。 --- ### 关键技术要点说明 - **性能优化**: 当处理大规模矢量要素集合时应考虑采用 InstancedMesh 技术减少重复绘制开销[^1]。 - **实时更新机制**: 如果涉及到频繁的数据刷新操作则推荐借助 Web Workers 多线程计算减轻主线程压力从而提升流畅度体验. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多喜乐 长安宁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值