基于html2canvas 的简易截图工具

今天分享一个自己手写的简易截图工具(基于html2canvas),因为是在vue项目上做的,少量用了es6语法,如果不支持es6的项目上需要稍微修改下,写的时候有考虑过这个,所以只需要改引入方式、将class换成es5的构造函数写法、let和const换成var即可。

基于html2canvas要画重点*,所以生成图片跟html2canvas的规则是一样的,请熟知html2canvas的特性(比如部分css3不兼容,svg之类的,该怎么处理就按html2canvas的方法处理完再调用)*

大致思路如下:
1、使用html2canvas将目标dom转化为图片;
2、然后在body上创建全屏弹窗,将图片置于弹窗内,再创建蒙版(蒙版绑定鼠标点击、移动事件)覆盖在上面,形成灰蒙蒙的截图界面;
3、截图界面已经形成,接来下就是鼠标拖拽创建(拉伸)矩形选区,根据鼠标在蒙版上点击时的坐标,然后计算按住鼠标后指针移动的实时坐标 计算出矩形选区的宽高,这样矩形选区就可以完整的创建出来并实时更新大小了,创建矩形选区之后在选区内8个方位创建8个拖拽点并绑定事件用于拉伸选区的宽高,同时也要给选区绑定拖拽事件用于移动选区;
4、矩形选区创建完成之后接下来就是让选区内部的视觉效果变成清晰高亮了,我这里用的是背景图偏移属性background-position,首先我们将矩形选区的样式background-image设置为第一步生成的图片,然后计算矩形选区与图片的相对坐标来设置偏移量;这样视觉效果就出来了;
5、通过上面的步骤我们已经能拿到选区的坐标和大小了,接下来就通过canvas的drawImage方法和计算的各个数值生成最终的截图并转化为base64,然后绑定各种钩子(如成功、取消等),这样一个简易的截图工具就算完成了,接下来我会考虑找时间加上那些辅助截图工具用于标注截图之类的

有什么地方写的不好的请私聊指正,勿喷哈

目录

  • 零、补充
    • a、演示地址
  • 一、代码实现
    • 1、初始化
    • 2、生成截图界面
    • 3、创建矩形选区,绑定选区相关事件
    • 4、工具栏相关
    • 5、截图完成
  • 二、场景效果(截图)
  • 三、完整代码

零、补充

a、演示地址

懒出风格的我终于弄了个demo来了 泪目
http://tools.lzhong.wang/tools/screenshot

一、代码实现

这里并非完整代码而是一个个实现步骤

1、初始化

开始之前记得先安装html2canvas(以vue为例)

npm install html2canvas --save

然后开始撸代码,首先创建一个构造函数并调用html2canvas将目标dom转化为图片的base64编码,赋值给imgUrl

// 引入html2canvas
import html2canvas from 'html2canvas';
// 样式
const cssText = {
  box: 'overflow:hidden;position:fixed;left:0;top:0;right:0;bottom:0;background-color:rgba(255,255,255,0.9);z-index: 100000;',
  img: '',
  mask: 'position:absolute;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,0.6);',
  rect: 'position:absolute;border:1px solid #3e8ef7;box-sizing:border-box;cursor:move;user-select:none;background: url() no-repeat;',
  toolBox: 'position:absolute;top:0;left:0;padding:0 10px;background:#eee;line-height:2em;text-align:right;',
  toolBtn: 'font-weight:bold;color:#111;margin:0 1em;user-select:none;font-size:12px;cursor:pointer;',
}
// 构造类
/**
 * dom节点截图工具(基于html2canvas)
 * dom: 要截图的目标dom
 * options: { 
 *   // 以下三个回调方法作用域this指向构造函数
 *   success: function(res), //截图完成触发 参数为截图结果
 *   fail: function(), //取消截图触发
 *   complete: function(), //截图结束触发 success和fail都会触发
 * }
 * 调用示例:
 * new ClipScreen(dom节点, {
 *   success: function (res) {},
 *   complete: function () {},
 * });
 */
class ClipScreen {
  constructor(dom, options) {
    if (window.ClipScreen) return false;
    window.ClipScreen = this;
    this.dom = dom;
    this.options = options;
    html2canvas(this.dom).then((canvas) => {
      let dataURL = canvas.toDataURL("image/png");
      this.imgUrl = dataURL;
      this.start(); // 下一步要用的 调用初始化方法
    });
  }
}
export default ClipScreen

2、生成截图界面

图片base64有了,接下来就是初始化截图界面(载入图片,创建蒙版,并为蒙版绑定事件)

start() {
    this.border = 2; // 用于计算选区拖拽点和边界的判断
    this.win_w = window.innerWidth;
    this.win_h = window.innerHeight; 
    // 创建弹窗并写入样式
    let box = this.box = document.createElement('div');
    box.id = 'ClipScreen';
    box.style.cssText = cssText.box;
    // 创建图片
    let img = document.createElement('img');
    img.style.cssText = cssText.img;
    img.src = this.imgUrl;
    // 创建蒙版
    let mask = document.createElement('div');
    mask.style.cssText = cssText.mask;
    box.appendChild(img);
    box.appendChild(mask);
    document.body.appendChild(box);
    // 图片载入 初始化图片
    img.onload = (e) => {
      let w = img.offsetWidth,
        h = img.offsetHeight,
        win_w = window.innerWidth,
        win_h = window.innerHeight,
        left = (win_w - w) / 2,
        top = (win_h - h) / 2;
      img.style.position = 'absolute';
      img.style.left = left + 'px';
      img.style.top = top + 'px';
      img.style.width = w + 'px';
      img.style.height = h + 'px';
      this.axis = {
        left,
        top
      }
      this.img = img;
      // 蒙版绑定事件
      this.bindEvent(mask);
    }
  }
  bindEvent(mask) {
    document.onkeydown = (e) => {
      if (e.keyCode == 27) {
        this.cancel();
      }
    }
    mask.onmousedown = (e) => {
      let offsetX = e.offsetX,
        offsetY = e.offsetY;
      document.onmousemove = (e) => {
        let x = e.offsetX,
          y = e.offsetY,
          sx = offsetX,
          sy = offsetY,
          w = Math.abs(offsetX - x),
          h = Math.abs(offsetY - y);
        if (x < offsetX) sx = x;
        if (y < offsetY) sy = y;
        this.createRect(sx, sy, w, h); // 下一部用的 创建矩形选区
      }
      document.onmouseup = (e) => {
        this.unbindMouseEvent(); // 解绑鼠标move和up事件
      }
    }
  }
   // 解绑鼠标move和up事件
  unbindMouseEvent() {
    document.onmousemove = null;
    document.onmouseup = null;
  }

3、创建矩形选区,绑定选区相关事件

通过上一步的蒙版拖拽事件来创建矩形选区,并绑定事件,同时改变选区图片视觉高亮效果

// 创建矩形截图选区
  createRect(x, y, w, h) {
    let rect = this.rect;
    if (!rect) {
      rect = this.rect = document.createElement('div');
      rect.style.cssText = cssText.rect;
      rect.style.backgroundImage = 'url(' + this.imgUrl + ')';
      // 创建8个方向的拖拽点
      const doms = this.createPoints(rect);
      this.box.appendChild(rect);
      // 绑定选区事件
      this.bindRectEvent(doms);
    }
    let border = this.border;
    if (x <= border) x = border;
    if (y <= border) y = border;
    if (x + w >= this.win_w - border) x = this.win_w - border - w;
    if (y + h >= this.win_h - border) y = this.win_h - border - h;
    rect.style.pointerEvents = 'none';
    rect.style.display = 'block';
    rect.style.left = x + 'px';
    rect.style.top = y + 'px';
    rect.style.width = w + 'px';
    rect.style.height = h + 'px';
    // 矩形选区图片视觉高亮
    rect.style.backgroundPosition = (-x + this.axis.left - 1) + 'px ' + (-y + this.axis.top - 1) + 'px';
    if (this.toolBox) this.toolBox.style.display = 'none';
  }
  // 创建截图选区各个方位拉伸点
  createPoints(rect) {
    let
      lt = document.createElement('span'),
      tc = document.createElement('span'),
      rt = document.createElement('span'),
      rc = document.createElement('span'),
      rb = document.createElement('span'),
      bc = document.createElement('span'),
      lb = document.createElement('span'),
      lc = document.createElement('span');
    let c_style = 'position:absolute;width:5px;height:5px;background:#3e8ef7;';
    lt.style.cssText = c_style + 'left:-3px;top:-3px;cursor:nw-resize;';
    tc.style.cssText = c_style + 'left:50%;top:-3px;margin-left:-3px;cursor:ns-resize;';
    rt.style.cssText = c_style + 'right:-3px;top:-3px;cursor:ne-resize;';
    rc.style.cssText = c_style + 'top:50%;right:-3px;margin-top:-3px;cursor:ew-resize;';
    rb.style.cssText = c_style + 'right:-3px;bottom:-3px;cursor:nw-resize;';
    bc.style.cssText = c_style + 'left:50%;bottom:-3px;margin-left:-3px;cursor:ns-resize;';
    lb.style.cssText = c_style + 'left:-3px;bottom:-3px;cursor:ne-resize;';
    lc.style.cssText = c_style + 'top:50%;left:-3px;margin-top:-3px;cursor:ew-resize;';
    let res = {lt,tc,rt,rc,rb,bc,lb,lc}
    for (let k in res) {
      rect.appendChild(res[k])
    }
    res.rect = rect;
    return res;
  }
  // 绑定截图选区事件
  bindRectEvent(o) {
    o.rect.addEventListener("mousedown", (e) => {
      let border = this.border;
      let $target = e.target;
      let offsetX = e.x,
        offsetY = e.y;
      let r_w = o.rect.offsetWidth,
        r_h = o.rect.offsetHeight,
        r_l = o.rect.offsetLeft,
        r_t = o.rect.offsetTop;
	// 拖拽移动选区
      if ($target == o.rect) {
        offsetX = e.offsetX;
        offsetY = e.offsetY;
        document.onmousemove = (e) => {
          let dif_x = e.x - offsetX,
            dif_y = e.y - offsetY;
            // 边界判断
          if (dif_x <= border) dif_x = border;
          if (dif_y <= border) dif_y = border;
          if (dif_x + r_w >= this.win_w - border) dif_x = this.win_w - border - r_w;
          if (dif_y + r_h >= this.win_h - border) dif_y = this.win_h - border - r_h;
          o.rect.style.left = dif_x + 'px';
          o.rect.style.top = dif_y + 'px';
          o.rect.style.backgroundPosition = (-dif_x + this.axis.left - 1) + 'px ' + (-dif_y + this.axis.top - 1) + 'px';
          this.toolBox.style.display = 'none'
        }
      } else {
      // 八个方位的拖拽点事件
        document.onmousemove = (e) => {
          this.toolBox.style.display = 'none'
          this.transform($target, o, offsetX, offsetY, r_w, r_h, r_l, r_t, e)
        }
      }
      document.onmouseup = (e) => {
        this.moveToolBox(); // 工具栏
        this.unbindMouseEvent();
      }
    })
  }
  // 拉伸选区
  transform($t, o, offsetX, offsetY, r_w, r_h, r_l, r_t, e) {
    let border = this.border;
    let x = e.x,
      y = e.y;
      // 边界判断
    if (x <= border) x = border;
    if (y <= border) y = border;
    if (x >= this.win_w - border) x = this.win_w - border;
    if (y >= this.win_h - border) y = this.win_h - border;
    let dif_x = x - offsetX,
      dif_y = y - offsetY;
    let min = 10; // 宽高最小值
    let left = r_l,
      top = r_t,
      width = r_w,
      height = r_h;
      // 判断是哪个方位的拖拽点
    if ($t == o.lt) {
      if (r_w - dif_x <= min || r_h - dif_y <= min) return false; // 最小宽高判断
      left = r_l + dif_x;
      top = r_t + dif_y;
      width = r_w - dif_x;
      height = r_h - dif_y;
    } else if ($t == o.tc) {
      if (r_h - dif_y <= min) return false;
      top = r_t + dif_y;
      height = r_h - dif_y;
    } else if ($t == o.rt) {
      if (r_w + dif_x <= min || r_h - dif_y <= min) return false;
      top = r_t + dif_y;
      width = r_w + dif_x;
      height = r_h - dif_y;
    } else if ($t == o.rc) {
      if (r_w + dif_x <= min) return false;
      width = r_w + dif_x;
    } else if ($t == o.rb) {
      if (r_w + dif_x <= min || r_h + dif_y <= min) return false;
      width = r_w + dif_x;
      height = r_h + dif_y;
    } else if ($t == o.bc) {
      if (r_h + dif_y <= min) return false;
      height = r_h + dif_y;
    } else if ($t == o.lb) {
      if (r_w - dif_x <= min || r_h + dif_y <= min) return false;
      left = r_l + dif_x;
      width = r_w - dif_x;
      height = r_h + dif_y;
    } else if ($t == o.lc) {
      if (r_w - dif_x <= min) return false;
      left = r_l + dif_x;
      width = r_w - dif_x;
    }
    // 根据拖拽点计算矩形的大小和方位
    o.rect.style.left = left + 'px';
    o.rect.style.top = top + 'px';
    o.rect.style.width = width + 'px';
    o.rect.style.height = height + 'px';
     //改变矩形选区背景图偏移量
    o.rect.style.backgroundPosition = (-left + this.axis.left - 1) + 'px ' + (-top + this.axis.top - 1) + 'px';
  }

4、工具栏相关

这里主要是工具栏的相关代码

// 生成 、移动工具
  moveToolBox() {
    let toolBox = this.toolBox;
    if (!toolBox) {
    // 创建工具栏
      toolBox = this.toolBox = document.createElement('div');
      toolBox.style.cssText = cssText.toolBox;
      let save = document.createElement('span'),
        cancel = document.createElement('span');
      save.innerText = '完成';
      cancel.innerText = '取消';
      save.style.cssText = cancel.style.cssText = cssText.toolBtn;
      toolBox.appendChild(cancel);
      toolBox.appendChild(save);
      this.box.appendChild(toolBox);
      this.bindToolBoxEvent(save, cancel);
    }
    // 显示工具栏并设置工具栏所在坐标
    toolBox.style.display = 'block';
    let border = this.border;
    let t_w = this.toolBox.offsetWidth,
      t_h = this.toolBox.offsetHeight,
      r_t = this.rect.offsetTop,
      r_h = this.rect.offsetHeight;
    let t = r_t + r_h + 10,
      l = this.rect.offsetLeft + this.rect.offsetWidth - t_w;
     // 工具栏坐标判断,确保不会跑出浏览器外部,提高视觉体验(规则同微信截图)
    if (l <= border) l = border;
    if (t >= this.win_h - border - t_h) t = r_t - t_h - 10;
    if (r_h >= this.win_h - border - t_h) {
      t = r_t + r_h - t_h - 10;
      l -= 10;
    }
    toolBox.style.top = t + 'px';
    toolBox.style.left = l + 'px';
  }
  // 绑定工具栏按钮事件
  bindToolBoxEvent(save, cancel) {
    save.onclick = () => { // 完成
      this.success();
    }
    cancel.onclick = () => { // 取消
      this.cancel();
    }
  }

5、截图完成

生成最终截图(没什么好说的就是调用各种api)、绑定各种回调事件,

// 生成base64图片
  getImagePortion(imgDom, new_w, new_h, s_x, s_y) {
    let sx = s_x - this.axis.left,
      sy = s_y - this.axis.top;
    let t_cv = document.createElement('canvas');
    let t_ct = t_cv.getContext('2d');
    t_cv.width = new_w;
    t_cv.height = new_h;

    let b_cv = document.createElement('canvas');
    let b_ct = b_cv.getContext('2d');
    b_cv.width = imgDom.width;
    b_cv.height = imgDom.height;
    b_ct.drawImage(imgDom, 0, 0);

    t_ct.drawImage(b_cv, sx, sy, new_w, new_h, 0, 0, new_w, new_h);
    let res = t_cv.toDataURL();
    return res;
  }
// 完成
  success() {
    let imgBase64 = this.getImagePortion(this.img, this.rect.offsetWidth, this.rect.offsetHeight, this.rect.offsetLeft, this.rect.offsetTop);
    if (this.options) {
      this.options.success && this.options.success.call(this, imgBase64);
    }
    this.close();
  }
  // 取消
  cancel() {
    if (this.options) {
      this.options.fail && this.options.fail.call(this);
    }
    this.close();
  }
  close() {
    if (this.options) {
      this.options.complete && this.options.complete.call(this);
    }
    this.distroy();
  }
  // 销毁
  distroy() {
    window.ClipScreen = undefined;
    this.box.remove();
  }

至此,简易功能就算是实现了

二、场景效果(截图)

初始化界面是根据dom的大小1比1生成的图片
原始dom节点 截图界面显示的是我截图的目标dom
基于html2canvas 的简易截图工具_第1张图片
初始化蒙版状态
基于html2canvas 的简易截图工具_第2张图片
截图过程
基于html2canvas 的简易截图工具_第3张图片

基于html2canvas 的简易截图工具_第4张图片
基于html2canvas 的简易截图工具_第5张图片
截图完成后获得的图片(该效果为项目内容,本插件只能返回成品截图的base64编码)
基于html2canvas 的简易截图工具_第6张图片
由于项目隐私 不方便拍视频 也懒得再去写一个demo 所以将就着哈

三、完整代码

完整代码下载链接:
https://download.csdn.net/download/qq_34206004/12812357

import html2canvas from 'html2canvas';
// 样式
const cssText = {
  box: 'overflow:hidden;position:fixed;left:0;top:0;right:0;bottom:0;background-color:rgba(255,255,255,0.9);z-index: 100000;',
  img: '',
  mask: 'position:absolute;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,0.6);',
  rect: 'position:absolute;border:1px solid #3e8ef7;box-sizing:border-box;cursor:move;user-select:none;background: url() no-repeat;',
  toolBox: 'position:absolute;top:0;left:0;padding:0 10px;background:#eee;line-height:2em;text-align:right;',
  toolBtn: 'font-weight:bold;color:#111;margin:0 1em;user-select:none;font-size:12px;cursor:pointer;',
}
/**
 * dom节点截图工具(基于html2canvas)
 * dom: 要截图的目标dom
 * options: { 
 *   // 以下三个回调方法作用域this指向构造函数
 *   success: function(res), //截图完成触发 参数为截图结果
 *   fail: function(), //取消截图触发
 *   complete: function(), //截图结束触发 success和fail都会触发
 * }
 * 
 * 调用示例:
 * new ClipScreen(dom节点, {
 *   success: function (res) {},
 *   complete: function () {},
 * });
 */
class ClipScreen {
  constructor(dom, options) {
    if (window.ClipScreen) return false;
    window.ClipScreen = this;
    this.dom = dom;
    this.options = options;
    html2canvas(this.dom).then((canvas) => {
      let dataURL = canvas.toDataURL("image/png");
      this.imgUrl = dataURL;
      this.start();
    });
  }
  // 初始化
  start() {
    this.border = 2; //用于计算选区拖拽点和边界的判断
    this.win_w = window.innerWidth;
    this.win_h = window.innerHeight;
    let box = this.box = document.createElement('div');
    box.id = 'ClipScreen';
    box.style.cssText = cssText.box;
    let img = document.createElement('img');
    img.style.cssText = cssText.img;
    img.src = this.imgUrl;
    let mask = document.createElement('div');
    mask.style.cssText = cssText.mask;
    box.appendChild(img);
    box.appendChild(mask);
    document.body.appendChild(box);
    img.onload = (e) => {
      let w = img.offsetWidth,
        h = img.offsetHeight,
        win_w = window.innerWidth,
        win_h = window.innerHeight,
        left = (win_w - w) / 2,
        top = (win_h - h) / 2;
      img.style.position = 'absolute';
      img.style.left = left + 'px';
      img.style.top = top + 'px';
      img.style.width = w + 'px';
      img.style.height = h + 'px';
      this.axis = {
        left,
        top
      }
      this.img = img;
      this.bindEvent(mask);
    }
  }
  // 绑定蒙版事件、键盘事件
  bindEvent(mask) {
    document.onkeydown = (e) => {
      if (e.keyCode == 27) {
        this.cancel();
      }
    }
    mask.onmousedown = (e) => {
      let offsetX = e.offsetX,
        offsetY = e.offsetY;
      document.onmousemove = (e) => {
        let x = e.offsetX,
          y = e.offsetY,
          sx = offsetX,
          sy = offsetY,
          w = Math.abs(offsetX - x),
          h = Math.abs(offsetY - y);
        if (x < offsetX) sx = x;
        if (y < offsetY) sy = y;
        this.createRect(sx, sy, w, h);
      }
      document.onmouseup = (e) => {
        this.moveToolBox();
        this.rect.style.pointerEvents = 'initial';
        this.unbindMouseEvent();
      }
    }
  }
  // 创建矩形截图选区
  createRect(x, y, w, h) {
    let rect = this.rect;
    if (!rect) {
      rect = this.rect = document.createElement('div');
      rect.style.cssText = cssText.rect;
      rect.style.backgroundImage = 'url(' + this.imgUrl + ')';
      // this.newImg = document.createElement('img');
      // this.newImg.style.cssText = cssText.rect_img;
      // rect.appendChild(this.newImg);
      let doms = this.createPoints(rect);
      this.box.appendChild(rect);
      this.bindRectEvent(doms);
    }
    let border = this.border;
    if (x <= border) x = border;
    if (y <= border) y = border;
    if (x + w >= this.win_w - border) x = this.win_w - border - w;
    if (y + h >= this.win_h - border) y = this.win_h - border - h;
    rect.style.pointerEvents = 'none';
    rect.style.display = 'block';
    rect.style.left = x + 'px';
    rect.style.top = y + 'px';
    rect.style.width = w + 'px';
    rect.style.height = h + 'px';
    rect.style.backgroundPosition = (-x + this.axis.left - 1) + 'px ' + (-y + this.axis.top - 1) + 'px';
    if (this.toolBox) this.toolBox.style.display = 'none';
  }
  // 创建截图选区各个方位拉伸点
  createPoints(rect) {
    let
      lt = document.createElement('span'),
      tc = document.createElement('span'),
      rt = document.createElement('span'),
      rc = document.createElement('span'),
      rb = document.createElement('span'),
      bc = document.createElement('span'),
      lb = document.createElement('span'),
      lc = document.createElement('span');
    let c_style = 'position:absolute;width:5px;height:5px;background:#3e8ef7;';
    lt.style.cssText = c_style + 'left:-3px;top:-3px;cursor:nw-resize;';
    tc.style.cssText = c_style + 'left:50%;top:-3px;margin-left:-3px;cursor:ns-resize;';
    rt.style.cssText = c_style + 'right:-3px;top:-3px;cursor:ne-resize;';
    rc.style.cssText = c_style + 'top:50%;right:-3px;margin-top:-3px;cursor:ew-resize;';
    rb.style.cssText = c_style + 'right:-3px;bottom:-3px;cursor:nw-resize;';
    bc.style.cssText = c_style + 'left:50%;bottom:-3px;margin-left:-3px;cursor:ns-resize;';
    lb.style.cssText = c_style + 'left:-3px;bottom:-3px;cursor:ne-resize;';
    lc.style.cssText = c_style + 'top:50%;left:-3px;margin-top:-3px;cursor:ew-resize;';
    let res = {
      lt,
      tc,
      rt,
      rc,
      rb,
      bc,
      lb,
      lc
    }
    for (let k in res) {
      rect.appendChild(res[k])
    }
    res.rect = rect;
    return res;
  }
  // 生成 、移动工具
  moveToolBox() {
    let toolBox = this.toolBox;
    if (!toolBox) {
      toolBox = this.toolBox = document.createElement('div');
      toolBox.style.cssText = cssText.toolBox;
      let save = document.createElement('span'),
        cancel = document.createElement('span');
      save.innerText = '完成';
      cancel.innerText = '取消';
      save.style.cssText = cancel.style.cssText = cssText.toolBtn;
      toolBox.appendChild(cancel);
      toolBox.appendChild(save);
      this.box.appendChild(toolBox);
      this.bindToolBoxEvent(save, cancel);
    }
    toolBox.style.display = 'block';
    let border = this.border;
    let t_w = this.toolBox.offsetWidth,
      t_h = this.toolBox.offsetHeight,
      r_t = this.rect.offsetTop,
      r_h = this.rect.offsetHeight;
    let t = r_t + r_h + 10,
      l = this.rect.offsetLeft + this.rect.offsetWidth - t_w;
    if (l <= border) l = border;
    if (t >= this.win_h - border - t_h) t = r_t - t_h - 10;
    if (r_h >= this.win_h - border - t_h) {
      t = r_t + r_h - t_h - 10;
      l -= 10;
    }
    toolBox.style.top = t + 'px';
    toolBox.style.left = l + 'px';
  }
  // 绑定工具栏事件
  bindToolBoxEvent(save, cancel) {
    save.onclick = () => {
      this.success();
    }
    cancel.onclick = () => {
      this.cancel();
    }
  }
  // 绑定截图选区事件
  bindRectEvent(o) {
    o.rect.addEventListener("mousedown", (e) => {
      let border = this.border;
      let $target = e.target;
      let offsetX = e.x,
        offsetY = e.y;
      let r_w = o.rect.offsetWidth,
        r_h = o.rect.offsetHeight,
        r_l = o.rect.offsetLeft,
        r_t = o.rect.offsetTop;

      if ($target == o.rect) {
        offsetX = e.offsetX;
        offsetY = e.offsetY;
        document.onmousemove = (e) => {
          let dif_x = e.x - offsetX,
            dif_y = e.y - offsetY;
          if (dif_x <= border) dif_x = border;
          if (dif_y <= border) dif_y = border;
          if (dif_x + r_w >= this.win_w - border) dif_x = this.win_w - border - r_w;
          if (dif_y + r_h >= this.win_h - border) dif_y = this.win_h - border - r_h;
          o.rect.style.left = dif_x + 'px';
          o.rect.style.top = dif_y + 'px';
          o.rect.style.backgroundPosition = (-dif_x + this.axis.left - 1) + 'px ' + (-dif_y + this.axis.top - 1) + 'px';
          this.toolBox.style.display = 'none'
        }
      } else {
        document.onmousemove = (e) => {
          this.toolBox.style.display = 'none'
          this.transform($target, o, offsetX, offsetY, r_w, r_h, r_l, r_t, e)
        }
      }
      document.onmouseup = (e) => {
        this.moveToolBox();
        this.unbindMouseEvent();
      }
    })
  }
  // 拉伸选区
  transform($t, o, offsetX, offsetY, r_w, r_h, r_l, r_t, e) {
    let border = this.border;
    let x = e.x,
      y = e.y;
    if (x <= border) x = border;
    if (y <= border) y = border;
    if (x >= this.win_w - border) x = this.win_w - border;
    if (y >= this.win_h - border) y = this.win_h - border;
    let dif_x = x - offsetX,
      dif_y = y - offsetY;
    let min = 10;
    let left = r_l,
      top = r_t,
      width = r_w,
      height = r_h;
    if ($t == o.lt) {
      if (r_w - dif_x <= min || r_h - dif_y <= min) return false;
      left = r_l + dif_x;
      top = r_t + dif_y;
      width = r_w - dif_x;
      height = r_h - dif_y;
    } else if ($t == o.tc) {
      if (r_h - dif_y <= min) return false;
      top = r_t + dif_y;
      height = r_h - dif_y;
    } else if ($t == o.rt) {
      if (r_w + dif_x <= min || r_h - dif_y <= min) return false;
      top = r_t + dif_y;
      width = r_w + dif_x;
      height = r_h - dif_y;
    } else if ($t == o.rc) {
      if (r_w + dif_x <= min) return false;
      width = r_w + dif_x;
    } else if ($t == o.rb) {
      if (r_w + dif_x <= min || r_h + dif_y <= min) return false;
      width = r_w + dif_x;
      height = r_h + dif_y;
    } else if ($t == o.bc) {
      if (r_h + dif_y <= min) return false;
      height = r_h + dif_y;
    } else if ($t == o.lb) {
      if (r_w - dif_x <= min || r_h + dif_y <= min) return false;
      left = r_l + dif_x;
      width = r_w - dif_x;
      height = r_h + dif_y;
    } else if ($t == o.lc) {
      if (r_w - dif_x <= min) return false;
      left = r_l + dif_x;
      width = r_w - dif_x;
    }
    o.rect.style.left = left + 'px';
    o.rect.style.top = top + 'px';
    o.rect.style.width = width + 'px';
    o.rect.style.height = height + 'px';
    o.rect.style.backgroundPosition = (-left + this.axis.left - 1) + 'px ' + (-top + this.axis.top - 1) + 'px';
  }
  // 解绑事件
  unbindMouseEvent() {
    document.onmousemove = null;
    document.onmouseup = null;
  }
  // 生成base64图片
  getImagePortion(imgDom, new_w, new_h, s_x, s_y) {
    let sx = s_x - this.axis.left,
      sy = s_y - this.axis.top;
    let t_cv = document.createElement('canvas');
    let t_ct = t_cv.getContext('2d');
    t_cv.width = new_w;
    t_cv.height = new_h;

    let b_cv = document.createElement('canvas');
    let b_ct = b_cv.getContext('2d');
    b_cv.width = imgDom.width;
    b_cv.height = imgDom.height;
    b_ct.drawImage(imgDom, 0, 0);

    t_ct.drawImage(b_cv, sx, sy, new_w, new_h, 0, 0, new_w, new_h);
    let res = t_cv.toDataURL();
    return res;
  }
  // 完成
  success() {
    let imgBase64 = this.getImagePortion(this.img, this.rect.offsetWidth, this.rect.offsetHeight, this.rect.offsetLeft, this.rect.offsetTop);
    if (this.options) {
      this.options.success && this.options.success.call(this, imgBase64);
    }
    this.close();
  }
  // 取消
  cancel() {
    if (this.options) {
      this.options.fail && this.options.fail.call(this);
    }
    this.close();
  }
  // 关闭
  close() {
    if (this.options) {
      this.options.complete && this.options.complete.call(this);
    }
    this.distroy();
  }
  // 销毁
  distroy() {
    window.ClipScreen = undefined;
    this.box.remove();
  }
}
export default ClipScreen

你可能感兴趣的:(学习,js,vue.js,html5,javascript)