明天的明天 永远的永远 未知的一切 我与你一起承担 ??

是非成败转头空 青山依旧在 几度夕阳红 。。。
  博客园  :: 首页  :: 管理

高德地图 平滑缩放(WEB、小程序 )

Posted on 2025-04-15 10:20  且行且思  阅读(110)  评论(0)    收藏  举报
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, viewport-fit=cover" />
  <meta name="apple-mobile-web-app-capable" content="yes" />
  <meta name="apple-mobile-web-app-status-bar-style" content="black" />
  <meta name="mobile-web-app-capable" content="yes" />
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <meta http-equiv="x-ua-compatible" content="IE=edge" />
    <title>地图展示</title>
    <style>
        #container { width: 100%; min-height:600px;}
        .legend {
            background: white;
            padding: 10px;
            box-shadow: 0 2px 6px rgba(0,0,0,0.3);
            border-radius: 4px;
        }
        .legend-item { display: flex; align-items: center; margin: 5px 0; }
        .color-block { width: 20px; height: 20px; margin-right: 8px; }


.custom-info-window {
  position: absolute;
  z-index: 999; /* 确保窗口显示在标记上层 */
  pointer-events: auto; /* 允许点击交互 */
border-radius: 5px;
font-size: 20px;
  padding: 3px 5px;
  background-color: #0009;
  color: #fff;
  border: none;
  box-shadow: none;
  opacity: 0.9;

}
.custom-info-window:before {
  position:absolute;
  pointer-events:none;
  border:8px solid transparent;
  background:transparent;
  content:""
}
.custom-info-window:before {
   left: 50%;
  margin-left: -6px;
  bottom: 0;
  margin-bottom: -16px;
  border-top-color: #0009;
}
    </style>
</head>
<body style="overflow-y: hidden;">
    <div id="container" ></div>
    
    <script src="https://webapi.amap.com/maps?v=2.0&key=xxxxxxxxxxxxxxxxxxx&&plugin=AMap.Scale,AMap.HawkEye,AMap.ToolBar,AMap.MapType,AMap.Geolocation"></script>
    <script>
       document.getElementById('container').style.height = (window.screen.availHeight-80) +'px'; // 设置为200像素
       document.getElementById('container').style.width = (window.screen.availWidth-10) +'px';
     

        // 海口市经纬度
        const haikouCenter = [110.35, 20.02];
        
        // 初始化地图
        const map = new AMap.Map('container', {
            zoom: 12,  // 初始缩放级别
            //viewMode:'3D', // 地图模式
            //zooms:[12,20],//级别
            expandZoomRange:true,
            center: haikouCenter,
            showIndoorMap: false
        });

        
        // 创建缩放控件实例
        var scale = new AMap.Scale({
          visible: false
        }),
        toolBar = new AMap.ToolBar({
            visible: false,
            position: {
              top: '20px',
              left: '20px'
            }
        }),
        mapTypeBar = new AMap.MapType({ 
          defaultType: 0 
        })
        geolocation = new AMap.Geolocation({
        enableHighAccuracy: true, // 是否使用高精度定位,默认true
        timeout: 10000, // 超过10秒后停止定位,默认无穷大
        maximumAge: 0, // 定位结果缓存0毫秒,默认0
        convert: true, // 自动偏移坐标,默认true
        showButton: true, // 显示定位按钮,默认true
        buttonPosition: 'LB', // 定位按钮停靠位置,默认'LB'(左下角)
        showMarker: true, // 定位成功后显示定位点标记,默认true
        showCircle: true, // 显示定位误差圈,默认true
        panel: 'panel-geolocation' // 自定义信息展示面板的id,默认null
      });

        map.addControl(scale);
        map.addControl(toolBar);
        map.addControl(mapTypeBar);
        map.addControl(geolocation);
       
        scale.show();
        toolBar.show();
        mapTypeBar.show();
        geolocation.show();

       
        // 标记点数据
        const markers = [
            {
                id: 'm1', 
                position: [110.35, 20.02],  // 海口市中心
                title: '海口市中心',
                content: '地址:秀英区长滨路市政府办公区'
            },
            {
                id: 'm2', 
                position: [110.293452, 20.031971],  // 万绿园
                title: '万绿园',
                content: '海口市滨海大道中段'
            },
            {
                 id: 'm3', 
                position: [110.32467, 19.97172],  // 观澜湖
                title: '观澜湖旅游度假区',
                content: '龙华区观澜湖大道1号'
            }
        ];


   // 存储所有标记的Map对象
    const markersMap = new Map();

        // 创建标记和信息窗口
    function createMarkersWithWindows() {
      markers.forEach(data => {
        let offsetX = 0; // 水平居中
        
        // 创建标记
        const marker = new AMap.Marker({
          position: data.position,
         icon: new AMap.Icon({  
                           image: data.id == 'm1'?'location.webp':"map1.png", // 自定义图标地址  
                           size: new AMap.Size(36, 36) // 图标尺寸  
                 }),
          //imageSize: new AMap.Size(46, 46), // 原始图片尺寸
          title: data.title,
          offset: new AMap.Pixel(0, 0), // 根据需要调整偏移量
          imageOffset:  data.id == 'm1'?new AMap.Pixel(20, 0):new AMap.Pixel(0, 0),
          extData: { id: data.id } // 存储自定义数据
        });
        map.add(marker);
        markersMap.set(data.id, marker); // 存入Map
       
        // 创建信息窗口DOM    <button onclick="this.parentElement.remove()">×</button>
        const infoWindow = document.createElement('div');
        infoWindow.className = 'custom-info-window';
        infoWindow.innerHTML = `
          <div>${data.title}</div>         
        `;
        document.body.appendChild(infoWindow); // 添加到body避免被地图覆盖

        // 更新窗口位置
        const updatePosition = () => {
          const pixel = map.lngLatToContainer(data.position);          

          // 创建临时DOM测量实际尺寸
          const tempDiv = document.createElement('div9099');
          tempDiv.innerHTML = data.title;
          tempDiv.style.visibility = 'hidden'; // 隐藏临时元素
          // 设置字体大小
          tempDiv.style.fontSize = "20px";
          document.body.appendChild(tempDiv);
          // 等待DOM渲染完成
          setTimeout(() => {
            const contentWidth = tempDiv.offsetWidth;
            const contentHeight = tempDiv.offsetHeight;
            document.body.removeChild(tempDiv); // 清理临时元素
            // 动态计算偏移量
            let offsetX = -contentWidth/2; // 水平居中
            //console.log('offsetX>>>' + offs
            // 
            // 
            // 
            // 
            // 
            // etX)
          
            infoWindow.style.left =  (pixel.x + offsetX + 18) + 'px';         
            infoWindow.style.top = (pixel.y - 30) + 'px'; // 向上偏移
          }, 16);
          
          handleZoomChange();
        };
         // 初始定位
         updatePosition(); 

         marker.on('click', function(e) {  
          //console.log(e);
          //console.log(e.target.getExtData());
          const markid = e.target.getExtData().id; // 获取参数 
          if(markid == "m1")
          {
            var el1 = document.createElement('a');
            //el1.setAttribute("target", "_blank");  //"_blank"
            el1.setAttribute("id", "openWin_markid");
            el1.setAttribute("href", 'vr.html');
            document.body.appendChild(el1);
            document.getElementById("openWin_markid").click();//点击事件
            document.body.removeChild(el1);
          }
          else
          {
            alert('待开发中!!');
          }
          //window.location.href = targetUrl;  
        }); 

        // 监听地图变化事件
        map.on('mapmove', updatePosition);   //监听地图正在移动的事件
        map.on('zoomchange', updatePosition);
      });
    }
    // 执行创建
    createMarkersWithWindows();

     // 确保所有标记完整显示在地图视图中‌
      //map.setFitView();

        // 平滑缩放动画
        let currentZoom = 12;
        const targetZoom = 20;
        const zoomIn = () => {
            if (currentZoom < targetZoom) {
                currentZoom += 1;
                map.setZoom(currentZoom);
                //requestAnimationFrame(zoomIn);  //requestAnimationFrame是浏览器用于定时循环操作的一个接口,类似于setTimeout,主要用途是按帧对网页进行重绘,让各种网页动画效果(DOM动画、Canvas动画、SVG动画、WebGL动画)能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。
               //  setTimeout(requestAnimationFrame(zoomIn), 1000); // 每 300 毫秒缩放一次
               //  alert(2224)
            }
        };

  // 自动缩放函数
    function autoZoom() {
      currentZoom = map.getZoom();
     if (currentZoom < targetZoom) {
       currentZoom += 1;

        map.setZoom(currentZoom);
         setTimeout(() => {
           requestAnimationFrame(autoZoom);
        }, 400);
      }    
  }
        // 2秒后开始执行缩放
        setTimeout(() => {
           //map.setCenter(haikouCenter);
           //autoZoom();
        }, 2000);


 function handleZoomChange() {
    const currentZoom = map.getZoom();
    console.log('Zoom>>>>' + currentZoom)
    const newIcon = currentZoom > 17 ? 'large-icon.png' : 'default-icon.png';
    if(currentZoom > 17)
    {
     changeMarkerIcon('m1', 'map-Bnd.png');
    }
    else
   { 
      const marker = markersMap.get('m1');
      if (marker) {
        marker.setIcon(new AMap.Icon({
          type: 'image',        
           image: 'location.webp', // 自定义图标地址      
          size: new AMap.Size(46, 46), // 设置合适的尺寸
          imageSize: new AMap.Size(46, 46), // 原始图片尺寸
          imageOffset:new AMap.Pixel(-6, 0)
        }));
        marker.setOffset(new AMap.Pixel(0, 0)); 
      }
   }
 }
// 修改指定标记的图标
    function changeMarkerIcon(targetId, newIconUrl) {
      const marker = markersMap.get(targetId);
      if (marker) {
        marker.setIcon(new AMap.Icon({
          type: 'image',        
           image: newIconUrl, // 自定义图标地址      
          size: new AMap.Size(200, 200), // 设置合适的尺寸
          imageSize: new AMap.Size(200, 200), // 原始图片尺寸
        }));
        marker.setOffset(new AMap.Pixel(-75, -20)); 
      }
    }


    //===============================平滑缩放 2 =========================================
    /**
     * 平滑缩放函数
     * @param {number} targetZoom - 目标缩放级别
     * @param {number} duration - 动画时长(毫秒)
     */
    function smoothZoom(targetZoom, duration = 1000) {

      // CSS 过渡动画
      //const container = map.getContainer();
      //container.style.transition = `transform 2000ms cubic-bezier(0.4, 0, 0.2, 1)`;
      //container.style.transform = `scale(10)`;

      const startZoom = map.getZoom();
      const startTime = Date.now();
      let isInterrupted = false;

      // 监听用户交互中断动画
      map.on('movestart', () => { isInterrupted = true; });

      function animate() {
        if (isInterrupted) return; // 用户操作时终止

        const elapsed = Date.now() - startTime;
        const progress = Math.min(elapsed / duration, 1);
        const easedProgress = easeInOutQuad(progress);
        const currentZoom = startZoom + (targetZoom - startZoom) * easedProgress;

        map.setZoom(currentZoom); // 3D 模式下支持浮点

        if (progress < 1) {
          requestAnimationFrame(animate);
          // setTimeout(() => {
          //   requestAnimationFrame(animate);
          // }, 500);
        }
      }
      requestAnimationFrame(animate);
    }

  // 缓动函数(easeInOutQuad)
  function easeInOutQuad(t) {
    return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
  }

  // 调用示例:从当前级别平滑缩放到 15 级,动画时长 1.5 秒
  setTimeout(() => {
   smoothZoom(20, 6000);
 }, 1500);
 

    
    </script>
</body>
</html>