使用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画迁徙线并加上动态效果与小飞机图标 的相关文章

  • 跨浏览器 SVG keepAspectRatio

    我正在尝试有一个SVG里面的图形 img 适合 不裁剪 标签内部且保留宽高比的标签 我创建了SVG在 Inkscape 中 它在所有浏览器上都按预期工作 除了互联网浏览器 9 为了让它发挥作用IE 9我必须添加viewBox 0 0 580
  • WPF 画布缩放/变换以适合

    我重新发布这个问题 因为上次我没有得到太多答复 希望重新措辞可能有所帮助 本质上 我想做的是创建一个数据绑定画布 它将自动缩放其内容以 填充 可用空间 有点像缩放以适应操作 不幸的是 我的 WPF 技能还不是很强 我正在努力弄清楚如何完成最
  • 尝试获取屏幕上绘制的每个随机圆圈的 x、y 坐标

    您好 我正在制作一款游戏 该游戏将在屏幕上创建随机圆圈 随机创建的圆圈的值为红色或绿色 我的问题是 我希望不仅能够确定用户何时单击其中一个圆圈 而且还能够确定他们最终单击的圆圈 红色或绿色 下面是我的代码 我的主要问题是试图找到将要绘制的圆
  • 鼠标移动时画布拖动

    我正在尝试构建一个可以使用鼠标移动拖动的画布 我做了一些我无法理解的错误 因为一开始似乎有效 然后出现了一个增量错误 使画布移动得太快 考虑以下代码 window onload function var canvas document ge
  • 没有宽度/高度的 SVG 以自然尺寸渲染

    我有这个 SVG 但没有width or height属性 我有以下 HTML div class block img src https s3 eu vAmfIxVv kiwi svg div 使用以下CSS block display
  • 带有 angular-cli 的 SVG 图标系统

    我有一个 Angular2 项目 它是通过 Angular CLI 创建的 在 webpack 中 有一个加载器来加载 svg sprite 并从 svgs 列表生成该 sprite 但是 当 angular cli 不允许我更改 webp
  • 画布图像遮罩/重叠

    在我的项目中 我必须使用画布在另一个相同尺寸和图案图像上实现一个不同的颜色图像 并且图像不是圆形或矩形形状 所有这些都是波浪形状的 它将应用于单个主背景图像 以便在每个主背景图像上显示多个图形onclick功能 重叠的图像应更改为另一种选定
  • ie11 中的 SVG 问题

    我有一个 div 其高度设置为 320 像素 然后它的子元素宽度设置为 100 它的子文件是一个 SVG 文件 我将其宽度设置为容器的 200 在运行良好的 Chrome 和 Firefox 中 我得到了如下所示的漂亮图像 HTML 看起来
  • HTML5 画布在缩放和旋转后平移

    我正在尝试用画布做一些事情 首先 我让用户上传图像 如果图像比我想要的大 我需要将其缩小 那部分工作得很好 最近我们遇到了 iPhone 用户上传图像的问题 这些都存在方向问题 我已经弄清楚如何提取方向 我的问题是当我操纵画布中的图像时会发
  • 使用 JavaScript 中的 mousemove 事件在画布内的图像上绘制矩形

    我正在尝试使用 mousemove event 在画布内的图像上绘制一个矩形 但由于clearRect 我在图像上得到了矩形 并且矩形中填充了颜色 谁能帮我解答一下 如何在图像上绘制一个只有边框的矩形 下面是我实现它所遵循的代码 var c
  • D3.以编程方式缩放后使用鼠标滚轮时缩放会跳转

    当我通过单击鼠标缩放到特定位置然后尝试平移或使用鼠标滚轮时 缩放行为会跳跃 看来我的缩放级别正在恢复 就像单击鼠标之前一样 这是我的事件处理程序 function click d var x y k if d centered d var
  • Firefox 上的 SVG 图像遮罩

    最近我创建了六边形 SVG 并计划将其实现为 Firefox 的图像遮罩 目前它只适用于 Chrome http jsfiddle net brokeneye WKEbw http jsfiddle net brokeneye WKEbw
  • 如何制作一条带有边框的 SVG“线”?

    我有一个小 svg 小部件 其目的是显示角度列表 参见图片 现在 角度是线元素 只有描边 没有填充 但现在我想要一个 内部填充 颜色和周围的 描边 边框 我猜 line 元素无法处理这个问题 那么我应该使用什么来代替呢 请注意 线条笔划的线
  • HTML5 使用画布旋转图像

    如何使用画布的 旋转 功能围绕图像中心旋转图像 而不是围绕原点旋转 考虑以下示例
  • 动画 SVG 在页面加载之前不会显示动画

    我的网站包含大量广告 加载需要一段时间 确切地说 这并不是一个问题 但我注意到任何 SVG 动画都会立即绘制第一帧 但动画只有在页面上的所有加载完成后才会出现 SVG 动画通常指示旋转器 加载图标 有没有办法立即启动SVG动画 或者如果我将
  • 在android中的圆形路径上绘制文本

    我需要在圆形路径上绘制文本 我已经尝试过drawTextOnPath 方法 但对于所附图像中的 肥沃窗口 等文本 文本会旋转且不可读 我使用过的代码 customPath2 addArc mCircleRectF 30F 64 28F cu
  • 使用拇指移动变换后的控件会产生奇怪的行为

    当尝试使用拇指在画布上移动控件时 我遇到了奇怪的行为 当我将控件添加到画布并使用 Thumb DragDelta 事件来移动它时 一切看起来都很好 但是 当我对控件应用旋转变换时 拖动它是很奇怪的 控件开始围绕光标旋转 角度越大 圆圈越大
  • 如何计算android中位图擦除区域的百分比?

    我是安卓新手 我正在制作一个可以使用手指擦除画布上的位图的应用程序 像手指画橡皮擦之类的东西 我想计算擦除区域的百分比 例如 60 已从完整图像中擦除 请帮助我做到这一点 提前致谢 我尝试了一些方法 它总是给我 0 它不起作用 请参阅该方法
  • javascript 选择自定义光标 (svg)

    我正在动态地将光标更改为悬停时的本地 svg element on mouseover function this css cursor url svgs pointer svg 9 30 auto 工作正常 但我想选择该 svg 来操纵其
  • 使用velocity.js制作可拖动元素的动画

    我正在使用velocity js 为用户拖动的可拖动 SVG 元素设置动画 然而 velocity js 将先前的 mousemove 坐标排队并通过所有后续的 mousemove 坐标进行动画处理 我想要的是velocity js 不要对

随机推荐

  • 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