预览图

类源码

/*
 * @Author: mulingyuer
 * @Date: 2021-06-21 11:29:12
 * @LastEditTime: 2021-06-21 18:39:31
 * @LastEditors: mulingyuer
 * @Description: canvas
 * @FilePath: \form-create\src\utils\canvas.js
 * 怎么可能会有bug!!!
 */

class Chart {
  constructor(options) {
    const defaultOptions = {
      el: null, //dom或者选择器
      data: [], //组件数据
      scale: 1, //canvas缩放等级
      minScale: 0.2, //最小缩放
      maxScale: 3, //最大缩放
      scaleStep: 0.1, //每次缩放比率
      offsetX: 0,  //画布x轴缩放偏移值
      offsetY: 0,  //画布y轴缩放偏移值
    };
    //合并配置
    Object.assign(this, defaultOptions, options);

    this.init();  //初始化
    this.addScaleEvent(); //添加缩放
    this.addCanvasDragEvent();  //添加画布拖拽
    this.render(); //首次渲染
  }

  //初始化
  init() {
    this.wrap = null;
    //获取容器
    if (typeof this.el === "string") {
      this.wrap = document.querySelector(el);
    } else {
      if (this.utils.getType(this.el) !== "htmldivelement") {
        throw new Error("el参数不正确:它应该是选择器或者dom对象");
      }
      this.wrap = this.el;
    }
    //获取容器样式&创建canvas
    const wrapStyle = getComputedStyle(this.wrap);
    this.width = parseInt(wrapStyle.width, 10);
    this.height = parseInt(wrapStyle.height, 10);
    this.canvas = document.createElement('canvas');
    this.canvas.width = this.width;
    this.canvas.height = this.height;
    this.ctx = this.canvas.getContext('2d');


    this.wrap.appendChild(this.canvas);
  }

  //绘制背景图
  drawBg(bgData) {
    const { src, data } = bgData;
    if (!src) return;

    if (bgData.el) {
      this.ctx.drawImage(bgData.el, data.left ?? 0, data.top ?? 0, data.width ?? 0, data.height ?? 0);
    } else {
      bgData.el = new Image();
      bgData.el.onload = () => {
        this.ctx.drawImage(bgData.el, data.left ?? 0, data.top ?? 0, data.width ?? 0, data.height ?? 0);
      };
      bgData.el.src = src;
    }
  }

  //是否为背景图
  isDrawBg(itemData, x, y) {
    const { data } = itemData;

    const isX = x >= data.left && x <= data.left + data.width;
    const isY = y >= data.top && y <= data.top + data.height;

    return isX && isY;
  }

  //添加滚轮缩放事件
  addScaleEvent() {
    //移入-添加
    this.canvas.addEventListener('mouseenter', () => {
      document.addEventListener('mousewheel', this.scaleMouseWhell, { passive: false });
      document.addEventListener('DOMMouseScroll', this.scaleMouseWhell, { passive: false }); //火狐
    });
    //移出-删除
    this.canvas.addEventListener('mouseleave', () => {
      document.removeEventListener('mousewheel', this.scaleMouseWhell, { passive: false });
      document.removeEventListener('DOMMouseScroll', this.scaleMouseWhell, { passive: false });//火狐
    });
  }

  //缩放滚轮
  scaleMouseWhell = (event) => {
    //阻止默认事件 (缩放时外部容器禁止滚动)
    event.preventDefault();
    const delta = event.deltaY || event.detail;

    // const x = event.offsetX - this.offsetX;
    // const y = event.offsetY - this.offsetY;

    const offsetX = (x / this.scale) * this.scaleStep;
    const offsetY = (y / this.scale) * this.scaleStep;
    if (delta > 0) {
      //往下滚-缩放
      // this.offsetX -= this.scale >= this.maxScale ? 0 : offsetX;
      // this.offsetY -= this.scale >= this.maxScale ? 0 : offsetY;

      this.scale -= this.scaleStep
    } else {
      //往上滚-放大
      // this.offsetX += this.scale <= this.minScale ? 0 : offsetX;
      // this.offsetY += this.scale <= this.minScale ? 0 : offsetY;

      this.scale += this.scaleStep;
    };

    this.scale = Math.min(this.maxScale, Math.max(this.scale, this.minScale));

    //重新渲染
    this.render();
  }

  //添加画布拖拽事件
  addCanvasDragEvent = () => {
    this.canvas.addEventListener('mousedown', this.addMoveEvent);
    document.addEventListener('mouseup', this.removeMoveEvent);
  }

  //添加鼠标移动 功能,获取保存当前点击坐标
  addMoveEvent = (event) => {
    this.targetX = event.offsetX;
    this.targetY = event.offsetY;

    this.mousedownOriginX = this.offsetX;
    this.mousedownOriginY = this.offsetY;

    const x = (this.targetX - this.offsetX) / this.scale;
    const y = (this.targetY - this.offsetY) / this.scale;

    //判断移动的是谁
    this.activeModData = null;
    this.data.forEach((itemData, index) => {
      switch (itemData.type) {
        case 'background':
          if (this.isDrawBg(itemData, x, y)) {
            //提高层级
            this.data.push(...this.data.splice(index, 1));
            this.draw(itemData);
            this.activeModData = itemData;
          }
          break;
      }
    });


    if (!this.activeModData) {
      this.wrap.style.cursor = 'grabbing'
      this.canvas.addEventListener('mousemove', this.moveCanvasFunc, false);
    } else {
      this.wrap.style.cursor = 'all-scroll'
      this.modOldX = null
      this.modOldY = null
      this.canvas.addEventListener('mousemove', this.moveModFunc, false)
    }

  }

  //移除拖拽事件
  removeMoveEvent = () => {
    this.wrap.style.cursor = '';
    this.canvas.removeEventListener('mousemove', this.moveCanvasFunc, false);
    this.canvas.removeEventListener('mousemove', this.moveModFunc, false);
  }

  //画布拖拽方法
  moveCanvasFunc = (event) => {
    // 获取 最大可移动宽
    const maxMoveX = this.canvas.width / 2;
    const maxMoveY = this.canvas.height / 2;

    const offsetX = this.mousedownOriginX + (event.offsetX - this.targetX);
    const offsetY = this.mousedownOriginY + (event.offsetY - this.targetY);

    this.offsetX = Math.abs(offsetX) > maxMoveX ? this.offsetX : offsetX;
    this.offsetY = Math.abs(offsetY) > maxMoveY ? this.offsetY : offsetY;

    this.render()
  }

  //模块拖动事件
  moveModFunc = (event) => {
    let moveX = event.offsetX - (this.modOldX || this.targetX);
    let moveY = event.offsetY - (this.modOldY || this.targetY);

    moveX /= this.scale
    moveY /= this.scale

    switch (this.activeModData.type) {
      case 'background':
        const { data } = this.activeModData;
        let x = data.left;
        let y = data.top;
        Object.assign(this.activeModData.data, { left: x + moveX, top: y + moveY })
        break;
    }

    this.modOldX = event.offsetX
    this.modOldY = event.offsetY

    this.render()
  }

  //通用绘制方法
  draw(data) {
    this.ctx.setTransform(this.scale, 0, 0, this.scale, this.offsetX, this.offsetY);

    switch (data.type) {
      case 'background':
        this.drawBg(data);
        break;
      default:
        console.log("该组件数据暂不支持", data);
    }
  }

  //渲染
  render() {
    this.canvas.width = this.width;

    this.data.forEach(itemData => {
      this.draw(itemData);
    });
  }

  //push
  push(data) {
    this.data.push(data);
    this.draw(data);
  }


  //工具
  utils = {
    //获取文件类型
    getType(value) {
      let type = typeof value;
      if (type !== "object") {
        return type;
      }
      return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
    }
  }
}



export default Chart;

使用

const $canvas = new Chart({
    el: this.$refs.chartWrap,
    data: [],
});

$canvas.push({
    id: uuid(),
    type: "background",
    src: "https://testsuperres.shetuan.pro/cos/admin/images/20210427/928c2e5ba23997b3e19307608b2fe76e.png",
    data: {
        top: 20,
        left: 18,
        width: 80,
        height: 80,
    },
});
$canvas.push({
    id: uuid(),
    type: "background",
    src: "https://testsuperres.shetuan.pro/cos/admin/images/20210408/843ebf2969ad1635cf893ec74887ddd8.png",
    data: {
        top: 20,
        left: 18,
        width: 364,
        height: 350,
    },
});

参考文章

来自掘金的《使用canvas 如何绘制形状并支持拖拽、缩放功能》

分类: JavaScript 标签: canvasjavascriptclass

评论

暂无评论数据

暂无评论数据

目录