<template>
  <div id="test">
    <el-row type="flex">
      <el-col :span="3" class="leftSide">
        <el-row class="row__opera">
          <el-col :span="24"> 操作面板 </el-col>
          <!-- <el-col :span="24">
            <span class="label__opera">背景地址<font style="color: red">(正常应该不显示)</font></span>
            <el-input type="textarea" v-model="backImage" :autosize="{ minRows: 4, maxRows: 10 }" @change="toPaint" />
          </el-col> -->
          <el-col :span="24">
            <el-button @click="clearAndDestory()" type="primary">清空画布</el-button>
          </el-col>
          <el-col :span="24">
            <span class="label__opera">操作模式</span>
            <el-radio-group v-model="operaType" @change="operaChange">
              <el-radio-button label="draw" disabled>绘制</el-radio-button>
              <el-radio-button label="mark">标记</el-radio-button>
            </el-radio-group>
          </el-col>
          <el-col :span="24" v-if="operaType === 'draw'">
            <span class="label__opera">绘制模式</span>
            <el-radio-group v-model="drawType" @change="drawTypeChange">
              <el-radio-button label="line">线段</el-radio-button>
              <el-radio-button label="rectangle">矩形</el-radio-button>
              <el-radio-button label="word">文字</el-radio-button>
              <el-radio-button label="route">线路</el-radio-button>
            </el-radio-group>
          </el-col>
          <el-col :span="24" v-if="operaType === 'mark'">
            <span class="label__opera">标记模式</span>
            <el-radio-group v-model="markType">
              <el-radio-button label="A">A</el-radio-button>
              <el-radio-button label="B">B</el-radio-button>
              <el-radio-button label="C">C</el-radio-button>
              <el-radio-button label="jc">警察</el-radio-button>
              <el-radio-button label="ba">保安</el-radio-button>
              <el-radio-button label="cc">出场</el-radio-button>
              <el-radio-button label="jchang">进场</el-radio-button>
              <el-radio-button label="jche">警车</el-radio-button>
              <el-radio-button label="shang">上</el-radio-button>
              <el-radio-button label="xia">下</el-radio-button>
              <el-radio-button label="zuo">左</el-radio-button>
              <el-radio-button label="you">右</el-radio-button>
            </el-radio-group>
          </el-col>
          <el-col :span="24">
            <el-table :data="drawList" max-height="200px">
              <el-table-column align="center" label="名称" prop="title"></el-table-column>
            </el-table>
          </el-col>
        </el-row>
      </el-col>
      <el-col :span="21">
        <el-row type="flex" align="middle">
          <el-col :span="24" style="height: 100vh; width: 100%; overflow: auto; text-align: center" id="view">
            <div id="mapView"></div>
            <canvas
              :style="`background:url(${this.backImage});background-size:100% 100%;`"
              :height="viewHeight"
              :width="viewWidth"
              ref="canvas"
              @click="canvasClick"
              @mousedown="mousedown"
              @mousemove="mousemove"
              @mouseup="mouseup"
            ></canvas>
          </el-col>
        </el-row>
        <div ref="wordInput" :style="inputStyle" v-show="inputShow">
          <el-input type="textarea" v-model="inputWord" :autosize="{ minRows: 4, maxRows: 10 }" @change="toPaint" />
        </div>
      </el-col>
    </el-row>
    <el-dialog :visible.sync="routeSelect" title="组成路线">
      <el-row>
        <el-col :span="24">
          标记:
          <el-radio-group v-model="routeObject.mark">
            <el-radio-button label="A">A</el-radio-button>
            <el-radio-button label="B">B</el-radio-button>
            <el-radio-button label="C">C</el-radio-button>
          </el-radio-group>
        </el-col>
        <el-col :span="24">
          <!-- 只将线段拿出 -->
          <el-table :data="drawList.filter((i) => i.type === 'line')" max-height="500px" @selection-change="routeSelectLine">
            <el-table-column align="center" type="selection" width="55"> </el-table-column>
            <el-table-column align="center" label="名称" prop="title"></el-table-column>
          </el-table>
        </el-col>
        <el-col :span="24">
          <el-button @click="makeRoute">组成线路</el-button>
        </el-col>
      </el-row>
    </el-dialog>
  </div>
</template>

<script>
import { EventBus } from './test/eventBus';
const _ = require('lodash');
export default {
  name: 'test',
  props: {},
  components: {},
  data: function () {
    return {
      viewHeight: 500,
      viewWidth: 500,
      canvas: undefined,
      ctx: undefined,
      isDown: false, // 是否开始拖拽
      downX: 0, //鼠标点击图片落下时的位置(X)
      downY: 0, //鼠标点击图片落下时的位置(Y)
      downTime: 0, // 鼠标落下的时间
      endX: 0, // 绘画实时的X位置
      endY: 0, // 绘画实时的Y位置
      drawList: [
        // { startX: 75, startY: 59, endX: 357, endY: 269, type: 'rectangle' },
        // { startX: 98, startY: 21, endX: 282, endY: 198, type: 'rectangle' },
        // { startX: 170, startY: 101, endX: 133, endY: 161, type: 'rectangle' },
        // { startX: 149, startY: 248, endX: 200, endY: 163, type: 'rectangle' },
      ], // 绘制的数据列表
      // 操作面板
      operaType: undefined, // 操作模式: draw:绘制模式;opera:选择模式
      drawType: undefined, // line:线段;rectangle:矩形;word:文字

      drawColor: '#850a1e', // 线条颜色
      drawLineWidth: 1, // 线条宽度

      inputStyle: {}, // 输入框样式
      inputWord: '', // 文字输入内容
      inputShow: false, //文字输入框是否显示
      // 标记
      pointImg: {
        A: require('./test/act.gif'),
        B: require('./test/B.png'),
        C: require('./test/C.png'),
        jc: require('./test/icon/jc.png'), // 警察
        ba: require('./test/icon/ba.png'), // 保安
        cc: require('./test/icon/cc.png'), // 出场
        jchang: require('./test/icon/jchang.png'), // 进场
        jche: require('./test/icon/jche.png'), // 警车
        shang: require('./test/icon/shang.png'), // 上
        xia: require('./test/icon/xia.png'), // 下
        zuo: require('./test/icon/zuo.png'), // 左
        you: require('./test/icon/you.png'), // 右
      },
      markType: undefined, //选择的标记
      backImage: 'http://baoan.fwedzgc.com:8090/bg.jpg',

      // 线路组成
      routeSelect: false,
      routeObject: {
        mark: undefined, //选择路线的标记
        route: [], // 线路的数组
      },
      routes: [], //已组成的线路 {mark,route:[]}
      routeStep: 1,
      routeEvent: [], // 线路定时器存储,用来停止线路
    };
  },
  created() {
    window.vm.inputLog = false; //控制台输出开关,可以在控制台修改这个变量,显示输出内容
  },
  mounted() {
    this.destoryBus();
    this.canvas = this.$refs.canvas;
    this.ctx = this.canvas.getContext('2d');
    this.toPaint();
    this.init();
    this.createBus();
  },
  methods: {
    operaChange(e) {
      if (e !== 'draw') this.leaveDrawMode();
    },
    /**
     * 离开绘制操作模式处理
     */
    leaveDrawMode() {
      this.drawType = undefined;
      this.inputShow = false;
    },
    init() {
      // 模拟请求,还原之前画的
      this.redrawAll();
    },
    // 替换底图
    toPaint() {
      console.log(this.$refs.view);
      const e = document.getElementById('view');
      const h = e.offsetHeight;
      const w = e.offsetWidth;
      this.$set(this, 'viewHeight', h);
      this.$set(this, 'viewWidth', w);
      let img = new Image();
      img.onload = (res) => {
        this.toLog(res);
        // const i = _.head(res.path);
        // this.canvas.height = i.height;
        // this.canvas.width = i.width;
      };
      // img.src = this.backImage;
      // 将底图设置为背景图片
      // this.canvas.style.backgroundImage = `url(${this.backImage})`;
      // this.canvas.style.backgroundSize = '100% 100%';
      // this.canvas.style.backgroundPosition = 'center center';
    },
    /**
     * 鼠标落下(开始拖拽)
     * @param {PointerEvent} e 浏览器指针对象
     */
    mousedown(e) {
      if (!this.drawType) {
        this.toLog('没有选择要绘制的图形');
        return;
      }
      this.isDown = true;
      this.downTime = new Date().getTime();
      this.downX = e.offsetX; // 鼠标落下时的X
      this.downY = e.offsetY; // 鼠标落下时的Y
    },
    /**
     * 鼠标移动(拖拽ing)
     * @param {PointerEvent} e 浏览器指针对象
     */
    mousemove(e) {
      if (!this.isDown) return;
      if (this.drawType === 'line') this.drawLine(e);
      else if (this.drawType === 'rectangle') this.drawRectangle(e);
    },
    /**
     * 鼠标抬起(结束拖拽)
     * @param {PointerEvent} e 浏览器指针对象
     */
    mouseup(e) {
      this.isDown = false;
      // 非线段和矩形外不适用该处理,它们有自己的记入处理
      if (this.drawType !== 'line' && this.drawType !== 'rectangle') return;
      if (!(this.endX > 0 || this.endY > 0)) {
        // 不满足要求,抹去绘制的
        this.clear();
        this.redrawAll();
        return;
      }
      let title = `${this.drawType === 'line' ? '线段' : this.drawType === 'rectangle' ? '矩形' : '绘制'}-${this.drawList.length + 1}`;
      const obj = _.cloneDeep({ startX: this.downX, startY: this.downY, endX: this.endX, endY: this.endY, type: this.drawType, title });
      this.drawList.push(obj);
    },
    /**
     * 画矩形
     * @param {PointerEvent} e 浏览器指针对象
     */
    drawRectangle(e) {
      // 矩形禁止从右下往左上画
      if (this.downX - e.offsetX > 0 || this.downY - e.offsetY > 0) return;
      this.endX = Math.abs(this.downX - e.offsetX);
      this.endY = Math.abs(this.downY - e.offsetY);
      if (this.endX < 10 || this.endY < 10) {
        this.toLog('距离过小,绘制成点,不记录');
        this.endX = 0;
        this.endY = 0;
        return;
      }
      this.clear();
      this.redrawAll();
      this.draw(this.downX, this.downY, this.endX, this.endY);
    },
    /**
     * 画线段
     * @param {PointerEvent} e 浏览器指针对象
     */
    drawLine(e) {
      this.endX = e.offsetX;
      this.endY = e.offsetY;
      if (!(Math.abs(this.downX - e.offsetX) > 10 || Math.abs(this.downY - e.offsetY) > 10)) {
        this.toLog('距离过小,绘制成点,不记录');
        this.endX = 0;
        this.endY = 0;
        return;
      }
      this.clear();
      this.redrawAll();
      this.draw(this.downX, this.downY, this.endX, this.endY);
    },

    /**
     * 绘制线条
     * @param {Number} x 起点x坐标
     * @param {Number} y 起点y坐标
     * @param {Number} endX 终点x坐标
     * @param {Number} endY 终点y坐标
     * @param {String} type 绘制的类型
     * @param {Object} others 其他参数
     */
    draw(x, y, endX, endY, type = this.drawType, others) {
      // 没有种类,无法进行绘制
      if (!type) return;
      // 开始绘制;
      this.ctx.beginPath();
      // 设置线条颜色，必须放在绘制之前
      this.ctx.strokeStyle = this.drawColor;
      // 线宽设置，必须放在绘制之前
      this.ctx.lineWidth = this.drawLineWidth;
      if (type === 'line') {
        this.ctx.moveTo(x, y);
        this.ctx.lineTo(endX, endY);
        this.ctx.stroke();
      } else if (type === 'rectangle') this.ctx.strokeRect(x, y, endX, endY);
      else if (type === 'word') {
        this.ctx.font = '26px Arial bolder';
        this.ctx.fillStyle = 'red';
        this.ctx.fillText(_.get(others, 'word', '未找到文字'), x, y);
      } else if (type === 'mark' && _.get(others, 'img')) {
        this.ctx.drawImage(_.get(others, 'img'), x, y);
      }
    },
    /**
     * 重新绘制所有已保存的形状
     */
    redrawAll() {
      for (const i of this.drawList) {
        const others = _.omit(i, ['startX', 'startY', 'endX', 'endY', 'type']);
        this.draw(i.startX, i.startY, i.endX, i.endY, i.type, others);
      }
    },
    /**
     * 清除画布
     */
    clear() {
      this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    },
    /**
     * 点击事件
     */
    canvasClick(e) {
      if (this.operaType === 'draw') this.clickDraw(e);
      else if (this.operaType === 'mark') this.clickMark(e);
    },
    /**
     * 点击事件-标记操作处理分支
     */
    clickMark(e) {
      if (!this.markType) {
        this.toLog('未选择标记,不能进行标记');
        return;
      }
      const img = this.pointImg[this.markType];
      let i = new Image();
      i.src = img;
      i.onload = () => {
        let title = `标记-${this.drawList.length + 1}`;
        // - i.width / 2 - i.height / 2
        const data = { startX: e.offsetX, startY: e.offsetY, type: 'mark', img: i, title };
        this.drawList.push(data);
        const scale = 0.3; // 缩放比
        const endX = i.width * scale;
        const endY = i.height * scale;
        this.ctx.drawImage(i, data.startX - 20, data.startY - 20, endX, endY);
        // 替代动态标记的方案,因为canvas只会写图片只取第一帧,不是动画,所以此处用的是img替代
        // 当缩放后,会导致位置错乱
        // let div = document.createElement('div');
        // div.style.height = '0px';
        // div.style.width = '0px';
        // div.style.position = 'relative';
        // div.style.top = `${e.offsetY}px`;
        // div.style.left = `${e.offsetX}px`;
        // let iele = document.createElement('img');
        // iele.src = img;
        // div.appendChild(iele);
        // document.getElementById('mapView').appendChild(div);
      };
    },

    /**
     * 点击事件-绘制操作处理分支
     */
    clickDraw(e) {
      if (this.drawType === 'word') {
        if (this.inputShow) {
          this.inputShow = false;
          // 结束输入并保存
          let { x, y } = this.inputStyle;
          if (!x || !y) {
            this.toLog('缺少坐标点,不能记入');
            return;
          }
          if (this.inputWord === '') {
            this.toLog('未输入内容,不能记入');
            return;
          }
          let title = `文字-${this.drawList.length + 1}`;
          const data = { startX: x, startY: y, type: 'word', word: _.clone(this.inputWord), title };
          this.drawList.push(data);
          this.inputStyle = {};
          this.inputWord = '';
          this.redrawAll();
        } else {
          this.inputStyle = this.getInputStyle(e.clientY, e.clientX);
          // 显示输入框开始输入
          this.inputShow = true;
        }
      }
    },
    /**
     * 绘制模式改变
     */
    drawTypeChange(e) {
      if (e !== 'route') return;
      this.routeSelect = true;
    },
    /**
     * 绘制线路
     * @param {Number} routesIndex 线路列表索引
     * @param {Number} routeIndex 线路下第几段路的索引
     */
    async drawRoute(routesIndex = 0, routeIndex = 0) {
      console.log(routesIndex, routeIndex);
      if (!(_.isNumber(routesIndex) && _.isNumber(routeIndex))) {
        console.log('绘制线路参数不是数字,无法找到线路');
        return;
      }
      const { mark, route } = this.routes[routesIndex];
      const img = this.pointImg[mark];
      const routePart = route[routeIndex];
      this.routeStart(routePart, img, routesIndex);
    },

    /**
     * 单路线移动
     * @param {Object} r 线路的部分对象
     * @param {String} img 标记
     * @param {Number} routesIndex 线路列表索引
     *
     */
    async routeStart(r, img, routesIndex) {
      const { startX, startY, endX, endY } = r;
      this.clear();
      this.redrawAll();
      let m = new Image();
      m.src = img;
      m.onload = () => {
        this.ctx.drawImage(m, startX - m.width / 2, startY - m.height / 2);
      };
      let x = _.clone(startX),
        y = _.clone(startY),
        xFinish = false,
        yFinish = false;
      const xspeed = (endX - startX) / 10;
      const yspeed = (endY - startY) / 10;
      let inter = setInterval(() => {
        const { finish: xf, result: xr } = this.computedPos(x, endX, xspeed);
        xFinish = xf;
        x = xr;
        const { finish: yf, result: yr } = this.computedPos(y, endY, yspeed);
        yFinish = yf;
        y = yr;
        this.clear();
        this.redrawAll();
        m = new Image();
        m.src = img;
        m.onload = () => {
          this.ctx.drawImage(m, x - m.width / 2, y - m.height / 2);
        };
        if (xFinish && yFinish) {
          //清除定时器并移除事件队列
          clearInterval(inter);
          const rei = this.routeEvent.findIndex((f) => f === inter);
          if (rei >= 0) {
            this.routeEvent.splice(rei, 1);
          }
          // 找到自己属于哪条线路
          if (!this.routes[routesIndex]) return; // 没找到规划的线路,不用继续了,停止
          const { route } = this.routes[routesIndex];
          const index = route.findIndex((f) => _.isEqual(f, r));
          if (index < 0) return; // 没找到
          if (index + 1 >= route.length) {
            // 说明是最后了,不应该再加了,从0开始了
            EventBus.$emit('routeLoop', { routesIndex, routeIndex: 0, type: 'one' });
          } else {
            // 下一段路
            EventBus.$emit('routeLoop', { routesIndex, routeIndex: index + 1, type: 'one' });
          }
        }
      }, 1000);
      // 将定时器放到存储中
      this.routeEvent.push(inter);
    },

    /**
     * 计算处理并返回结果,针对单轴而言
     * @param {Number} sn 开始数字
     * @param {Number} en 结始数字
     * @param {Number} speed 步进
     */
    computedPos(sn, en, speed) {
      // 不处理的情况
      let res = {};
      if (speed > 0) {
        if (sn + speed < en) {
          res = { finish: false, result: sn + speed };
        } else res = { finish: true, result: en };
      } else {
        if (sn + speed > en) {
          res = { finish: false, result: sn + speed };
        } else res = { finish: true, result: en };
      }

      return res;
    },

    /**
     * 组成线路
     */
    makeRoute() {
      this.routes.push(_.cloneDeep(this.routeObject));
      // 通过总线事件处理
      this.routeObject = {};
      this.routeSelect = false;
      EventBus.$emit('routeLoop', {});
    },
    /**
     * 选择线路
     */
    routeSelectLine(val) {
      this.routeObject.route = val;
    },

    /**
     * 清空画布,删除数据
     */
    clearAndDestory() {
      this.clear();
      this.drawList = [];
      this.routes = [];
    },

    /**
     * 获取输入框样式(主要是定位点)
     */
    getInputStyle(x = 0, y = 0) {
      const style = { height: '200px', width: '200px', position: 'absolute', top: `${x}px`, left: `${y}px`, x: _.clone(this.downX), y: _.clone(this.downY) };
      return style;
    },

    /**
     * 日志输出
     */
    toLog(word) {
      if (window.vm.inputLog) console.log(word);
    },

    /**
     * 创建总线监听
     */
    createBus() {
      EventBus.$on('routeLoop', (data) => {
        const { routesIndex, routeIndex, type = 'all' } = data;
        // type:all/one all:所有的都从头开始; one: 用routesIndex和routeIndex确定是开始第几段路
        if (type === 'one') this.drawRoute(routesIndex, routeIndex);
        else if (type === 'all') {
          // 将所有规划的线路启动
          for (let i = 0; i < this.routes.length; i++) {
            this.drawRoute(i, 0);
          }
        }
      });
    },

    /**
     * 销毁总线
     */
    destoryBus() {
      EventBus.$off();
    },
  },
};
</script>

<style lang="less" scoped>
.leftSide {
  height: 100vh;
  background: #00ff0066;
  text-align: center;
}
.row__opera {
  .label__opera {
    padding: 0 10px;
  }
  .el-col {
    margin-top: 10px;
  }
}
</style>
