使用canvas画迁徙线并加上动态效果与小飞机图标

2023-10-27

首先在页面中放上地图图片,并建立三个canvas标签,分别用于点、迁徙线、动态效果

   <div class="mapBox">
      <div class="map">
        <img src="@/assets/shanxi.svg" alt="">
      </div>
            <!-- 线 -->
      <canvas id="canvas" class="canvas"></canvas>
      <!---->
      <canvas id="canvasPoint" class="canvas"></canvas>
      <!-- 动态效果 -->
      <canvas id="canvasMove" class="canvas"></canvas>
    </div>

在这里插入图片描述
初始化canvas

初始化时,需要给各个canvas画布一个确定的宽高,否则画布会使用固定的宽300高150,这时如果另外使用的css给canvas画布制定宽高样式,会导致画布被拉伸,里面内容也会跟着被拉伸

  data() {
    return {
      canvas: null,
      canvasPoint: null,
      canvasMove: null,
      center: {}, // 迁徙线起点位置
      directionArr: [], // 迁徙线终点位置
      endKeep: [], // 保存一下各个迁徙线起点
      end: [], // 运动中的各迁徙线时间p时所在位置
      p: 0, // 时间记录,每到1时变为0
      step: 0.005, // 时间每次递增量
      animationSpeed: 0.03, // 点动画效果圆圈每次增加量
      dotNumber: 25, // 动画迁徙线 动态的线的部分由多少个点组成
      rate: 1.053, // 1.033 贝塞尔曲线计算时用到的参数
      requestAnimationFrameName: '',
      compareData: [ // 用于临时计算各终点位置的参数
        { x: 0.65, y: 0.89 },
        { x: 0.094, y: 0.76 },
        { x: 0.95, y: 0.28 },
        { x: 0.19, y: 0.19 },
        { x: 0.49, y: 0.08 }
      ]
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      // 获取需要画布达到的宽高数据
      const mapBox = document.getElementsByClassName('mapBox')[0];
      const width = mapBox.offsetWidth;
      const height = mapBox.offsetHeight;
      // 拿到三个画布,给定宽高
      const canvas = document.getElementById('canvas');
      const canvasPoint = document.getElementById('canvasPoint');
      const canvasMove = document.getElementById('canvasMove');
      canvas.width = width;
      canvas.height = height;
      canvasPoint.width = width;
      canvasPoint.height = height;
      canvasMove.width = width;
      canvasMove.height = height;
      this.canvas = canvas.getContext('2d');
      this.canvasPoint = canvasPoint.getContext('2d');
      this.canvasMove = canvasMove.getContext('2d');
      // 找到所有迁徙线起点,项目中我的起点是太原,所以大概找到一下
      this.center = {
        x: Math.ceil(width * 0.52),
        y: Math.ceil(height * 0.48)
      };
      // 各线终点 以下仅为参考,具体以项目要求为准
      for (let i = 0; i<= 4; i++) {
        this.directionArr[i] = {
          x: Math.ceil(width * compareData[index].x),
          y: Math.ceil(height * compareData[index].y)
        }
        this.endKeep[index] = {
          x: this.center.x,
          y: this.center.y
        };
      }
      this.end = JSON.parse(JSON.stringify(this.endKeep));
    },

画布一:固定的迁徙线画布

drawAllLine() {
  // 根据每个点分别画线
  this.directionArr.forEach(item => {
    this.drawLine(item);
  });
},
drawLine({ x, y }) {
      this.canvas.beginPath();
      this.canvas.moveTo(this.center.x, this.center.y); // 起始点(x,y)
      // 计算贝塞尔曲线控制点位置
      const coord = this.calcCp([x, y], [this.center.x, this.center.y]);
      this.canvas.quadraticCurveTo(coord.x, coord.y, x, y); //创建二次贝塞尔曲线
      // 线宽1
      this.canvas.lineWidth = 1;
      // 线颜色
      this.canvas.strokeStyle = '#5cb85c';
      this.canvas.stroke();
      this.canvas.closePath();
},
/*
 * num: 要被转换的数字
 * exnum: 当前中心坐标 不一定是x还是y
 */
calcCp(start, end) {
  let middleX = 0;
  let middleY = 0;
  if (start[0] > end[0] && start[1] > end[1]) {
    middleX = ((start[0] + end[0]) / 2) * this.rate;
    middleY = ((start[1] + end[1]) / 2) * (2 - this.rate);
  }
  if (start[0] > end[0] && start[1] < end[1]) {
    middleX = ((start[0] + end[0]) / 2) * this.rate;
    middleY = ((start[1] + end[1]) / 2) * this.rate;
  }
  if (start[0] < end[0] && start[1] > end[1]) {
    middleX = ((start[0] + end[0]) / 2) * (2 - this.rate);
    middleY = ((start[1] + end[1]) / 2) * (2 - this.rate);
  }
  if (start[0] < end[0] && start[1] < end[1]) {
    middleX = ((start[0] + end[0]) / 2) * (2 - this.rate);
    middleY = ((start[1] + end[1]) / 2) * this.rate;
  }
  return {
    x: middleX,
    y: middleY
  };
},

在这里插入图片描述

画布二:各终点效果

data中增加关于点的一些参数

      radius: 1, // 航路点半径
      radiusRing: 1,
      radiusRingMin: 1,
      radiusRingMax: 25, // 最大设为25时,涟漪消失的不会很突兀
      dotColor: '243,254,193',
      ringColor: 'rgba(236,210,32,0.5)'

还需要增加控制动画执行的方法

    drawPoint(x1, y1) {
      // 最里圈小圆
      this.canvasPoint.fillStyle = `rgba(${this.dotColor}, 1)`;
      this.canvasPoint.beginPath();
      this.canvasPoint.arc(x1, y1, this.radius, 0, 2 * Math.PI);
      this.canvasPoint.closePath();
      this.canvasPoint.fill();

      // 外层小圆
      this.canvasPoint.fillStyle = `rgba(${this.dotColor}, 0.3)`;
      this.canvasPoint.beginPath();
      this.canvasPoint.arc(x1, y1, this.accAdd(this.radius, 3), 0, 2 * Math.PI);
      this.canvasPoint.closePath();
      this.canvasPoint.fill();

      // 以下为涟漪部分
      if (this.radiusRing >= this.radiusRingMax) {
        this.radiusRing = this.radiusRingMin;
      }
      this.canvasPoint.fillStyle = this.ringColor;
      this.canvasPoint.beginPath();
      this.canvasPoint.arc(x1, y1, this.radiusRing, 0, 2 * Math.PI);
      this.canvasPoint.closePath();
      this.canvasPoint.fill();
      // this.radiusRing += 0.03;
      this.radiusRing += this.animationSpeed;
      this.ringColor =
        this.ringColor
          .split(',')
          .slice(0, 3)
          .join(',') +
        ',' +
        (0.5 - (this.radiusRing - this.radiusRingMin) * 0.02) +
        ')';
    },
    drawMove() {
      cancelAnimationFrame(this.requestAnimationFrameName);
      // 动态线的画布
      // 点的画布
      this.canvasPoint.clearRect(0, 0, 10000, 10000);
      this.drawPoint(this.center.x, this.center.y);
      this.directionArr.forEach((item) => {
        this.drawPoint(item.x, item.y);
      });
      this.p = this.accAdd(this.p, this.step);
      this.requestAnimationFrameName = requestAnimationFrame(this.drawMove);
    },

在这里插入图片描述
画布三:奔跑的动态线条以及小飞机

此处需要增加一个img标签,放上小飞机图标

目前依然存在飞机图标飞行角度不准确问题,以后有时间再调整

js代码如下

  mounted() {
    this.plane = document.getElementById('airportIcon');
    this.init();
  },
  drawMivie(index) {
      // 获取当前时间p时贝塞尔曲线的x, y点
      const coord = this.calcCp(
        [this.directionArr[index].x, this.directionArr[index].y],
        [this.center.x, this.center.y]
      );
      const x = this.calcRightNow(this.p, this.center.x, coord.x, this.directionArr[index].x);
      const y = this.calcRightNow(this.p, this.center.y, coord.y, this.directionArr[index].y);
      this.canvasMove.beginPath();
      this.canvasMove.moveTo(this.end[index].x, this.end[index].y);
      this.canvasMove.lineTo(x, y);
      const gnt1 = this.canvasMove.createLinearGradient(this.end[index].x, this.end[index].y, x, y);
      gnt1.addColorStop(0, '#fff');
      gnt1.addColorStop(1, '#ECD220');
      this.canvasMove.strokeStyle = gnt1;
      this.canvasMove.lineWidth = 1;
      this.canvasMove.stroke();
      // this.canvasMove.closePath();
      for (var i = 0; i < this.dotNumber; i++) {
        let _t = this.p - this.step * i * 2 >= 0 ? this.p - this.step * i * 2 : 1 + (this.p - this.step * i * 2);
        const coord1 = this.calcCp(
          [this.directionArr[index].x, this.directionArr[index].y],
          [this.center.x, this.center.y]
        );
        const x1 = this.calcRightNow(_t, this.center.x, coord1.x, this.directionArr[index].x);
        const y1 = this.calcRightNow(_t, this.center.y, coord1.y, this.directionArr[index].y);
        this.canvasMove.fillStyle = 'rgba(' + this.dotColor + ',' + (1 - (1 / this.dotNumber) * i) + ')';
        this.canvasMove.beginPath();
        this.canvasMove.arc(x1, y1, 1, 0, 2 * Math.PI);
        this.canvasMove.fill();
        this.canvasMove.closePath();
      }
      // 加个小飞机图标飞起来
      const xx = this.calcRightNow(this.p + this.step * 3, this.center.x, coord.x, this.directionArr[index].x);
      const yy = this.calcRightNow(this.p + this.step * 2, this.center.y, coord.y, this.directionArr[index].y);
      const img = this.createIcon(xx, yy, index);
      this.canvasMove.drawImage(img, xx - 8, yy - 8);
      this.end[index].x = x;
      this.end[index].y = y;
    },
    // 获取当前时间p时贝塞尔曲线的x, y点, 此方法不区分x y
    calcRightNow(p, start, controlPoint, end) {
      return Math.pow(1 - p, 2) * start + 2 * p * (1 - p) * controlPoint + Math.pow(p, 2) * end;
    },
    getAngle(x, y) {
      var radian = Math.atan(y / x); // 弧度
      var angle = Math.floor(180 / (Math.PI / radian)); // 弧度转角度
      if (x < 0) {
        // x小于0的时候加上180°,即实际角度
        angle = angle + 180;
      }
      return angle;
    },
    createIcon(x, y, index) {
      const deg = this.getAngle(x - this.end[index].x, y - this.end[index].y);
      const c = document.createElement('canvas');
      c.width = 16;
      c.height = 16;
      const cCtx = c.getContext('2d');
      cCtx.translate(8, 8);
      if (y < this.end[index].y && ((Math.abs(deg) > 80 && Math.abs(deg) < 91) || (deg > 240 && deg < 270))) {
        cCtx.drawImage(this.plane, -8, -8);
      } else if (x >= this.end[index].x && y < this.end[index].y) {
        cCtx.rotate(((-deg + 20) * Math.PI) / 180);
        cCtx.drawImage(this.plane, -8, -8);
        cCtx.rotate(((deg - 20) * Math.PI) / 180);
      } else if (x < this.end[index].x && y < this.end[index].y) {
        cCtx.rotate(((-deg + 160) * Math.PI) / 180);
        cCtx.drawImage(this.plane, -8, -8);
        cCtx.rotate(((deg - 160) * Math.PI) / 180);
      } else if (x < this.end[index].x && y >= this.end[index].y) {
        cCtx.rotate(((-deg + 45) * Math.PI) / 180);
        cCtx.drawImage(this.plane, -8, -8);
        cCtx.rotate(((deg - 45) * Math.PI) / 180);
      } else {
        cCtx.rotate(((225 - deg) * Math.PI) / 180);
        cCtx.drawImage(this.plane, -8, -8);
        cCtx.rotate(((deg - 225) * Math.PI) / 180);
      }
      return c;
    },
    drawMove() {
      cancelAnimationFrame(this.requestAnimationFrameName);
      // 动态线的画布
      this.canvasMove.clearRect(0, 0, 10000, 10000);
      if (this.p >= 1) {
        this.p = this.step;
        this.end = JSON.parse(JSON.stringify(this.endKeep));
      }
      // 点的画布
      this.canvasPoint.clearRect(0, 0, 10000, 10000);
      this.drawPoint(this.center.x, this.center.y);
      this.directionArr.forEach((item, index) => {
        this.drawMivie(index);
        this.drawPoint(item.x, item.y);
      });
      this.p = this.accAdd(this.p, this.step);
      this.requestAnimationFrameName = requestAnimationFrame(this.drawMove);
    },

在这里插入图片描述

以下为完整代码

<template>
  <div class="box">
    <div class="mapBox">
      <div class="map">
        <img src="@/assets/shanxi.svg" alt="">
      </div>
      <!-- 线 -->
      <canvas id="canvas" class="canvas"></canvas>
      <!---->
      <canvas id="canvasPoint" class="canvas"></canvas>
      <!-- 动态效果 -->
      <canvas id="canvasMove" class="canvas"></canvas>
      <img class="airport" id="airportIcon" src="@/assets/airport.svg" alt="">
    </div>
  </div>
</template>

<script>
export default {
  name: 'homePage',
  data() {
    return {
      canvas: null,
      canvasPoint: null,
      canvasMove: null,
      center: {}, // 迁徙线起点位置
      directionArr: [], // 迁徙线终点位置
      endKeep: [], // 保存一下各个迁徙线起点
      end: [], // 运动中的各迁徙线时间p时所在位置
      p: 0, // 时间记录,每到1时变为0
      step: 0.005, // 时间每次递增量
      animationSpeed: 0.03, // 点动画效果圆圈每次增加量
      dotNumber: 25, // 动画迁徙线 动态的线的部分由多少个点组成
      rate: 1.053, // 1.033 贝塞尔曲线计算时用到的参数
      requestAnimationFrameName: '',
      compareData: [ // 用于临时计算各终点位置的参数
        { x: 0.65, y: 0.89 },
        { x: 0.094, y: 0.76 },
        { x: 0.95, y: 0.28 },
        { x: 0.19, y: 0.19 },
        { x: 0.49, y: 0.08 }
      ],
      radius: 1, // 航路点半径
      radiusRing: 1,
      radiusRingMin: 1,
      radiusRingMax: 25, // 最大设为25时,涟漪消失的不会很突兀
      dotColor: '243,254,193',
      ringColor: 'rgba(236,210,32,0.5)',
      plane: null
    };
  },
  mounted() {
    this.plane = document.getElementById('airportIcon');
    this.init();
  },
  methods: {
    init() {
      // 获取需要画布达到的宽高数据
      const mapBox = document.getElementsByClassName('mapBox')[0];
      const width = mapBox.offsetWidth;
      const height = mapBox.offsetHeight;
      // 拿到三个画布,给定宽高
      const canvas = document.getElementById('canvas');
      const canvasPoint = document.getElementById('canvasPoint');
      const canvasMove = document.getElementById('canvasMove');
      canvas.width = width;
      canvas.height = height;
      canvasPoint.width = width;
      canvasPoint.height = height;
      canvasMove.width = width;
      canvasMove.height = height;
      this.canvas = canvas.getContext('2d');
      this.canvasPoint = canvasPoint.getContext('2d');
      this.canvasMove = canvasMove.getContext('2d');
      // 找到所有迁徙线起点,项目中我的起点是太原,所以大概找到一下
      this.center = {
        x: Math.ceil(width * 0.52),
        y: Math.ceil(height * 0.48)
      };
      // 各线终点 以下仅为参考,具体以项目要求为准
      for (let i = 0; i <= 4; i++) {
        this.directionArr[i] = {
          x: Math.ceil(width * this.compareData[i].x),
          y: Math.ceil(height * this.compareData[i].y)
        }
        this.endKeep[i] = {
          x: this.center.x,
          y: this.center.y
        };
      }
      this.end = JSON.parse(JSON.stringify(this.endKeep));
      // 画线开始
      this.drawAllLine();
    },
    drawAllLine() {
      // 根据每个点分别画线
      this.directionArr.forEach(item => {
        this.drawLine(item);
      });
      this.drawMove();
    },
    drawLine({ x, y }) {
      this.canvas.beginPath();
      this.canvas.moveTo(this.center.x, this.center.y); // 起始点(x,y)
      // 计算贝塞尔曲线控制点位置
      const coord = this.calcCp([x, y], [this.center.x, this.center.y]);
      this.canvas.quadraticCurveTo(coord.x, coord.y, x, y); //创建二次贝塞尔曲线
      // 线宽1
      this.canvas.lineWidth = 1;
      // 线颜色
      this.canvas.strokeStyle = '#5cb85c';
      this.canvas.stroke();
      this.canvas.closePath();
    },
    drawPoint(x1, y1) {
      // 最里圈小圆
      this.canvasPoint.fillStyle = `rgba(${this.dotColor}, 1)`;
      this.canvasPoint.beginPath();
      this.canvasPoint.arc(x1, y1, this.radius, 0, 2 * Math.PI);
      this.canvasPoint.closePath();
      this.canvasPoint.fill();

      // 外层小圆
      this.canvasPoint.fillStyle = `rgba(${this.dotColor}, 0.3)`;
      this.canvasPoint.beginPath();
      this.canvasPoint.arc(x1, y1, this.accAdd(this.radius, 3), 0, 2 * Math.PI);
      this.canvasPoint.closePath();
      this.canvasPoint.fill();

      // 以下为涟漪部分
      if (this.radiusRing >= this.radiusRingMax) {
        this.radiusRing = this.radiusRingMin;
      }
      this.canvasPoint.fillStyle = this.ringColor;
      this.canvasPoint.beginPath();
      this.canvasPoint.arc(x1, y1, this.radiusRing, 0, 2 * Math.PI);
      this.canvasPoint.closePath();
      this.canvasPoint.fill();
      // this.radiusRing += 0.03;
      this.radiusRing += this.animationSpeed;
      this.ringColor =
        this.ringColor
          .split(',')
          .slice(0, 3)
          .join(',') +
        ',' +
        (0.5 - (this.radiusRing - this.radiusRingMin) * 0.02) +
        ')';
    },
    drawMivie(index) {
      // 获取当前时间p时贝塞尔曲线的x, y点
      const coord = this.calcCp(
        [this.directionArr[index].x, this.directionArr[index].y],
        [this.center.x, this.center.y]
      );
      const x = this.calcRightNow(this.p, this.center.x, coord.x, this.directionArr[index].x);
      const y = this.calcRightNow(this.p, this.center.y, coord.y, this.directionArr[index].y);
      this.canvasMove.beginPath();
      this.canvasMove.moveTo(this.end[index].x, this.end[index].y);
      this.canvasMove.lineTo(x, y);
      const gnt1 = this.canvasMove.createLinearGradient(this.end[index].x, this.end[index].y, x, y);
      gnt1.addColorStop(0, '#fff');
      gnt1.addColorStop(1, '#ECD220');
      this.canvasMove.strokeStyle = gnt1;
      this.canvasMove.lineWidth = 1;
      this.canvasMove.stroke();
      // this.canvasMove.closePath();
      for (var i = 0; i < this.dotNumber; i++) {
        let _t = this.p - this.step * i * 2 >= 0 ? this.p - this.step * i * 2 : 1 + (this.p - this.step * i * 2);
        const coord1 = this.calcCp(
          [this.directionArr[index].x, this.directionArr[index].y],
          [this.center.x, this.center.y]
        );
        const x1 = this.calcRightNow(_t, this.center.x, coord1.x, this.directionArr[index].x);
        const y1 = this.calcRightNow(_t, this.center.y, coord1.y, this.directionArr[index].y);
        this.canvasMove.fillStyle = 'rgba(' + this.dotColor + ',' + (1 - (1 / this.dotNumber) * i) + ')';
        this.canvasMove.beginPath();
        this.canvasMove.arc(x1, y1, 1, 0, 2 * Math.PI);
        this.canvasMove.fill();
        this.canvasMove.closePath();
      }
      // 加个小飞机图标飞起来
      const xx = this.calcRightNow(this.p + this.step * 3, this.center.x, coord.x, this.directionArr[index].x);
      const yy = this.calcRightNow(this.p + this.step * 2, this.center.y, coord.y, this.directionArr[index].y);
      const img = this.createIcon(xx, yy, index);
      this.canvasMove.drawImage(img, xx - 8, yy - 8);
      this.end[index].x = x;
      this.end[index].y = y;
    },
    // 获取当前时间p时贝塞尔曲线的x, y点, 此方法不区分x y
    calcRightNow(p, start, controlPoint, end) {
      return Math.pow(1 - p, 2) * start + 2 * p * (1 - p) * controlPoint + Math.pow(p, 2) * end;
    },
    getAngle(x, y) {
      var radian = Math.atan(y / x); // 弧度
      var angle = Math.floor(180 / (Math.PI / radian)); // 弧度转角度
      if (x < 0) {
        // x小于0的时候加上180°,即实际角度
        angle = angle + 180;
      }
      return angle;
    },
    createIcon(x, y, index) {
      const deg = this.getAngle(x - this.end[index].x, y - this.end[index].y);
      const c = document.createElement('canvas');
      c.width = 16;
      c.height = 16;
      const cCtx = c.getContext('2d');
      cCtx.translate(8, 8);
      if (y < this.end[index].y && ((Math.abs(deg) > 80 && Math.abs(deg) < 91) || (deg > 240 && deg < 270))) {
        cCtx.drawImage(this.plane, -8, -8);
      } else if (x >= this.end[index].x && y < this.end[index].y) {
        cCtx.rotate(((-deg + 20) * Math.PI) / 180);
        cCtx.drawImage(this.plane, -8, -8);
        cCtx.rotate(((deg - 20) * Math.PI) / 180);
      } else if (x < this.end[index].x && y < this.end[index].y) {
        cCtx.rotate(((-deg + 160) * Math.PI) / 180);
        cCtx.drawImage(this.plane, -8, -8);
        cCtx.rotate(((deg - 160) * Math.PI) / 180);
      } else if (x < this.end[index].x && y >= this.end[index].y) {
        cCtx.rotate(((-deg + 45) * Math.PI) / 180);
        cCtx.drawImage(this.plane, -8, -8);
        cCtx.rotate(((deg - 45) * Math.PI) / 180);
      } else {
        cCtx.rotate(((225 - deg) * Math.PI) / 180);
        cCtx.drawImage(this.plane, -8, -8);
        cCtx.rotate(((deg - 225) * Math.PI) / 180);
      }
      return c;
    },
    drawMove() {
      cancelAnimationFrame(this.requestAnimationFrameName);
      // 动态线的画布
      this.canvasMove.clearRect(0, 0, 10000, 10000);
      if (this.p >= 1) {
        this.p = this.step;
        this.end = JSON.parse(JSON.stringify(this.endKeep));
      }
      // 点的画布
      this.canvasPoint.clearRect(0, 0, 10000, 10000);
      this.drawPoint(this.center.x, this.center.y);
      this.directionArr.forEach((item, index) => {
        this.drawMivie(index);
        this.drawPoint(item.x, item.y);
      });
      this.p = this.accAdd(this.p, this.step);
      this.requestAnimationFrameName = requestAnimationFrame(this.drawMove);
    },
    /*
     * num: 要被转换的数字
     * exnum: 当前中心坐标 不一定是x还是y
     */
    calcCp(start, end) {
      let middleX = 0;
      let middleY = 0;
      if (start[0] > end[0] && start[1] > end[1]) {
        middleX = ((start[0] + end[0]) / 2) * this.rate;
        middleY = ((start[1] + end[1]) / 2) * (2 - this.rate);
      }
      if (start[0] > end[0] && start[1] < end[1]) {
        middleX = ((start[0] + end[0]) / 2) * this.rate;
        middleY = ((start[1] + end[1]) / 2) * this.rate;
      }
      if (start[0] < end[0] && start[1] > end[1]) {
        middleX = ((start[0] + end[0]) / 2) * (2 - this.rate);
        middleY = ((start[1] + end[1]) / 2) * (2 - this.rate);
      }
      if (start[0] < end[0] && start[1] < end[1]) {
        middleX = ((start[0] + end[0]) / 2) * (2 - this.rate);
        middleY = ((start[1] + end[1]) / 2) * this.rate;
      }
      return {
        x: middleX,
        y: middleY
      };
    },
    accAdd(arg1, arg2) {
      let r1, r2, m;
      try {
        r1 = arg1.toString().split('.')[1].length;
      } catch (e) {
        r1 = 0;
      }
      try {
        r2 = arg2.toString().split('.')[1].length;
      } catch (e) {
        r2 = 0;
      }
      m = Math.pow(10, Math.max(r1, r2));
      return (arg1 * m + arg2 * m) / m;
    },
  }
};
</script>

<style lang="scss" scoped>
.box{
  background-color: #333;
  height: 100vh;
}
.mapBox {
  margin: 100px;
  width: 250px;
  height: 410px;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  .map{
    width: 200px;
    height: 360px;
    img{
      width: 100%;
      height: 100%;
    }
  }
  .canvas{
    position: absolute;
    top: 0;
    left: 0;
  }
  .airport{
    width: 16px;
    height: 16px;
    z-index: -1;
    position: absolute;
  }
}
</style>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用canvas画迁徙线并加上动态效果与小飞机图标 的相关文章

  • 增加 D3 图中边缘的可点击区域

    我有一个由 d3 制作的图 dagre d3 位于其顶部用于绘制有向图 这给出了我对渲染图表的期望 要编辑构建图表的数据 每个元素都是可单击的 这对于使用标签渲染边缘的情况来说很好 但它们并不总是有标签 导致未标记的边缘很难单击进行编辑 分
  • A* 在 HTML5 Canvas 中开始寻路

    我正在尝试在我的游戏中实现 A Start 路径查找 用 JavaScript HTML5 Canvas 编写 A Start 的图书馆发现了这个 http 46dogs blogspot com 2009 10 star pathrout
  • QML改变图像颜色

    我搜索了如何对图像进行着色 格式为 svg 或 png 我尝试用一 个填充图像的矩形覆盖我的图像 但由于我的图像不是矩形 它会给整个矩形着色 而不仅仅是图像 可以用qml改变图像颜色吗 或者 是否可以使用 QPixmap 更改 qt 使用
  • 如何按比例缩放 SVG

    我已从 Illustrator 导出 SVG 并希望设置 SVG 的高度并按比例设置宽度比例 这是 SVG 示例
  • 如何缩放 SVG 路径以适应窗口大小?

    我在缩放 SVG 以适应窗口大小时遇到 了麻烦 在这个例子中 我有一个波浪路径和一个文本元素 我想在这里实现的是沿着波浪路径从左到右移动文本元素 这是由GSAP完成的 并停在路径的中间在初始负载下 当用户开始滚动时 它将移至末尾 我的问题是
  • SVG 过滤器在 Firefox 中不显示,在 Chrome 中工作正常

    我想要深色背景上的一段深色文本外面有白色的光芒 虽然默认阴影滤镜 https developer mozilla org en US docs Web CSS filter drop shadow 2在 CSS 中 比如filter dro
  • 获取SVG绘图的边界框

    我想提取 SVG 绘图的边界框 由于 Python 已经在系统上可用并且还用于执行其他任务 因此我不想使用 JavaScript 或任何其他语言 我的理解是是否可以计算单个元素的边界框 但我不知道如何计算 整个绘图的边界框只是所有元素的最小
  • 如何将多个画布元素放入一个画布元素中?

    我有多个由 JavaScript 创建的画布元素 我的问题是 如何将多个画布元素放入一个画布元素 干得好 JSfiddle http jsfiddle net Allendar UqxCY 它会抓取每个画布并将它们做成 PNG 的 访问缓冲
  • SVG定位

    我正在使用 SVG 但在定位方面遇到了一些问题 我有一系列形状包含在g组标签 我希望像容器一样使用它 这样我就可以设置它的 x 位置 然后该组中的所有元素也会移动 但这似乎不可能 大多数人如何定位您希望串联移动的一组元素 有相对定位的概念吗
  • 使用路径id获取SVG路径d属性

    我有单独的 json 包含 SVG 路径 Id 有没有办法使用路径 id 获取 SVG 路径 d 属性 或者有没有办法只使用路径 ID 来填充 SVG 路径 您可以通过调用 document getElementById 来获取路径元素 然
  • Jest/Enzyme SVG Sprites 意外令牌 <

    我在使用 SVG 精灵在组件上使用 Jest 和 Enzyme 创建快照测试时遇到问题 我正在使用 svg sprite loader 包 https github com kisenka svg sprite loader https g
  • svg 圆形描边上的多种颜色

    我想创建一个彩虹圈 如下图所示 如何绘制弯曲的多色标渐变 这是我当前的代码
  • 为什么在缩放的 html5 画布中可以看到伪像?

    我见过this https stackoverflow com questions 7615009 disable interpolation when scaling a canvas and this https stackoverfl
  • CreateJs Canvas 形状在 Windows Phone 上丢失坐标

    我正在制作一个 Createjs 和 html5 项目 在其中绘制一个形状 红色圆圈 当我单击圆圈时它会发出警报 它在所有台式机和 Android 手机上都能正常工作 除非我在 Windows Phone 中打开它 否则它在普通屏幕上工作正
  • Dimple.js - 将数据标签添加到条形图的每个条形

    我使用的是dimple js 它基于d3 js 是否可以向本示例中提到的条形图的每个条形添加数据标签http dimplejs org examples viewer html id bars vertical http dimplejs
  • 如何获取 html5 画布内像素的像素坐标

    我知道您可以使用 getImageData 和 data 获取 html5 Canvas 内每个像素的值 但是有没有办法获取它们的坐标而不仅仅是它们的值 var w ctx canvas width h ctx canvas height
  • 如何从SurfaceView绘制到Canvas?

    我正在尝试做简单的画家 问题是Android看起来有三个独立的Canvas并给我它来顺序绘制 我用以下方式制作了用户界面SurfaceView 把霍尔德从中拿走 Override protected void onCreate Bundle
  • 如何使用 HTML5 Javascript Canvas 获取三个碰撞形状的交集并删除不碰撞的部分?

    我最近专门针对 KonvaJs 发布了类似的问题here https stackoverflow com questions 64603077 how can i get the intersection of three shapes c
  • 跨浏览器 SVG keepAspectRatio

    我正在尝试有一个SVG里面的图形 img 适合 不裁剪 标签内部且保留宽高比的标签 我创建了SVG在 Inkscape 中 它在所有浏览器上都按预期工作 除了互联网浏览器 9 为了让它发挥作用IE 9我必须添加viewBox 0 0 580
  • webglcontextcreationerror事件:是否同步触发?

    Is the webglcontextcreationerror 事件 https developer mozilla org en US docs Web Events webglcontextcreationerror同步触发还是异步触

随机推荐

  • windows服务程序中创建用户进程

    最近碰到个问题 需要在服务中检测用户桌面的情况 但是服务程序都是SYSTEM账户下运行 属于Session0 不能检测到用户桌面的情况 所以就需要另启一个用户进程来获取这些信息 然后发送给服务 所以就用到了 CreateProcessAsU
  • 卷积神经网络系列之卷积/池化后特征图大小怎么计算??

    1 卷积后的大小 W 矩阵宽 H 矩阵高 F 卷积核宽和高 P padding 需要填充的0的个数 N 卷积核的个数 S 步长 width 卷积后输出矩阵的宽 height 卷积后输出矩阵的高 width W F 2P S 1 向下取整 h
  • 小米路由器mini 安装openWrt+更新源+挂载U盘+安装python

    刚刚入手一个小米路由器mini 本来就是打算装openWrt的 想试试玩玩看 刷openwrt的基本流程是参考的如下博主的文章 http www right com cn forum thread 147929 1 1 html 没有遇到什
  • BUUCTF [极客大挑战 2019]FinalSQL

    极客大挑战 2019 FinalSQL 操作 脚本 总结 操作 打开题目 又是这个鬼 跟着他的流程走 点按钮 让我们试试别的 告诉我们对了 但是不是这张表 埋坑 怀疑这个地址是存在sql注入的 经过fuzz 发现过滤了空格 union之类的
  • DOM方式实现Excel导入

    DOM解析Excel 在我们的工作场景中经常会遇到数据录入的需求 有些批量数据录入太麻烦 就需要用到批量导入的方式来提高效率 这就涉及到读取Excel数据的技术 Appache Poi提供了DOM解析和SAX解析两种方式 本篇主要记录自己工
  • Windows Terminal 安装gsudo插件

    Gsudo Windows下类似于linux的sudo 可用于提权 新建 Windows Terminal 标签页时可以用于新建有管理员的页面 或者直接sudo将当前页面提权 需要在安装过程中把sudo命令和gsudo命令建立关联 Powe
  • elasticsearch python连接池吗_了解Elasticsearch及其与Python的对接实现

    什么是 Elasticsearch 但我们想查数据的时候就免不了搜索 搜索就离不开搜索引擎 百度 谷歌都是一个非常庞大复杂的搜索引擎 他们几乎索引了互联网上开放的所有网页和数据 然而对于我们自己的业务数据来说 肯定就没必要用这么复杂的技术了
  • 使用的工具

    文档 devdocs 开发知识 css tricks css技巧分享 开发工具 可以检测前端代码规范的工具 sonarlint 还未用过 样式工具 collect ui 用来查看设计的ui界面参考 其他工具 虚拟号码生成 https sms
  • CentOS8配置yum/dnf镜像源

    Centos8 dnf命令 DNF意思是 Dandified Yum 这是下一代的yum软件包管理器 Yum的派生 Centos8开始使用dnf工具来管理软件包 它可以在基于RPM的Linux发行版上安装 更新和删除软件包 它会自动计算依赖
  • MATLAB克劳特算法,克劳特(Crout)(LU)分解法求解线性方程组的matlab实现

    克劳特 Crout LU 分解法求解线性方程组的matlab实现 由会员分享 可在线阅读 更多相关 克劳特 Crout LU 分解法求解线性方程组的matlab实现 3页珍藏版 请在人人文库网上搜索 1 1 克劳特 Crout LU 分解法
  • c语言课程主要目的和内容,C语言程序设计课程教学大纲

    C语言程序设计课程教学大纲 C语言程序设计课程教学大纲 一 本课程的性质 目的和任务 1 课程的性质 本课程是计算机科学与技术专业的一门重要的专业基础课程 它既可以为其它专业课程奠定程序设计的基础 又可以作为其它专业课程的程序设计工具 2
  • OpenWRT 增加内核模块及应用方法

    进入package目录 创建模块目录 cd mcp branches V1 1 beta1 mcp package mkdir example 进入example目录 创建Makefile文件和代码路径 cd example touch M
  • VMware虚拟机Linux系统根目录空间扩充操作

    VMWare虚拟机安装的应用多了 导致根目录空间不足 有没有办法可以将根目录空间进行扩充呢 经过搜集各各资料 顺利解决问题 把服务器的空间由6G扩成8G 现将执行全过程总结如下 以 供分享 首先 介绍下大体的解决思路 要想扩充 必须要有一块
  • 最完整的分布式架构设计图谱

    我们身处于一个充斥着分布式系统解决方案的计算机时代 无论是支付宝 微信这样顶级流量产品 还是区块链 IOT 等热门概念 抑或如火如荼的容器生态技术如 Kubernetes 其背后的技术架构核心都离不开分布式系统 为什么要懂分布式架构设计 系
  • CF::B. Odd Swap Sort

    题目大意 有多组测试数据 每组测试数据为一个长度为n的正整数数组 问是否可以通过任意此特定操作 每次操作可以选择挨着的一个为奇数 一个为偶数的两个数交换 使数组变为不严格的升序数组 如果可以的话输出 YES 否则输出 NO time lim
  • git 代码管理工具3

    团队协作分支开发模式 一个好的 github 项目一般都有多个分支 master dev release分支 新建分支 branch git branch branch1 切换到目标分支 git checkout branch1 在本地的b
  • 如何通过轨迹信息判断驾驶人是否为同一人?

    轨迹识别问题旨在验证传入的轨迹是否是由所要求的人员产生 即给定一组单独的人员历史轨迹 例如行人 出租车司机 以及由特定人员生成的一组新轨迹 判定两组轨迹是否由同一个人员生成 这个问题在许多实际应用中都很重要 例如出租车驾驶人员身份认证 汽车
  • 用于独立系统应用的光伏MPPT铅酸电池充电控制器建模(Simulink实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Simulink实现 详细文章 1 概述 本文介绍了MATLAB Simu
  • android开发浅谈之写在前面的话

    自我介绍 先简单的介绍一下我的主要工作经历吧 时间 东家 主要工作 2011年8月 深圳大学毕业 那是安卓开始崛起的前夜 自己整上午整下午的看网上的新品手机 基本上注定了自己从事手机相关的职业选择了 2011年8月 2013年8月 深圳康佳
  • 使用canvas画迁徙线并加上动态效果与小飞机图标

    首先在页面中放上地图图片 并建立三个canvas标签 分别用于点 迁徙线 动态效果 div class mapBox div class map img src assets shanxi svg alt div div