<!--
 * canvas静态路径绘制
  -->
<script lang="ts" setup>
import {
  onMounted,
  ref,
  reactive,
  onBeforeUnmount,
  watch,
  nextTick,
} from 'vue';
import { isEmpty, cloneDeep } from 'src/common/utils';
import { CanvasData } from '../models';

interface Props {
  width: number;
  height: number;
  data: CanvasData[];
  border?: boolean;
  center?: boolean; // 是否居将内容居中展示
  drag?: boolean; // 是否可拖动画布
  zoom?: boolean; // 是否开启鼠标滚轮缩放
  bgUrl?: string;
  bgWidth?: number;
  bgHeight?: number;
}

const props = withDefaults(defineProps<Props>(), {
  width: 700,
  height: 700,
  data: () => [],
  border: true,
  center: false,
  drag: false,
  zoom: false,
  bgUrl: '',
  bgWidth: 0,
  bgHeight: 0,
});

const canvasDom = ref<any>(null);
const boxDom = ref<any>(null);

const img = ref<any>(new Image());
img.value.src = require('./imgs/arrow.png');
const step = 20;
const defaultLineWidth = 10; // 线条宽度,默认为10
const firstObserver = ref(1); // 初始化时的尺寸变化
const timer = ref<any>(null); // 防抖

const wheelDelta = ref<number>(1); // 放大的倍数,初始值1,既不放大,也不缩小
const lastWheelDelta = ref<number>(1);
const wheelDeltaStep = 0.2; // 鼠标每滚动一下,放大的倍数增长
const cursor = ref('default'); // 鼠标样式

const state = reactive({
  ctx: null as any,
  lineWidth: defaultLineWidth,
  styBasic: {
    lineWidth: defaultLineWidth,
    strokeStyle: '#00BB52',
    lineJoin: 'round',
    lineCap: 'round',
  } as any,
  observer: null as any, // 监听元素尺寸变化
  translateX: 0,
  translateY: 0,
  lastTranslateX: 0,
  lastTranslateY: 0,
  canvasW: props.bgWidth || props.width,
  canvasH: props.bgHeight || props.height,
  mousePointX: 0, // 鼠标缩放点的位置
  mousePointY: 0,
  // mousePointScaleX: 0, // 鼠标乘以wheelDelta的位置
  // mousePointScaleY: 0,
});

watch(
  () => props.data,
  () => {
    dataChange();
  }
);

watch(
  () => [props.bgWidth, props.bgHeight],
  ([newW, newH]) => {
    bgSizeChange(newW, newH);
  }
);

onMounted(() => {
  img.value.onload = function () {
    initCanvas();
    getCanvas();
  };
});

onBeforeUnmount(() => {
  offDisconnect();
});

function innitCtx() {
  let ctx = canvasDom.value.getContext('2d');
  state.ctx = ctx;

  // 监听元素尺寸变化
  offDisconnect();
  state.observer = new ResizeObserver(handleResize);
  state.observer.observe(boxDom.value);
}
function initCanvas() {
  if (canvasDom.value) {
    innitCtx();
  } else {
    setTimeout(() => {
      initCanvas();
    }, 1000);
  }
}

function getCanvas(type = '') {
  let DATA = cloneDeep(props.data);
  DATA.map((item: any) => {
    item.data.map((ele: any) => {
      ele.x *= wheelDelta.value;
      ele.y *= wheelDelta.value;
    });
  });
  console.log('数据', DATA);
  // 重置画布偏移位置
  //   state.translateX = 0;
  //   state.translateY = 0;
  //   state.lastTranslateX = 0;
  //   state.lastTranslateY = 0;

  const reData1 = DATA.map((item: any) => {
    return item.data;
  });

  let reData2 = [] as any[];
  reData1.map((item: any) => {
    reData2.push(...item);
  });

  let resData3 = [] as any[];
  let arrX = [] as number[];
  let arrY = [] as number[];
  reData2.map((item: any) => {
    resData3.push(item.x, item.y);
    arrX.push(item.x);
    arrY.push(item.y);
  });

  // 将内容居中展示
  if (type === 'onmousewheel') {
    // 当鼠标滚动放大缩小时不做居中展示处理,要另外计算以鼠标缩放点为中心的偏移值
  } else {
    if (props.center) {
      const maxX = Math.max(...arrX); // x的最大值
      const minX = Math.min(...arrX); // x的最小值
      const maxY = Math.max(...arrY); // y的最大值
      const minY = Math.min(...arrY); // y的最大值
      const absX = maxX - minX;
      const absY = maxY - minY;
      const relativeX = absX / 2; // x的相对中心坐标
      const relativeY = absY / 2; // y的相对中心坐标
      const viewCenterX = props.width / 2; // 可视区域的中心x坐标
      const viewCenterY = props.height / 2; // 可视区域的中心y坐标

      const diffX = viewCenterX - relativeX - minX; // 中心点坐标的差值
      const diffY = 0 - (viewCenterY - relativeY - minY);
      state.translateX = diffX;
      state.translateY = 0 - diffY;
      state.lastTranslateX = state.translateX;
      state.lastTranslateY = state.translateY;
    }
  }

  clearCanvas();
  if (props.bgWidth > 0 && props.bgHeight > 0) {
  } else {
    const max = Math.max(...resData3); // 取最大数据作为cnavas的宽高
    state.canvasW = max + 100; // 额外空间,要考虑算上线条的宽度
    state.canvasH = max + 100; // 额外空间,要考虑算上线条的宽度
  }

  nextTick(() => {
    DATA.map((item: CanvasData) => {
      drawCanvaIten(item);
    });
  });
}

function drawCanvaIten(data: CanvasData) {
  const _data = data.data;
  const style = data.style as any;
  // data里面至少有2个点才能连线
  if (_data.length > 1) {
    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;

      const ctx = state.ctx;
      let _step: number = step; // 箭头间相隔的间距
      // 设置样式
      state.lineWidth = defaultLineWidth;
      for (const key in state.styBasic) {
        ctx[key] = state.styBasic[key];
      }
      if (!isEmpty(style)) {
        state.lineWidth = style.lineWidth || defaultLineWidth;
        _step = style.step || step;
        for (const key in style) {
          ctx[key] = style[key];
        }
      }
      ctx.save();
      ctx.beginPath();
      ctx.moveTo(x1, y1);
      ctx.lineTo(x2, y2);
      ctx.closePath();
      ctx.stroke();
      ctx.restore();

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

      // console.log('斜边长度', size, '斜边分为了几段', part, '旋转度', du);

      // let X = Math.sin(theta * (Math.PI / 180)) * size;
      // let Y = Math.cos(theta * (Math.PI / 180)) * size;
      // console.log('长', X, '高', Y);

      for (let i = 0; i <= part; i++) {
        let part_c = _step * i;
        let part_x = x1 + Math.sin(theta * (Math.PI / 180)) * part_c;
        let 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 = state.lineWidth * 2;
        ctx.drawImage(
          img.value,
          0 - state.lineWidth,
          0 - state.lineWidth,
          doubleLineWidth,
          doubleLineWidth
        );
        ctx.restore();
      }
    }
  }
}

function dataChange() {
  console.log('数据变了');
  if (state.ctx) {
    clearCanvas();
    getCanvas();
  }
}

function onMove(ev: any) {
  if (!props.drag) return;
  state.translateX = ev.offset.x + state.lastTranslateX;
  state.translateY = ev.offset.y + state.lastTranslateY;
  if (ev.isFinal) {
    state.lastTranslateX = state.translateX;
    state.lastTranslateY = state.translateY;
  }
}

function clearCanvas() {
  if (state.ctx) {
    state.ctx.clearRect(0, 0, state.canvasW, state.canvasW);
  }
}

function handleResize() {
  if (firstObserver.value > 1) {
    console.log('尺寸变了');
    getCanvas();
  }
  firstObserver.value++;
}

function bgSizeChange(w: any, h: any) {
  console.log('背景图片的尺寸变了');
  state.canvasW = w * wheelDelta.value;
  state.canvasH = h * wheelDelta.value;
  getCanvas();
}

// 缩放
function onMousewheel(ev: any) {
  if (!props.zoom) return;

  cursor.value = 'progress';
  state.mousePointX = ev.layerX;
  state.mousePointY = ev.layerY;
  if (ev.wheelDelta > 0) {
    wheelDelta.value += wheelDeltaStep;
    console.log('放大');
  } else {
    // 如果是0.51 则 0.51-0.2 = 0.31 ,所以缩小的最小倍数是0.3倍左右
    wheelDelta.value =
      wheelDelta.value < 0.5 ? 0.3 : wheelDelta.value - wheelDeltaStep;
    console.log('缩小');
  }
  debounce(() => {
    const relativeX = state.mousePointX - state.translateX;
    const relativeY = state.mousePointY - state.translateY;

    const diffScale = wheelDelta.value / lastWheelDelta.value;
    const relativeScaleX = relativeX * diffScale;
    const relativeScaleY = relativeY * diffScale;

    // state.mousePointScaleX = relativeScaleX + state.translateX;
    // state.mousePointScaleY = relativeScaleY + state.translateY;

    const diffTranslateX = relativeScaleX - relativeX;
    const diffTranslateY = relativeScaleY - relativeY;
    state.translateX -= diffTranslateX;
    state.translateY -= diffTranslateY;

    // console.log(
    //   'x=',
    //   state.mousePointX,
    //   'y=',
    //   state.mousePointY,
    //   'relativeX=',
    //   relativeX,
    //   'relativeY=',
    //   relativeY,
    //   'relativeScaleX=',
    //   relativeScaleX,
    //   'relativeScaleY=',
    //   relativeScaleY,
    //   'diffTranslateX=',
    //   diffTranslateX,
    //   'diffTranslateY=',
    //   diffTranslateY
    // );

    console.log('倍数', wheelDelta.value);
    lastWheelDelta.value = wheelDelta.value;
    state.lastTranslateX = state.translateX;
    state.lastTranslateY = state.translateY;

    // 如果设置了背景图片尺寸
    if (props.bgWidth > 0 && props.bgHeight > 0) {
      state.canvasW = props.bgWidth * wheelDelta.value;
      state.canvasH = props.bgHeight * wheelDelta.value;
    }
    getCanvas('onmousewheel');
    cursor.value = 'default';
  }, 500);
}

function offDisconnect() {
  if (state.observer) {
    state.observer.disconnect();
    state.observer = null;
  }
}

// 防抖
// 触发事件 n 秒后执行函数,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
function debounce(func: any, time: number) {
  // setTimeout需是全局变量
  if (timer.value) {
    clearTimeout(timer.value);
    timer.value = null;
  }
  timer.value = setTimeout(() => {
    func();
    clearTimeout(timer.value);
    timer.value = null;
  }, time);
}
</script>

<template>
  <div
    class="hcy-canvas-box"
    :style="{
      width: props.width + 'px',
      height: props.height + 'px',
      cursor: cursor,
    }"
    :class="[props.border ? 'has-border' : 'hasno-border']"
    ref="boxDom"
    @mousewheel.prevent="onMousewheel"
  >
    <!-- <div
      class="text-box"
      :style="{ top: state.mousePointY + 'px', left: state.mousePointX + 'px' }"
    ></div> -->
    <canvas
      ref="canvasDom"
      id="canvas"
      :width="state.canvasW"
      :height="state.canvasH"
      v-touch-pan.prevent.mouse="onMove"
      :style="{
        transform: `translate(${state.translateX}px, ${state.translateY}px)`,
        backgroundImage: `url(${props.bgUrl})`,
      }"
    ></canvas>
  </div>
</template>

<style lang="scss" scoped>
.text-box {
  width: 10px;
  height: 10px;
  background-color: red;
  position: absolute;
}
.hcy-canvas-box {
  position: relative;
  box-sizing: content-box;
  overflow: hidden;
}
.has-border {
  border: 1px solid;
}
.hasno-border {
  border: 1px solid transparent;
}
#canvas {
  background-color: rgb(248, 246, 241);
  background-size: 100% 100%;
}
</style>