当高德地图遇见Three.js:Vue3实战行政区域3D遮罩与法线贴图特效

2025-04-18 0 480
技术架构
vue3+高德地图2.0+threejs
代码步骤
这里我们就用合肥市为主要的地区,将其他地区扣除,首先使用高德的webapi的DistrictSearch功能,使用该功能之前记得检查一下初始化的时候是否添加到plugins中,然后搜索合肥市的行政数据。

district.search(targetArea.name(status, result) => {      if (status === "complete") {        //设置中心点        map.setCenter([result.districtList[0].center.lng, result.districtList[0].center.lat], true);                                                                //整个世界        let outer = [new AMap.LngLat(-36090true), new AMap.LngLat(-360, -90true), new AMap.LngLat(360, -90true), new AMap.LngLat(36090true)];                                //合肥市边界信息        let holes = result.districtList[0].boundaries;        let pathArray = [outer];
        // 绘制行政区划        pathArray.push.apply(pathArray, holes);
            // 在合肥市内描边        let polygon1 = new AMap.Polygon({path: [outer],          strokeColor"#00eeff",          strokeWeight5,          fillOpacity0,        });                                                                    //添加至地图                                polygon.setPath(pathArray);              map.add(polygon);                                                     }    });  
这里就已经有了整个城市的轮廓。
当高德地图遇见Three.js:Vue3实战行政区域3D遮罩与法线贴图特效

我们的需求是自定义背景,首先我们需要获取到除了合肥市以外的地区,这里我们使用threejs,版本是0.157.0,大家这里可以安装和我一样的版本保证不会出错,版本太高会有问题,控制台 npm i three@0.157.0,在代码中import * as THREE from “three”导入,这里我们单独将threejs业务代码分出去。


------继续上面的代码----------
//先获取地图画布的大小const innerHeight = mapRef.value.clientHeight;const innerWidth = mapRef.value.clientWidth;
let options = {    pathArray,    center: [result.districtList[0].center.lng, result.districtList[0].center.lat],    size: {      innerWidth,      innerHeight,    },  };                //这里传入数据,单独写一个js文件来处理threejs业务 ,instace是AMapcreateThreeLayer(instance, map, options);
收集到我们需要的数据之后单独建一个js文件,先安装一个插件叫turf,这个插件非常善于处理地图上的数学、多边型,先 npm i @turf/turf,再导入import * as turf from “@turf/turf,下面开始写逻辑。

import * as THREE from "three";import * as turf from "@turf/turf";
let glLayer;let camera, renderer, scene, raycaster;let customCoords = null;
/** * @description 创建ThreeLayer */export function createThreeLayer(instance, map, options) {  const { polygon, center, size } = options;
  customCoords = map.customCoords;  // 地图中心转换为墨卡托  const centerCoord = customCoords.lngLatToCoord(center);
  // polygon是传入的多边形坐标,是一个非遮罩区域,outer外边框是整个地图的边界  const outer = polygon[0];  const inner = polygon[1];
  // inner的bbox  const turfPoints = inner.map((item) => [item.lng, item.lat]);  const innerBbox = turf.bbox(turf.polygon([turfPoints]));  // 最小正方形  const squared = turf.square(innerBbox);  // 转为4个点  const squaredPoints = [    [squared[0], squared[1]],    [squared[0], squared[3]],    [squared[2], squared[3]],    [squared[2], squared[1]],  ];
  // 多边形坐标转换为世界中的坐标  const innerCoords = inner.map((item) => {    return {      ...item,      coord: customCoords.lngLatToCoord([item.lng, item.lat]),    };  });  const outerCoords = outer.map((item) => {    return {      ...item,      coord: customCoords.lngLatToCoord([item.lng, item.lat]),    };  });                //世界边界点  const squaredPointsCoords = squaredPoints.map((item) => customCoords.lngLatToCoord(item));                        //——--------------------——------------继续写代码的位置-------------------------           }
这里将我们所有使用的点转换好了之后就可以创建自定义图层了,接下来开始绘制,这里我们准备两张图片,一张贴图,一张法线贴图(让图片凹凸部分感光,让画面更有质感),所有步骤的解释都在注释里面。

// 背景贴图  const bgTexture = new THREE.TextureLoader().load("static/three/bg.png");  // 法线背景贴图  const normalTexture = new THREE.TextureLoader().load("static/three/bg_normal.png");
if (!glLayer) {    glLayer = new instance.GLCustomLayer({      zIndex: 10,      init: (gl) => {        camera = new THREE.PerspectiveCamera(45, size.innerWidth / size.innerHeight, 1001 << 30);
        renderer = new THREE.WebGLRenderer({          context: gl, // 地图的 gl 上下文        });                                        renderer.autoClear = false;        scene = new THREE.Scene();        // 环境光照和平行光        let aLight = new THREE.AmbientLight(0xffffff2);        let dLight = new THREE.DirectionalLight(0xffffff);        dLight.intensity = 6;        dLight.position.set(centerCoord[0], -10000050000);        dLight.target.position.set(centerCoord[0], centerCoord[1], 0);        dLight.target.updateMatrixWorld(); 
        scene.add(dLight);        scene.add(aLight);
        // 辅助        // const helper = new THREE.DirectionalLightHelper(dLight, 1);        // scene.add(helper);
                // 辅助坐标轴        // const axesHelper = new THREE.AxesHelper(100000);        // scene.add(axesHelper);


        // 创建遮罩区域,遮罩区域 = outer - inner        const maskShape = new THREE.Shape();
        // 创建outer        maskShape.moveTo(outerCoords[0].coord[0], outerCoords[0].coord[1]);        outerCoords.forEach((item) => {          maskShape.lineTo(item.coord[0], item.coord[1]);        });
        // 创建inner        maskShape.holes.push(new THREE.Path());        maskShape.holes[0].moveTo(innerCoords[0].coord[0], innerCoords[0].coord[1]);        innerCoords.forEach((item) => {          maskShape.holes[0].lineTo(item.coord[0], item.coord[1]);        });
        // 信息添加给多边形        const maskGeometry = new THREE.ShapeGeometry(maskShape);
        // 调整纹理坐标 这里是调整贴图偏移和大小        bgTexture.wrapS = THREE.RepeatWrapping;        bgTexture.wrapT = THREE.RepeatWrapping;        bgTexture.repeat.set(120120);        bgTexture.offset.set(0.8770.275);                                                                               //这里是调整法线贴图的        normalTexture.wrapS = THREE.RepeatWrapping;        normalTexture.wrapT = THREE.RepeatWrapping;        normalTexture.repeat.set(120120);        normalTexture.offset.set(0.8770.275);
        // 调整 UV        maskGeometry.computeBoundingBox();        const bbox = maskGeometry.boundingBox;        let size1 = new THREE.Vector2();        bbox.getSize(size1);
        const uvAttribute = maskGeometry.attributes.uv;
        for (let i = 0; i < uvAttribute.count; i++) {          const uv = new THREE.Vector2().fromBufferAttribute(uvAttribute, i);          uv.x = (uv.x - bbox.min.x) / size1.x; // uv坐标映射          uv.y = (uv.y - bbox.min.y) / size1.y;          uvAttribute.setXY(i, uv.x, uv.y);        }
        uvAttribute.needsUpdate = true;
        const maskMaterial = new THREE.MeshStandardMaterial({          map: bgTexture,          side: THREE.DoubleSide,          transparent: true,          normalMap: normalTexture,          roughness: 0.5,          metalness: 0.5,        });
        const maskMesh = new THREE.Mesh(maskGeometry, maskMaterial);        scene.add(maskMesh);
        
      },
      render: () => {        let { near, far, fov, up, lookAt, position } = customCoords.getCameraParams();        camera.near = near;        camera.far = far;        camera.fov = fov;        camera.position.set(...position);        camera.up.set(...up);        camera.lookAt(...lookAt);        camera.updateProjectionMatrix();        renderer.render(scene, camera);        renderer.resetState();      },    });
    map.add(glLayer);
    const animate = () => {      map.render();      requestAnimationFrame(animate);    };    animate();
    function onWindowResize() {      camera.aspect = size.innerWidth / size.innerHeight;      camera.updateProjectionMatrix();      renderer.setSize(size.innerWidth, size.innerHeight);    }    window.addEventListener("resize", onWindowResize);  }
下面放出效果图。
当高德地图遇见Three.js:Vue3实战行政区域3D遮罩与法线贴图特效

 

因为加上了法线贴图所以他会像这样。

当高德地图遇见Three.js:Vue3实战行政区域3D遮罩与法线贴图特效
当高德地图遇见Three.js:Vue3实战行政区域3D遮罩与法线贴图特效
加入高德地图各种特效之后是这样的效果。
当高德地图遇见Three.js:Vue3实战行政区域3D遮罩与法线贴图特效
素材放在下面了,需要的朋友可以自取哦~
当高德地图遇见Three.js:Vue3实战行政区域3D遮罩与法线贴图特效
当高德地图遇见Three.js:Vue3实战行政区域3D遮罩与法线贴图特效
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

免责声明 1、百创网作为第三方中介平台,依据交易合同(商品描述、交易前商定的内容)来保障交易的安全及买卖双方的权益; 2、非平台线上交易的项目,出现任何后果均与百创网无关;无论卖家以何理由要求线下交易的,请联系管理举报。 3. 百创网网站的资源均由店家上传出售,本站无法判断和识别资源的版权等合法性属性。如果您对本网站上传的信息资源的版权存有异议,请您及时联系 我们。如果需要删除链接,请下载下面的附件,正确填写信息后并发给我们,本站核实信息真实性后,在24小时内对商品进行删除处理。 联系邮箱:baicxx@baicxx.com (相关事务请发函至该邮箱)

百创网-源码交易平台_网站源码_商城源码_小程序源码 行业资讯 当高德地图遇见Three.js:Vue3实战行政区域3D遮罩与法线贴图特效 https://www.baicxx.com/30238.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、百创会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、百创无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在百创上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于百创介入快速处理。
查看详情
  • 1、百创作为第三方中介平台,依据交易合同(商品描述、交易前商定的内容)来保障交易的安全及买卖双方的权益; 2、非平台线上交易的项目,出现任何后果均与百创无关;无论卖家以何理由要求线下交易的,请联系管理举报。
查看详情
  • 免责声明 1、百创网作为第三方中介平台,依据交易合同(商品描述、交易前商定的内容)来保障交易的安全及买卖双方的权益; 2、非平台线上交易的项目,出现任何后果均与百创网无关;无论卖家以何理由要求线下交易的,请联系管理举报。 3. 百创网网站的资源均由店家上传出售,本站无法判断和识别资源的版权等合法性属性。如果您对本网站上传的信息资源的版权存有异议,请您及时联系 我们。如果需要删除链接,请下载下面的附件,正确填写信息后并发给我们,本站核实信息真实性后,在24小时内对商品进行删除处理。 联系邮箱:baicxx@baicxx.com (相关事务请发函至该邮箱)
查看详情

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务

  • 0 +

    访问总数

  • 0 +

    会员总数

  • 0 +

    文章总数

  • 0 +

    今日发布

  • 0 +

    本周发布

  • 0 +

    运行天数

你的前景,远超我们想象