import BaseFoundation from "../base/foundation";
import { getMiddle, getAspectHW } from "./utils";
export default class CropperFoundation extends BaseFoundation {
  constructor(adapter) {
    super(Object.assign({}, adapter));
    this.getImgDataWhenResize = ratio => {
      const {
        imgData
      } = this.getStates();
      const newImgData = {
        width: imgData.width * ratio,
        height: imgData.height * ratio,
        centerPoint: {
          x: imgData.centerPoint.x * ratio,
          y: imgData.centerPoint.y * ratio
        }
      };
      this.imgData.scale *= ratio;
      return newImgData;
    };
    this.getCropperBoxWhenResize = (ratio, newContainerData) => {
      const {
        cropperBox
      } = this.getStates();
      const {
        aspectRatio
      } = this.getProps();
      const tempCropperBox = {
        width: cropperBox.width * ratio,
        height: cropperBox.height * ratio,
        centerPoint: {
          x: cropperBox.centerPoint.x * ratio,
          y: cropperBox.centerPoint.y * ratio
        }
      };
      let xMin = tempCropperBox.centerPoint.x - tempCropperBox.width / 2;
      let xMax = tempCropperBox.centerPoint.x + tempCropperBox.width / 2;
      let yMin = tempCropperBox.centerPoint.y - tempCropperBox.height / 2;
      let yMax = tempCropperBox.centerPoint.y + tempCropperBox.height / 2;
      if (aspectRatio) {
        if (xMax > newContainerData.width) {
          xMax = newContainerData.width;
          xMin = tempCropperBox.width > newContainerData.width ? 0 : newContainerData.width - tempCropperBox.width;
          tempCropperBox.width = xMax - xMin;
          tempCropperBox.height = tempCropperBox.width / aspectRatio;
          yMax = yMin + tempCropperBox.height;
        }
        if (yMax > newContainerData.height) {
          yMax = newContainerData.height;
          yMin = tempCropperBox.height > newContainerData.height ? 0 : newContainerData.height - tempCropperBox.height;
          tempCropperBox.height = yMax - yMin;
          tempCropperBox.width = tempCropperBox.height * aspectRatio;
          xMax = xMin + tempCropperBox.width;
        }
      } else {
        if (xMax > newContainerData.width) {
          xMax = newContainerData.width;
          xMin = tempCropperBox.width > newContainerData.width ? 0 : newContainerData.width - tempCropperBox.width;
        }
        if (yMax > newContainerData.height) {
          yMax = newContainerData.height;
          yMin = tempCropperBox.height > newContainerData.height ? 0 : newContainerData.height - tempCropperBox.height;
        }
      }
      return {
        width: xMax - xMin,
        height: yMax - yMin,
        centerPoint: {
          x: (xMax + xMin) / 2,
          y: (yMax + yMin) / 2
        }
      };
    };
    this.handleResize = () => {
      const {
        loaded
      } = this.getStates();
      if (!this.initial) {
        this.initial = true;
        return;
      }
      if (!loaded) {
        return;
      }
      const container = this._adapter.getContainer();
      const newContainerData = {
        width: container.clientWidth,
        height: container.clientHeight
      };
      const ratio = newContainerData.width / this.containerData.width;
      const newImgData = this.getImgDataWhenResize(ratio);
      const newCropperBox = this.getCropperBoxWhenResize(ratio, newContainerData);
      this.containerData = newContainerData;
      this.setState({
        imgData: newImgData,
        cropperBox: newCropperBox
      });
    };
    this.handleImageLoad = e => {
      /**
       * 1. 图片加载完成后，获得图片的原始大小
       * 2. 计算图片的缩放比例，中心点位置
       */
      const {
        naturalWidth,
        naturalHeight
      } = e.target;
      const {
        width: containerWidth,
        height: containerHeight
      } = this.containerData;
      this.imgData.originalWidth = naturalWidth;
      this.imgData.originalHeight = naturalHeight;
      let scale = 1;
      const newImgDataState = {};
      /* 计算图片加载后的初始显示尺寸 */
      if (naturalWidth / containerWidth > naturalHeight / containerHeight) {
        scale = containerWidth / naturalWidth;
        newImgDataState.width = containerWidth;
        newImgDataState.height = naturalHeight * scale;
      } else {
        scale = containerHeight / naturalHeight;
        newImgDataState.width = naturalWidth * scale;
        newImgDataState.height = containerHeight;
      }
      this.imgData.scale = scale;
      newImgDataState.centerPoint = {};
      newImgDataState.centerPoint.x = containerWidth / 2;
      newImgDataState.centerPoint.y = containerHeight / 2;
      /* 计算裁切框大小 */
      const newCropperBoxState = {};
      const {
        defaultAspectRatio,
        aspectRatio
      } = this.getProps();
      const calcAspect = aspectRatio || defaultAspectRatio;
      if (containerWidth / containerHeight > calcAspect) {
        newCropperBoxState.width = containerHeight * calcAspect;
        newCropperBoxState.height = containerHeight;
      } else {
        newCropperBoxState.width = containerWidth;
        newCropperBoxState.height = containerWidth / calcAspect;
      }
      newCropperBoxState.centerPoint = {};
      newCropperBoxState.centerPoint.x = containerWidth / 2;
      newCropperBoxState.centerPoint.y = containerHeight / 2;
      this.setState({
        imgData: newImgDataState,
        cropperBox: newCropperBoxState,
        loaded: true
      });
    };
    this.handleWheel = e => {
      // 防止双手缩放导致页面被放大
      e.preventDefault();
      const {
        imgData,
        zoom: currZoom
      } = this.getStates();
      const {
        maxZoom,
        minZoom,
        zoomStep
      } = this.getProps();
      let _zoom;
      if (e.deltaY < 0) {
        /* zoom in */
        if (currZoom + zoomStep <= maxZoom) {
          _zoom = Number((currZoom + zoomStep).toFixed(2));
        }
      } else if (e.deltaY > 0) {
        /* zoom out */
        if (currZoom - zoomStep >= minZoom) {
          _zoom = Number((currZoom - zoomStep).toFixed(2));
        }
      }
      if (_zoom === undefined) {
        return;
      }
      const boundingRect = e.currentTarget.getBoundingClientRect();
      const offsetX = e.clientX - boundingRect.left;
      const offsetY = e.clientY - boundingRect.top;
      const scaleCenter = {
        x: offsetX,
        y: -offsetY
      };
      // 计算新的中心点位置
      const currentPoint = Object.assign({}, imgData.centerPoint);
      currentPoint.y = -currentPoint.y;
      const newCenterPoint = {
        x: (currentPoint.x - scaleCenter.x) / currZoom * _zoom + scaleCenter.x,
        y: -[(currentPoint.y - scaleCenter.y) / currZoom * _zoom + scaleCenter.y]
      };
      const newWidth = imgData.width / currZoom * _zoom;
      const newHeight = imgData.height / currZoom * _zoom;
      const newImgDataState = {
        width: newWidth,
        height: newHeight,
        centerPoint: newCenterPoint
      };
      this.setState({
        imgData: newImgDataState,
        zoom: _zoom
      });
      this._adapter.notifyZoomChange(_zoom);
    };
    this.getRangeForAspectChange = () => {
      const {
        cropperBox
      } = this.getStates();
      const {
        aspectRatio
      } = this.getProps();
      const {
        width: containerWidth,
        height: containerHeight
      } = this.containerData;
      // 可能的最大宽高
      let height, width;
      // 裁剪框当前的位置
      const xMin = cropperBox.centerPoint.x - cropperBox.width / 2;
      const xMax = cropperBox.centerPoint.x + cropperBox.width / 2;
      const yMin = cropperBox.centerPoint.y - cropperBox.height / 2;
      const yMax = cropperBox.centerPoint.y + cropperBox.height / 2;
      switch (this.boxMoveDir) {
        case 'tl':
          height = yMax;
          width = xMax;
          [width, height] = getAspectHW(width, height, aspectRatio);
          this.rangeX = [xMax - width, xMax];
          this.rangeY = [yMax - height, yMax];
          break;
        case 'tm':
          height = yMax;
          const leftHalfWidth = cropperBox.centerPoint.x;
          const rightHalfWidth = containerWidth - cropperBox.centerPoint.x;
          width = 2 * (leftHalfWidth < rightHalfWidth ? leftHalfWidth : rightHalfWidth);
          [width, height] = getAspectHW(width, height, aspectRatio);
          this.rangeX = [cropperBox.centerPoint.x - width / 2, cropperBox.centerPoint.x + width / 2];
          this.rangeY = [yMax - height, yMax];
          break;
        case 'tr':
          height = yMax;
          width = containerWidth - xMin;
          [width, height] = getAspectHW(width, height, aspectRatio);
          this.rangeX = [xMin, xMin + width];
          this.rangeY = [yMax - height, yMax];
          break;
        case 'ml':
          width = xMax;
          const topHalfHeight = cropperBox.centerPoint.y;
          const bottomHalfHeight = containerHeight - cropperBox.centerPoint.y;
          height = 2 * (topHalfHeight < bottomHalfHeight ? topHalfHeight : bottomHalfHeight);
          [width, height] = getAspectHW(width, height, aspectRatio);
          this.rangeX = [xMax - width, xMax];
          this.rangeY = [cropperBox.centerPoint.y - height / 2, cropperBox.centerPoint.y + height / 2];
          break;
        case 'mr':
          width = containerWidth - xMin;
          const topHalfHeight2 = cropperBox.centerPoint.y;
          const bottomHalfHeight2 = containerHeight - cropperBox.centerPoint.y;
          height = 2 * (topHalfHeight2 < bottomHalfHeight2 ? topHalfHeight2 : bottomHalfHeight2);
          [width, height] = getAspectHW(width, height, aspectRatio);
          this.rangeX = [xMin, xMin + width];
          this.rangeY = [cropperBox.centerPoint.y - height / 2, cropperBox.centerPoint.y + height / 2];
          break;
        case 'bl':
          height = containerHeight - yMin;
          width = xMax;
          [width, height] = getAspectHW(width, height, aspectRatio);
          this.rangeX = [xMax - width, xMax];
          this.rangeY = [yMin, yMin + height];
          break;
        case 'bm':
          height = containerHeight - yMin;
          const leftHalfWidth2 = cropperBox.centerPoint.x;
          const rightHalfWidth2 = containerWidth - cropperBox.centerPoint.x;
          width = 2 * (leftHalfWidth2 < rightHalfWidth2 ? leftHalfWidth2 : rightHalfWidth2);
          [width, height] = getAspectHW(width, height, aspectRatio);
          this.rangeX = [cropperBox.centerPoint.x - width / 2, cropperBox.centerPoint.x + width / 2];
          this.rangeY = [yMin, yMin + height];
          break;
        case 'br':
          height = containerHeight - yMin;
          width = containerWidth - xMin;
          [width, height] = getAspectHW(width, height, aspectRatio);
          this.rangeX = [xMin, xMin + width];
          this.rangeY = [yMin, yMin + height];
          break;
        default:
          break;
      }
    };
    this.handleCornerMouseDown = e => {
      const currentTarget = e.currentTarget;
      if (!currentTarget) {
        return;
      }
      e.preventDefault();
      const dir = currentTarget.dataset.dir;
      this.boxMoveDir = dir;
      this.boxMoveParam = this.getMoveParamByDir(dir);
      this.bindResizeEvent();
      const {
        aspectRatio
      } = this.getProps();
      if (aspectRatio) {
        this.getRangeForAspectChange();
      } else {
        this.rangeX = [0, this.containerData.width];
        this.rangeY = [0, this.containerData.height];
      }
    };
    this.bindResizeEvent = () => {
      const {
        aspectRatio
      } = this.getProps();
      document.addEventListener('mousemove', aspectRatio ? this.handleCornerAspectMouseMove : this.handleCornerMouseMove);
      document.addEventListener('mouseup', this.handleCornerMouseUp);
    };
    this.unBindResizeEvent = () => {
      const {
        aspectRatio
      } = this.getProps();
      document.removeEventListener('mousemove', aspectRatio ? this.handleCornerAspectMouseMove : this.handleCornerMouseMove);
      document.removeEventListener('mouseup', this.handleCornerMouseUp);
    };
    this.viewIMGDragStart = e => {
      e.preventDefault();
    };
    this.handleCornerAspectMouseMove = e => {
      e.preventDefault();
      const {
        clientX,
        clientY
      } = e;
      const {
        cropperBox
      } = this.getStates();
      const {
        aspectRatio
      } = this.getProps();
      const boundingRect = this._adapter.getContainer().getBoundingClientRect();
      const newCropperBoxPos = {
        width: cropperBox.width,
        height: cropperBox.height,
        centerPoint: Object.assign({}, cropperBox.centerPoint)
      };
      let offsetX, offsetY;
      if (['ml', 'mr'].includes(this.boxMoveDir)) {
        offsetX = getMiddle(clientX - boundingRect.left, this.rangeX);
      } else {
        offsetY = getMiddle(clientY - boundingRect.top, this.rangeY);
      }
      switch (this.boxMoveDir) {
        case 'tl':
          newCropperBoxPos.height = this.rangeY[1] - offsetY;
          newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
          newCropperBoxPos.centerPoint = {
            x: this.rangeX[1] - newCropperBoxPos.width / 2,
            y: this.rangeY[1] - newCropperBoxPos.height / 2
          };
          break;
        case 'tm':
          newCropperBoxPos.height = this.rangeY[1] - offsetY;
          newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
          newCropperBoxPos.centerPoint = {
            x: cropperBox.centerPoint.x,
            y: this.rangeY[1] - newCropperBoxPos.height / 2
          };
          break;
        case 'tr':
          newCropperBoxPos.height = this.rangeY[1] - offsetY;
          newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
          newCropperBoxPos.centerPoint = {
            x: this.rangeX[0] + newCropperBoxPos.width / 2,
            y: this.rangeY[1] - newCropperBoxPos.height / 2
          };
          break;
        case 'ml':
          newCropperBoxPos.width = this.rangeX[1] - offsetX;
          newCropperBoxPos.height = newCropperBoxPos.width / aspectRatio;
          newCropperBoxPos.centerPoint = {
            x: this.rangeX[1] - newCropperBoxPos.width / 2,
            y: cropperBox.centerPoint.y
          };
          break;
        case 'mr':
          newCropperBoxPos.width = offsetX - this.rangeX[0];
          newCropperBoxPos.height = newCropperBoxPos.width / aspectRatio;
          newCropperBoxPos.centerPoint = {
            x: this.rangeX[0] + newCropperBoxPos.width / 2,
            y: cropperBox.centerPoint.y
          };
          break;
        case 'bl':
          newCropperBoxPos.height = offsetY - this.rangeY[0];
          newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
          newCropperBoxPos.centerPoint = {
            x: this.rangeX[1] - newCropperBoxPos.width / 2,
            y: this.rangeY[0] + newCropperBoxPos.height / 2
          };
          break;
        case 'bm':
          newCropperBoxPos.height = offsetY - this.rangeY[0];
          newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
          newCropperBoxPos.centerPoint = {
            x: cropperBox.centerPoint.x,
            y: this.rangeY[0] + newCropperBoxPos.height / 2
          };
          break;
        case 'br':
          newCropperBoxPos.height = offsetY - this.rangeY[0];
          newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
          newCropperBoxPos.centerPoint = {
            x: this.rangeX[0] + newCropperBoxPos.width / 2,
            y: this.rangeY[0] + newCropperBoxPos.height / 2
          };
          break;
        default:
          break;
      }
      if (newCropperBoxPos.height === 0 && newCropperBoxPos.width === 0) {
        this.changeDir();
        this.getRangeForAspectChange();
      }
      this.setState({
        cropperBox: newCropperBoxPos
      });
    };
    this.changeDir = () => {
      if (this.boxMoveDir.includes('t')) {
        this.boxMoveDir = this.boxMoveDir.replace('t', 'b');
      } else if (this.boxMoveDir.includes('b')) {
        this.boxMoveDir = this.boxMoveDir.replace('b', 't');
      }
      if (this.boxMoveDir.includes('l')) {
        this.boxMoveDir = this.boxMoveDir.replace('l', 'r');
      } else if (this.boxMoveDir.includes('r')) {
        this.boxMoveDir = this.boxMoveDir.replace('r', 'l');
      }
    };
    this.handleCornerMouseMove = e => {
      e.preventDefault();
      const {
        clientX,
        clientY
      } = e;
      const {
        cropperBox
      } = this.getStates();
      const boundingRect = this._adapter.getContainer().getBoundingClientRect();
      let offsetX = getMiddle(clientX - boundingRect.left, this.rangeX);
      let offsetY = getMiddle(clientY - boundingRect.top, this.rangeY);
      const newCropperBoxPos = {
        width: cropperBox.width,
        height: cropperBox.height,
        centerPoint: {
          x: cropperBox.centerPoint.x,
          y: cropperBox.centerPoint.y
        }
      };
      const {
        paramX,
        paramY
      } = this.boxMoveParam;
      let x, y;
      if (paramX) {
        x = cropperBox.centerPoint.x + paramX * cropperBox.width / 2;
        newCropperBoxPos.width = cropperBox.width + paramX * (offsetX - x);
        if (newCropperBoxPos.width < 0) {
          newCropperBoxPos.width = -newCropperBoxPos.width;
          this.boxMoveParam.paramX = -paramX;
        }
        newCropperBoxPos.centerPoint.x = offsetX - paramX * newCropperBoxPos.width / 2;
      }
      if (paramY) {
        y = cropperBox.centerPoint.y + paramY * cropperBox.height / 2;
        newCropperBoxPos.height = cropperBox.height + paramY * (offsetY - y);
        if (newCropperBoxPos.height < 0) {
          newCropperBoxPos.height = -newCropperBoxPos.height;
          this.boxMoveParam.paramY = -paramY;
        }
        newCropperBoxPos.centerPoint.y = offsetY - paramY * newCropperBoxPos.height / 2;
      }
      this.setState({
        cropperBox: newCropperBoxPos
      });
    };
    this.handleCornerMouseUp = e => {
      this.boxMoveParam = {
        paramX: 0,
        paramY: 0
      };
      this.unBindResizeEvent();
    };
    this.handleCropperBoxMouseDown = e => {
      const target = e.target;
      const {
        cropperBox
      } = this.getStates();
      const container = this._adapter.getContainer();
      const boundingRect = container.getBoundingClientRect();
      if (target.dataset.dir) {
        // 如果鼠标是落在了corner上，那么不做任何操作
        return;
      }
      // 移动裁切框
      this.cropperBoxMoveStart = {
        x: e.clientX,
        y: e.clientY
      };
      this.bindMoveEvent();
      // 计算 cropperBox 中心点移动范围
      this.moveRange = {
        xMin: cropperBox.width / 2,
        xMax: boundingRect.width - cropperBox.width / 2,
        yMin: cropperBox.height / 2,
        yMax: boundingRect.height - cropperBox.height / 2
      };
    };
    this.bindMoveEvent = () => {
      document.addEventListener('mousemove', this.handleCropperBoxMouseMove);
      document.addEventListener('mouseup', this.handleCropperBoxMouseUp);
    };
    this.unBindMoveEvent = () => {
      document.removeEventListener('mousemove', this.handleCropperBoxMouseMove);
      document.removeEventListener('mouseup', this.handleCropperBoxMouseUp);
    };
    this.handleCropperBoxMouseMove = e => {
      if (!this.cropperBoxMoveStart) {
        return;
      }
      const {
        clientX,
        clientY
      } = e;
      const {
        cropperBox
      } = this.getStates();
      const offsetX = clientX - this.cropperBoxMoveStart.x;
      const offsetY = clientY - this.cropperBoxMoveStart.y;
      const newCenterPointX = getMiddle(cropperBox.centerPoint.x + offsetX, [this.moveRange.xMin, this.moveRange.xMax]);
      const newCenterPointY = getMiddle(cropperBox.centerPoint.y + offsetY, [this.moveRange.yMin, this.moveRange.yMax]);
      const newCropperBoxPos = {
        width: cropperBox.width,
        height: cropperBox.height,
        centerPoint: {
          x: newCenterPointX,
          y: newCenterPointY
        }
      };
      this.cropperBoxMoveStart = {
        x: clientX,
        y: clientY
      };
      this.setState({
        cropperBox: newCropperBoxPos
      });
    };
    this.handleCropperBoxMouseUp = e => {
      if (!this.cropperBoxMoveStart) {
        return;
      }
      this.cropperBoxMoveStart = null;
      this.unBindMoveEvent();
    };
    this.handleMaskMouseDown = e => {
      if (e.currentTarget !== e.target) {
        return;
      }
      this.bindImgMoveEvent();
      // 记录开始移动的位置
      this.imgMoveStart = {
        x: e.clientX,
        y: e.clientY
      };
    };
    this.bindImgMoveEvent = () => {
      document.addEventListener('mousemove', this.handleImgMove);
      document.addEventListener('mouseup', this.handleImgMoveUp);
    };
    this.unBindImgMoveEvent = () => {
      document.removeEventListener('mousemove', this.handleImgMove);
      document.removeEventListener('mouseup', this.handleImgMoveUp);
    };
    this.handleImgMove = e => {
      if (!this.imgMoveStart) {
        return;
      }
      const {
        clientX,
        clientY
      } = e;
      const {
        imgData
      } = this.getStates();
      const offsetX = clientX - this.imgMoveStart.x;
      const offsetY = clientY - this.imgMoveStart.y;
      const newCenterPointX = imgData.centerPoint.x + offsetX;
      const newCenterPointY = imgData.centerPoint.y + offsetY;
      const newImgData = {
        width: imgData.width,
        height: imgData.height,
        centerPoint: {
          x: newCenterPointX,
          y: newCenterPointY
        }
      };
      this.imgMoveStart = {
        x: clientX,
        y: clientY
      };
      this.setState({
        imgData: newImgData
      });
    };
    this.handleImgMoveUp = e => {
      if (!this.imgMoveStart) {
        return;
      }
      this.imgMoveStart = null;
      this.unBindImgMoveEvent();
    };
    this.getCropperCanvas = () => {
      const {
        cropperBox,
        imgData,
        rotate,
        zoom
      } = this.getStates();
      const {
        fill
      } = this.getProps();
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      const img = this._adapter.getImg();
      // 计算包含旋转后的图片的矩形容器的宽高
      const angle = rotate * Math.PI / 180;
      const sine = Math.abs(Math.sin(angle));
      const cosine = Math.abs(Math.cos(angle));
      const imgWidth = this.imgData.originalWidth;
      const imgHeight = this.imgData.originalHeight;
      const containerWidth = imgWidth * cosine + imgHeight * sine;
      const containerHeight = imgHeight * cosine + imgWidth * sine;
      // 判断裁切区域和外接矩形是否存在交集，如果不存在，则直接返回空白图片
      // 计算需要裁剪的区域实际大小和位置
      const cropperContainerWidth = containerWidth * zoom * this.imgData.scale;
      const cropperContainerHeight = containerHeight * zoom * this.imgData.scale;
      const cropperContainerTop = imgData.centerPoint.y - cropperContainerHeight / 2;
      const cropperContainerLeft = imgData.centerPoint.x - cropperContainerWidth / 2;
      const cropperBoxLeft = cropperBox.centerPoint.x - cropperBox.width / 2;
      const cropperBoxTop = cropperBox.centerPoint.y - cropperBox.height / 2;
      const realZoom = zoom * this.imgData.scale;
      const relativeCropLeft = (cropperBoxLeft - cropperContainerLeft) / realZoom;
      const relativeCropTop = (cropperBoxTop - cropperContainerTop) / realZoom;
      const relativeWidth = cropperBox.width / realZoom;
      const relativeHeight = cropperBox.height / realZoom;
      const relativeCropRight = relativeCropLeft + relativeWidth;
      const relativeCropBottom = relativeCropTop + relativeHeight;
      if (relativeCropRight < 0 || relativeCropBottom < 0 || relativeCropLeft > containerWidth || relativeCropTop > containerHeight) {
        // 没有交集，直接返回空白图片
        const emptyCanvas = document.createElement('canvas');
        const ctx = emptyCanvas.getContext('2d');
        emptyCanvas.width = relativeWidth;
        emptyCanvas.height = relativeHeight;
        ctx.fillStyle = fill;
        ctx.fillRect(0, 0, relativeWidth, relativeHeight);
        return emptyCanvas;
      }
      canvas.width = containerWidth;
      canvas.height = containerHeight;
      ctx.fillStyle = fill;
      ctx.fillRect(0, 0, containerWidth, containerHeight);
      const halfWidth = containerWidth / 2;
      const halfHeight = containerHeight / 2;
      ctx.translate(halfWidth, halfHeight);
      ctx.rotate(rotate * Math.PI / 180);
      ctx.translate(-halfWidth, -halfHeight);
      const imgX = (containerWidth - imgWidth) / 2;
      const imgY = (containerHeight - imgHeight) / 2;
      ctx.drawImage(img, 0, 0, imgWidth, imgHeight, imgX, imgY, imgWidth, imgHeight);
      const canvas2 = document.createElement('canvas');
      const ctx2 = canvas2.getContext('2d');
      // 为了避免裁剪时候，超出被裁切的画布的部分颜色不正常，需要将裁切区域限制在画布范围内。
      // 相对位置会在后续进行修正
      let realLeft = relativeCropLeft;
      let realTop = relativeCropTop;
      let realWidth = relativeWidth;
      let realHeight = relativeHeight;
      if (relativeCropLeft < 0) {
        realLeft = 0;
      }
      if (relativeCropTop < 0) {
        realTop = 0;
      }
      if (relativeCropRight > containerWidth) {
        realWidth = containerWidth - realLeft;
      } else if (relativeCropLeft < 0) {
        realWidth = relativeCropRight;
      }
      if (relativeCropBottom > containerHeight) {
        realHeight = containerHeight - realTop;
      } else if (relativeCropTop < 0) {
        realHeight = relativeCropBottom;
      }
      const imgDataResult = ctx.getImageData(realLeft, realTop, realWidth, realHeight);
      canvas2.width = relativeWidth;
      canvas2.height = relativeHeight;
      ctx2.fillStyle = fill;
      ctx2.fillRect(0, 0, relativeWidth, relativeHeight);
      ctx2.putImageData(imgDataResult, relativeCropLeft < 0 ? -relativeCropLeft : 0, relativeCropTop < 0 ? -relativeCropTop : 0);
      return canvas2;
    };
    this.containerData = {};
    this.imgData = {};
    this.boxMoveDir = '';
    this.boxMoveParam = {
      paramX: 0,
      paramY: 0
    };
    this.rangeX = null;
    this.rangeY = null;
    this.initial = false;
  }
  init() {
    // 获取容器的宽高
    // get cropping Container 's width & height
    const container = this._adapter.getContainer();
    this.containerData.width = container.clientWidth;
    this.containerData.height = container.clientHeight;
    this.cropperBoxMoveStart = null;
  }
  destroy() {
    this.unBindMoveEvent();
    this.unBindResizeEvent();
  }
  getMoveParamByDir(dir) {
    let paramX = 0,
      paramY = 0;
    switch (dir) {
      case 'tl':
        paramX = -1;
        paramY = -1;
        break;
      case 'tm':
        paramY = -1;
        break;
      case 'tr':
        paramX = 1;
        paramY = -1;
        break;
      case 'ml':
        paramX = -1;
        break;
      case 'mr':
        paramX = 1;
        break;
      case 'bl':
        paramX = -1;
        paramY = 1;
        break;
      case 'bm':
        paramY = 1;
        break;
      case 'br':
        paramX = 1;
        paramY = 1;
        break;
      default:
        break;
    }
    return {
      paramX,
      paramY
    };
  }
}