<!--
 * canvas 绘制动态路径
  -->
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { ComTitlePage } from 'src/components';

const img = ref<any>(new Image());
img.value.src = require('../icons/arrow.png');
const width = 900;
const height = 600;
const isShowGrid = ref(false);
const step = 40; // 箭头相隔距离
const lineWidth = ref(16); // 线宽
const isRunning = ref(false);

const bigIndex = ref(0); // 当前长线段的下标
const smallIndex = ref(0); // 当前小线段的下标

const carTranslateX = ref(0);
const carTranslateY = ref(0);
const carRotate = ref(0);

const state = reactive({
  canvasDom: null as HTMLCanvasElement | null,
  canvasCtx: null as CanvasRenderingContext2D | null,
  animationCanvasDom: null as HTMLCanvasElement | null,
  animationCanvasCtx: null as CanvasRenderingContext2D | null,
  data: [
    { x: 400, y: 300 },
    { x: 500, y: 300 },
    { x: 500, y: 200 },
    { x: 400, y: 200 },
    { x: 300, y: 200 },
    { x: 300, y: 400 },
    { x: 500, y: 400 },
    { x: 600, y: 400 },
    { x: 600, y: 100 },
    { x: 200, y: 100 },
    { x: 200, y: 500 },
    { x: 700, y: 500 },
    { x: 700, y: 100 },
  ],
  originRunData: [
    { x: 400, y: 300 },
    { x: 500, y: 300 },
    { x: 500, y: 200 },
    { x: 400, y: 200 },
    { x: 300, y: 200 },
    { x: 300, y: 400 },
    { x: 500, y: 400 },
    { x: 600, y: 400 },
  ],
  runData: [],
});

onMounted(() => {
  img.value.onload = function () {
    state.canvasDom = <HTMLCanvasElement>document.getElementById('canvas');
    state.canvasCtx = state.canvasDom.getContext('2d');

    state.animationCanvasDom = <HTMLCanvasElement>(
      document.getElementById('canvas-animation')
    );
    state.animationCanvasCtx = state.animationCanvasDom.getContext('2d');
  };
});

// 显示网格
function showGrid(value: boolean) {
  let canvas = <HTMLCanvasElement>document.getElementById('canvas-grid');
  let pen = <CanvasRenderingContext2D>canvas.getContext('2d');

  if (value) {
    drawGrid('#FD7013', width, height, pen);
  } else {
    pen.clearRect(0, 0, width, height);
  }
}

function onStart() {
  if (state.data.length < 2) return; // data里面至少有2个点才能连线

  // data处理，把同一个放向上的点集合到一个点
  console.log('原始数据', state.data);
  const data = setData(state.data);
  console.log('处理后', data);

  const ctx = <CanvasRenderingContext2D>state.canvasCtx;
  for (let n = 0; n < data.length - 1; n++) {
    const x1 = data[n].x;
    const y1 = data[n].y;
    const x2 = data[n + 1].x;
    const y2 = data[n + 1].y;

    const A = y1 - y2;
    const B = x1 - x2;

    ctx.save();
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.closePath();
    ctx.lineWidth = lineWidth.value;
    ctx.lineCap = 'round';
    ctx.lineJoin = 'round';
    ctx.strokeStyle = '#78C0A8';
    ctx.stroke();
    ctx.restore();

    const abs_x = Math.abs(A);
    const abs_y = Math.abs(B);
    const angle = Math.atan2(A, B);
    const theta = 0 - angle * (180 / Math.PI) - 90;
    // 求斜边长度
    const size = Math.sqrt(abs_x * abs_x + abs_y * abs_y);
    // 把斜边分成了几段
    const part = Number(parseInt(String(size / step)));
    const du = 90 - theta;

    for (let i = 0; i <= part; i++) {
      const part_c = step * i;
      const part_x = x1 + Math.sin(theta * (Math.PI / 180)) * part_c;
      const part_y = y1 + Math.cos(theta * (Math.PI / 180)) * part_c;

      ctx.save();
      ctx.translate(part_x, part_y);
      ctx.rotate((Math.PI / 180) * du);
      const doubleLineWidth = lineWidth.value * 2;
      ctx.drawImage(
        img.value,
        0 - lineWidth.value,
        0 - lineWidth.value,
        doubleLineWidth,
        doubleLineWidth
      );
      ctx.restore();
    }
  }
}

function onAnimation() {
  state.runData = setData(state.originRunData);
  console.log('runData ', state.runData);

  const ctx = <CanvasRenderingContext2D>state.animationCanvasCtx;
  const { runData } = state;
  if (runData.length < 2) return;

  // 这是一段长线段，动画需要把长段分成若干小段
  const step = 2; // 动画每次绘制的步长
  const arrowStep = 40; // 箭头相隔距离
  const stepDiff = Math.floor(arrowStep / 2); // 每隔 stepDiff 个小段，绘制一个箭头

  const x1 = runData[bigIndex.value].x;
  const y1 = runData[bigIndex.value].y;
  const x2 = runData[bigIndex.value + 1].x;
  const y2 = runData[bigIndex.value + 1].y;
  const A = x2 - x1;
  const B = y2 - y1;
  const angle = Math.atan2(B, A); // 两点间的弧度值
  const c = Math.sqrt(Math.pow(A, 2) + Math.pow(B, 2)); // 斜边长度
  const stepSize = Math.ceil(c / step); // 把斜边分为了几段

  isRunning.value = true;
  window.requestAnimationFrame(() =>
    draw(ctx, smallIndex.value, x1, y1, x2, y2, angle, stepSize, bigIndex.value)
  );

  function draw(
    ctx: CanvasRenderingContext2D,
    i: number,
    x1: number,
    y1: number,
    x2: number,
    y2: number,
    angle: number,
    stepSize: number,
    index: number
  ) {
    if (isRunning.value) {
      const current_x1 = x1;
      const current_y1 = y1;
      let current_x2, current_y2;
      if (i === 0) {
        // 这里是覆盖了之前的路径，如果按分成的小段来画，中间连接不顺滑
        current_x2 = x1 + Math.cos(angle) * ((i + 1) * step);
        current_y2 = y1 + Math.sin(angle) * ((i + 1) * step);
      } else if (i === stepSize - 1) {
        current_x2 = x2;
        current_y2 = y2;
      } else {
        current_x2 = x1 + Math.cos(angle) * ((i + 1) * step);
        current_y2 = y1 + Math.sin(angle) * ((i + 1) * step);
      }

      ctx.save();
      ctx.beginPath();
      ctx.moveTo(current_x1, current_y1);
      ctx.lineTo(current_x2, current_y2);

      // 小车的运动
      // 减去小车宽高的一半
      carTranslateX.value = current_x2 - 20;
      carTranslateY.value = current_y2 - 20;
      carRotate.value = (angle * 180) / Math.PI;

      // 箭头路径
      if (i % stepDiff === 0) {
        const arrowCtx = <CanvasRenderingContext2D>state.canvasCtx;
        const doubleLineWidth = lineWidth.value * 2;

        // 旋转中心
        const center = {
          x: current_x2,
          y: current_y2,
        };
        const offset = rotateOriginOffset(width, center, angle);
        arrowCtx.save();
        arrowCtx.translate(offset.x, offset.y); // 改变旋转中心
        arrowCtx.rotate(angle);
        arrowCtx.drawImage(
          img.value,
          current_x2 - lineWidth.value,
          current_y2 - lineWidth.value,
          doubleLineWidth,
          doubleLineWidth
        );
        // arrowCtx.strokeRect(
        //   current_x2 - lineWidth.value,
        //   current_y2 - lineWidth.value,
        //   doubleLineWidth,
        //   doubleLineWidth
        // );
        arrowCtx.restore();
      }

      ctx.lineWidth = lineWidth.value;
      ctx.lineCap = 'round';
      ctx.lineJoin = 'round';
      ctx.strokeStyle = '#EA7070';
      ctx.stroke();
      ctx.restore();

      i++;

      if (i < stepSize) {
        window.requestAnimationFrame(() =>
          draw(ctx, i, x1, y1, x2, y2, angle, stepSize, index)
        );
      } else {
        if (index === runData.length - 2) {
          isRunning.value = false;
          console.log('运动结束');
        } else {
          const x1 = runData[index + 1].x;
          const y1 = runData[index + 1].y;
          const x2 = runData[index + 2].x;
          const y2 = runData[index + 2].y;
          const A = x2 - x1;
          const B = y2 - y1;
          const angle = Math.atan2(B, A); // 两点间的弧度值
          const c = Math.sqrt(Math.pow(A, 2) + Math.pow(B, 2)); // 斜边长度
          const stepSize = Math.ceil(c / step); // 把斜边分为了几段
          draw(ctx, 0, x1, y1, x2, y2, angle, stepSize, index + 1);
        }
      }
    } else {
      bigIndex.value = index;
      smallIndex.value = i;
      console.log('停止>>>', '当前长线段的下标', index, '当前小线段的下标', i);
    }
  }
}

function onStop() {
  isRunning.value = false;
}

// data处理，把同一个放向上的点集合到一个点
function setData(data: any[]) {
  let DATA = JSON.parse(JSON.stringify(data));

  if (data.length < 2) return DATA;

  let arr = [] as any[];
  for (let i = 0; i < DATA.length; i++) {
    const x1 = DATA[i].x;
    const y1 = DATA[i].y;
    if (i === 0) {
      // 第一个点要添加进去
      arr.push({ x: x1, y: y1 });
    } else if (i === DATA.length - 1) {
      // 最后一个点要添加进去
      arr.push({ x: x1, y: y1 });
    } else {
      const last_x = DATA[i - 1].x;
      const last_y = DATA[i - 1].y;
      const next_x = DATA[i + 1].x;
      const next_y = DATA[i + 1].y;
      const A = x1 - last_x;
      const B = y1 - last_y;
      const angle = Math.atan2(B, A);
      const C = next_x - x1;
      const D = next_y - y1;
      const angle2 = Math.atan2(D, C);
      // console.log('当前', x1, y1, angle, angle2);

      if (angle !== angle2 && !(x1 === next_x && y1 === next_y)) {
        arr.push({ x: x1, y: y1 });
      }
    }
  }
  return arr;
}

function clearArrowCanvas() {
  const ctx = <CanvasRenderingContext2D>state.canvasCtx;
  ctx.clearRect(0, 0, width, height);
}

// 绘制网格
function drawGrid(
  color: string,
  w: number,
  h: number,
  pen: CanvasRenderingContext2D
) {
  const step = 100;
  const w_l = w / step;
  const h_l = h / step;

  pen.save();
  // 横着的线
  for (let i = 0; i <= h_l; i++) {
    pen.beginPath();
    pen.strokeStyle = color;
    pen.moveTo(0, i * step);
    pen.lineTo(w, i * step);
    pen.stroke();
  }
  // 竖着的线
  for (let i = 0; i <= w_l; i++) {
    pen.beginPath();
    pen.moveTo(i * step, 0);
    pen.lineTo(i * step, h);
    pen.stroke();
  }
  pen.restore();
}

// 重载
function onReload() {
  document.location.reload();
}

/**
 * canvas旋转中心偏移值
 * @param width canvas宽
 * @param center 中心坐标点 center:{x:100,y:100}
 * @param arc 旋转的弧度值
 */
function rotateOriginOffset(
  width: number,
  center: { x: number; y: number },
  arc: any
) {
  const r1 = width - center.x;
  const xRes1 = Math.cos(arc) * r1;
  const yRes1 = Math.sin(arc) * r1;
  const x1 = center.x + xRes1;
  const y1 = center.y + yRes1;

  const x0 = width;
  const y0 = center.y;
  const c0 = Math.sqrt(Math.pow(x0, 2) + Math.pow(y0, 2));
  const arc0 = Math.atan2(y0, x0);
  const arc_0 = arc0 + arc;
  const y2 = Math.sin(arc_0) * c0;
  const x2 = y2 / Math.tan(arc_0);

  const xLength = x1 - x2;
  const yLength = y1 - y2;
  return {
    x: xLength,
    y: yLength,
  };
}
</script>
<template>
  <div class="fit">
    <com-title-page title="canvas 绘制动态路径" />
    <div class="btns q-my-sm">
      <div class="q-gutter-sm">
        <q-btn color="primary" label="重载" @click="onReload" />
        <q-toggle
          v-model="isShowGrid"
          label="网格"
          @update:model-value="showGrid"
        />
        <q-btn color="primary" label="显示箭头静态路径" @click="onStart" />
        <q-btn
          color="primary"
          label="清除箭头静态路径"
          @click="clearArrowCanvas"
        />
        <q-btn
          style="background: #ea7070; color: white"
          label="动画"
          @click="onAnimation"
        />

        <q-btn
          style="background: #ea7070; color: white"
          label="停止"
          @click="onStop"
        />

        <q-btn
          :loading="isRunning"
          style="background: #ea7070; color: white"
          label="状态"
        />
      </div>
    </div>
    <div class="content">
      <div
        class="canvas-box"
        :style="{ width: width + 'px', height: height + 'px' }"
      >
        <canvas
          id="canvas-animation"
          :width="width"
          :height="height"
          style="position: absolute; top: 0; left: 0"
        ></canvas>
        <canvas
          id="canvas"
          :width="width"
          :height="height"
          style="position: absolute; top: 0; left: 0"
        ></canvas>
        <canvas
          id="canvas-grid"
          :width="width"
          :height="height"
          style="position: absolute; top: 0; left: 0"
        ></canvas>
        <!-- 小车图标图层 -->
        <div
          :style="{
            position: 'absolute',
            top: 0,
            left: 0,
            width: width + 'px',
            height: height + 'px',
          }"
        >
          <div
            class="car"
            :style="{
              transform: `translate(${carTranslateX}px, ${carTranslateY}px) rotate(${carRotate}deg)`,
            }"
          ></div>
        </div>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.canvas-box {
  position: relative;
  box-sizing: border-box;
  border: 1px solid red;
}
.car {
  position: absolute;
  top: 0;
  left: 0;
  width: 40px;
  height: 40px;
  background-image: url('../icons/car.svg');
  background-size: 100% 100%;
}

// .btns {
//   display: flex;
//   flex-flow: row wrap;
// }
// .btns > div {
//   margin-right: 10px;
// }
</style>
