高德地图雷达扫描效果实现

2,917 阅读2分钟
  • 一看就会,十分钟看完。后边用到不挠头发。
  • 这个其实没什么难点,在地图上动效不是很常见,最主要的是我在搜索高德地图雷达扫描, 地图雷达扫描 得到的并没有直接能用的。某篇文章的评论下一位老哥评论说

截屏2021-08-22 下午4.12.45.png 我是很能理解这种心情的,搜索发现有案例,然后发现案例需要加好友获取🤬

开始

地图上加载canvas

  • 注意:文中使用的api版本为 2.0
  • 高德地图的加载,初始化这里就不再叙述了,官网文档写的很清晰此处丢个链接

地图上如何加载canvas

  • 首先还是要先看下官方文档,在图层-》自有数据图层下有一个canvas图层的示例就是我们要找的链接在此,主要就是这段代码
 // 创建一个 canvas图层 
 // canvas 的创建的 canvas 元素
 // bounds 界限 canvas元素展示范围的大小及在地图的那个点展示(个人理解)
 var CanvasLayer = new AMap.CanvasLayer({
    canvas: canvas,
    bounds: new AMap.Bounds(
        [116.328911, 39.937229],
        [116.342659, 39.946275]
    ),
    zooms: [3, 18],
});

// 添加到地图
map.addLayer(CanvasLayer);
  • 现在我们,有了canvas绘制的雷达扫描图以及加载到地图的上的方法。那么就可以开始组合代码了

将雷达扫描图加载到地图上

  • 看似代码很长,主要是创建雷达扫描图的函数比较长,但是无需关心。三个函数
    • initMap 初始化地图 加载完成后执行加载canvas图层的操作。
    • initMyCanvas 创建雷达扫描
    • addTestCanvas 创建canvas图层并将canvas雷达扫描加载到地图
<template>
<div class="mapbox">
  <!-- 地图加载容器 -->
  <div id="map"></div>
   <!-- canvas  雷达扫描图容器 -->
   <canvas id="canvas" width="300" height="300"></canvas>
</div>
</template>

<script>
export default {
  name: "GDMap",
  data () {
    return {
      mapObj: null
    }
  },
  mounted() {
    // 初始化 高德地图
    this.initMap()
  },
  methods: {
    // 地图初始化函数
    initMap() {
      this.mapObj = new AMap.Map('map', {
        viewMode:"3D",
        center: [116.335183, 39.941735],
        zoom: 14
      })

      // 保存this
      let self = this

      // 地图图块加载完成后触发
      this.mapObj.on('complete', function(){
        self.addTestCanvas()
      });
    },

    // 创建 雷达扫描图  https://wjoan.github.io/2017/06/15/canvas/ 由此借鉴
    initMyCanvas(canvas) {
      const c = document.getElementById(canvas);
      let animID = null
      // 未能找到该DOM对象
      if (!c) {
        console.log("[error] can not find id '" + canvas + "'");
        return null;
      }
      // 引用了错误的DOM对象
      if (!c.getContext) {
        console.log("[error] getContext is undefined");
        return null;
      }

      const ctx = c.getContext("2d");

      // 画布的宽高
      const cWidth = c.width;
      const cHeight = c.height;
      // 中心点
      const centerX = c.width / 2;
      const centerY = c.height / 2;
      // 半径
      const radius = centerX * 0.9;

      const drawPoint = function (x, y, n) {
        ctx.lineWidth = 1;
        for (let i = n; i > 0; i--) {
          ctx.beginPath();
          ctx.arc(x, y, n - i, 0, 2 * Math.PI);
          ctx.strokeStyle = "rgba(42,195,39," + i / n + ")";
          ctx.stroke();
        }
      };

      const drawCircle = function (r, lineWidth = 1, color = "#090") {
        ctx.beginPath();
        ctx.setLineDash([]);
        ctx.arc(centerX, centerY, r, 0, 2 * Math.PI);
        ctx.lineWidth = lineWidth;
        ctx.strokeStyle = color;
        ctx.stroke();
      };

      const drawSector = function (sAngle, eAngle) {
        let blob = 50;
        let increase = 0;

        if (sAngle < eAngle) {
          increase = (eAngle - sAngle) / blob;
        } else if (sAngle > eAngle) {
          increase = (Math.PI * 2 - sAngle + eAngle) / blob;
        } else {
          return;
        }

        let angle1 = sAngle;
        let angle2 = sAngle + increase;
        for (let i = 0; i < blob; i++) {
          ctx.beginPath();
          ctx.moveTo(centerX, centerY);
          ctx.arc(centerX, centerY, radius, angle1, angle2);
          ctx.fillStyle = "rgba(42,195,39," + i / blob + ")";
          ctx.fill();
          angle1 = angle2;
          angle2 = angle1 + increase;
          if (angle2 >= Math.PI * 2) {
            ctx.beginPath();
            ctx.moveTo(centerX, centerY);
            ctx.arc(centerX, centerY, radius, angle1, Math.PI * 2);
            ctx.fillStyle = "rgba(42,195,39," + i / blob + ")";
            ctx.fill();
            angle1 = 0;
            angle2 = angle1 + increase;
          }
        }
      };

      const Line = function (x, y, lineDash = [], color = "#396a00", lineWidth = 1) {
        ctx.beginPath();
        ctx.setLineDash(lineDash);
        ctx.moveTo(centerX, centerY);
        ctx.lineTo(x, y);
        ctx.lineWidth = lineWidth;
        ctx.strokeStyle = color;
        ctx.stroke();
      };

      const init = function () {
        // 背景上填充为黑色
        ctx.fillStyle = "transparent";
        ctx.fillRect(0, 0, cWidth, cHeight);
        for (let i = 1; i <= 8; i++) {
          Line(centerX + Math.sin(Math.PI * i / 4) * radius,
              centerY + Math.cos(Math.PI * i / 4) * radius,
              [5, 3], "#396a00");
        }

        for (let i = 1; i <= 15;) {
          Line(centerX + Math.sin(Math.PI * i / 8) * radius,
              centerY + Math.cos(Math.PI * i / 8) * radius,
              [], "#062807");
          i += 2;
        }

        drawCircle(0.9 * centerY, 2.5);
        drawCircle(0.8 * centerY, 2.5, "#042906");
        drawCircle(0.6 * centerY);
        drawCircle(0.3 * centerY);
      };

      return {
        animID: undefined,
        points: [
          [cWidth / 3, cHeight * 3 / 7],
          [cWidth * 4 / 5, cHeight * 6 / 9]
        ],
        addPoints(x, y) {
          this.points.push([x, y]);
        },
        clear() {
          cancelAnimationFrame(animID);   // 停止动画
          ctx.clearRect(0, 0, cWidth, cHeight);  // 清除画布
          this.points = [[cWidth / 3, cHeight * 3 / 7], [cWidth * 4 / 5, cHeight * 6 / 9]];  // 重置默认点
        },
        scan() {
          let angle = Math.PI / 4;
          let scanBegin = 0;
          let scanEnd = angle;
          let pointRadius = 1;
          // 绘制雷达扫描
          let move = () => {
            ctx.clearRect(0, 0, cWidth, cHeight);  // 清除画布
            init();         // 重绘背景
            drawSector(scanBegin, scanEnd);   // 绘制扇形扫描区域
            // drawPoint(cWidth/3, cHeight*3/7, pointRadius);  // 绘制发光点
            // drawPoint(cWidth*4/5, cHeight*6/9, pointRadius);
            for (let p of this.points) {
              drawPoint(p[0], p[1], pointRadius);
            }
            // 改变点的半径以及扇形的角度
            pointRadius += 0.08;
            scanBegin += angle / 25;
            scanEnd = scanBegin + angle;
            // 超过阈值变为初始值
            if (scanBegin >= Math.PI * 2) {
              scanBegin = 0;
              scanEnd = scanBegin + angle;
            }
            if (pointRadius >= 7) pointRadius = 0;
            // 再次绘制
            animID = window.requestAnimFrame(move);
          }

          window.requestAnimFrame = (function () {
            return window.requestAnimationFrame ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                window.oRequestAnimationFrame ||
                window.msRequestAnimationFrame ||
                function (callback) {
                  window.setTimeout(callback, 1000 / 60);
                };
          })();

          animID = window.requestAnimFrame(move);
        },
        stopScan() {
          cancelAnimationFrame(animID);
        }
      }

    },

    // 创建canvas图层,并把雷达扫描图加载到地图中
    addTestCanvas() {
      // 创建图层
      const CanvasLayer = new AMap.CanvasLayer({
        canvas: document.getElementById('canvas'),
        bounds: new AMap.Bounds(
            [116.328911, 39.937229],
            [116.362659, 39.966275]
        ),
        zooms: [3, 18],
      });

      // 将图层加载到地图
      this.mapObj.addLayer(CanvasLayer);

      // 异步加载 其实延迟一秒更好,通过js创建canvas元素则不需要。
      this.$nextTick(() => {
        // 创建雷达扫描图 并开始动画
        this.initMyCanvas('canvas').scan()
      })
    }
  },
  beforeDestroy() {
    // 当地图存在 销毁地图
    if (this.mapObj) {
      this.mapObj.destroy( );
    }
  }
}
</script>

<style lang="stylus" scoped>
.mapbox{
  width 100%
  height 100%
  #map{
    width 100%
    height 100%
  }
}
</style>

其他地图加载canvas

  • 其实都差不多,具体看官网示例,食材都给你了,还能饿着自己?

百度

四维图新

  • 图层-》特殊图层-》canvas source图层 我是官网链接
  • 这个地图估计也没有太高的知名度, 具体的可以百度下, 还有个超图 他俩挺像🤔

文章就到结束了,觉得还行的点个赞啊。

  • 欢迎讨论。